Skip to content

feat(tutorial): add reusable onboarding engine - #136

Merged
imjlk merged 42 commits into
mainfrom
codex/tutorial-engine
Aug 13, 2026
Merged

imjlk merged 42 commits into
mainfrom
codex/tutorial-engine

Conversation

@imjlk

@imjlk imjlk commented Aug 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add the public @mpgd/tutorial package with a DOM-free tutorial director, strict progress parsing, queued PlatformGateway.storage persistence, and replay/skip semantics
  • add the optional Driver.js presenter with responsive visible-target rebinding, action/signal gating, accessible modal ownership, stable automation attributes, and a host-owned replay-trigger binding
  • preserve host-owned ARIA values/removals and clean Driver target/modal semantics across responsive rebinding and detached DOM nodes
  • add opt-in local reproduction utilities: memory progress, DEV-only URL policy/debug bridge, and an optional floating debug trigger
  • add examples/tutorial-driver so the engine can be reproduced without a game; a host replay/help button may be present or omitted, while the demo floating trigger appears only with ?trigger=1
  • document the extraction boundary: game-specific steps, i18n, Phaser events/simulation gating, navigation, and visual styling remain game-owned

Local reproduction

pnpm dev:tutorial

Then open:

  • http://localhost:5198/
  • http://localhost:5198/?host-trigger=0
  • http://localhost:5198/?trigger=1&host-trigger=0
  • http://localhost:5198/?mpgd-tutorial=off
  • http://localhost:5198/?mpgd-tutorial=replay&mpgd-tutorial-step=modal-choice

Validation

  • pnpm install --frozen-lockfile
  • pnpm check
  • pnpm --filter @mpgd/tutorial test (123 tests)
  • pnpm build:packages
  • pnpm smoke:tutorial:dist
  • pnpm build:tutorial
  • pnpm validate:public
  • pnpm pack:packages:prepared
  • package tarball dry-run and external tarball consumer import/build smoke
  • public-registry consumer install smoke for @mpgd/tutorial@0.1.0, including root, ./platform-storage, ./testing, ./driver, and ./driver.css
  • browser smoke across desktop/390px, host trigger on/off, debug trigger, off mode, direct step, responsive target rebinding, host-owned ARIA updates/removals, detached target cleanup, and modal ARIA cleanup; zero console/page errors
  • git diff --check

Release

  • @mpgd/tutorial@0.1.0 was initially published manually from the verified pnpm-generated tarball and is publicly available with the latest dist-tag
  • the package is registered as an independent singleton Sampo release group for future changeset-driven releases
  • the initial 0.1.0 publish is the baseline; the post-publish runtime/accessibility fixes carry a patch changeset that plans 0.1.1
  • npm Trusted Publisher/OIDC is configured for the existing id-token: write release workflow, completing the Sampo path for subsequent changeset-driven releases

Summary by CodeRabbit

  • New Features

    • Added the @mpgd/tutorial package for interactive tutorial flows with acknowledgements, actions, signals, replay, skipping, suspension, and saved progress.
    • Added optional responsive Driver.js presentations with accessibility support and dynamic target handling.
    • Added local testing and debugging tools, including replay controls and a browser demonstration harness.
  • Bug Fixes

    • Improved lifecycle handling, focus retention, blocked-action guidance, and error isolation.
  • Documentation

    • Added setup, API, persistence, accessibility, replay, troubleshooting, and release guidance.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1a429aba-c6dc-4c24-a7a5-39bf5c3f4ec7

📥 Commits

Reviewing files that changed from the base of the PR and between cc2b54b and 1423553.

📒 Files selected for processing (4)
  • packages/tutorial/src/driver.ts
  • packages/tutorial/src/platform-storage.ts
  • packages/tutorial/test/driver.test.ts
  • packages/tutorial/test/platform-storage.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/tutorial/src/platform-storage.ts
  • packages/tutorial/src/driver.ts

📝 Walkthrough

Walkthrough

Adds the private @mpgd/tutorial package with typed progression, persistence, replay, Driver.js presentation, debug helpers, tests, documentation, and a local eight-step browser harness.

Changes

Tutorial engine

Layer / File(s) Summary
Tutorial contracts and progress storage
packages/tutorial/src/definition.ts, packages/tutorial/src/progress.ts, packages/tutorial/src/platform-storage.ts, packages/tutorial/src/testing.ts, packages/tutorial/package.json, packages/tutorial/src/index.ts, packages/tutorial/test/platform-storage.test.ts, tsconfig.base.json
Adds typed definitions, progress validation, platform and memory stores, package exports, and storage tests.
Director lifecycle and progression
packages/tutorial/src/director.ts, packages/tutorial/test/director.test.ts
Adds advancement, persistence, snapshots, subscriptions, replay, suspension, skipping, and lifecycle handling.
Driver.js presentation and accessibility
packages/tutorial/src/driver.ts, packages/tutorial/driver.css, packages/tutorial/test/driver.test.ts
Adds target resolution, rebinding, presentation, replay triggers, modal semantics, ARIA restoration, cleanup, styling, and tests.
Debug bridge and browser harness
packages/tutorial/src/testing.ts, examples/tutorial-driver/*, package.json, packages/tutorial/test/dist-import.mjs
Adds debug policies, replay controls, the eight-step harness, host synchronization, responsive styling, Vite wiring, scripts, and distribution smoke testing.
Tutorial documentation and release wiring
docs/TUTORIAL_ENGINE.md, packages/tutorial/README.md, README.md, docs/PUBLIC_RELEASE_CHECKLIST.md, .sampo/config.toml, packages/tutorial/CHANGELOG.md, .sampo/changesets/harden-tutorial-lifecycle.md
Documents package boundaries, APIs, persistence, presentation, replay ownership, debugging, cleanup, release checks, and package registration.

Estimated code review effort: 4 (Complex) | ~60 minutes

Mergeability Score: ⚪ Minimal · up to 14235

The PR adds the tutorial engine and supporting integrations with no actionable merge-blocking risk remaining after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant Host as Tutorial harness
  participant Director as Tutorial director
  participant Store as Progress store
  participant Presenter as Driver.js presenter
  Host->>Director: Observe actions, signals, and acknowledgements
  Director->>Store: Persist progress
  Director->>Presenter: Publish tutorial snapshot
  Presenter->>Host: Present the current step
  Host->>Presenter: Trigger replay or target refresh
  Presenter->>Director: Request replay or acknowledgement
Loading

Possibly related PRs

  • imjlk/mpgd-kit#5: Shares package release configuration and publication guidance updates.

Poem

A rabbit hops through steps so bright,
With signals, cards, and modal light.
It saves each trail, then starts anew,
While Driver guides the path in view.
“Replay!” the little paws proclaim—
The tutorial now knows the game.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: adding a reusable tutorial onboarding engine.
Description check ✅ Passed The description covers the summary, validation, reproduction, release, and scope details; the missing Notes section is non-critical.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/tutorial-engine

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review\n\nLatest patch 3e613754 hardens Driver target ownership cleanup: exact-value host ARIA writes, removals, responsive rebinding, target-only DOM removal, and detached modal semantics are preserved. Package check, 16/16 tests, workspace check, demo build, and 7 browser scenarios pass with zero console/page errors.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e6137543b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Caution

CodeRabbit couldn't update its existing comment. The review summary may be out of date.

Error details
putComment timed out

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (13)
examples/tutorial-driver/vite.config.ts (2)

20-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

String aliases match by prefix, so the order is load-bearing.

'@mpgd/tutorial/driver' is a prefix of '@mpgd/tutorial/driver.css'. The list works only because the CSS entry comes first. A future reordering silently resolves the stylesheet to src/driver.ts. Use exact patterns to remove the ordering dependency.

♻️ Proposed refactor
       {
-        find: '`@mpgd/tutorial/driver`',
+        find: /^`@mpgd`\/tutorial\/driver$/u,
         replacement: resolve(import.meta.dirname, '../../packages/tutorial/src/driver.ts'),
       },

Apply the same anchored pattern to the remaining entries.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/tutorial-driver/vite.config.ts` around lines 20 - 43, Update the
alias find patterns in the Vite resolve.alias configuration so every entry
matches its complete package specifier exactly, including the CSS,
platform-storage, driver, testing, and root tutorial aliases. Preserve each
existing replacement target while removing the prefix-based ordering dependency.

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unnecessary double cast. @ttsc/unplugin/vite is compatible with Vite PluginOption for the pinned versions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/tutorial-driver/vite.config.ts` around lines 14 - 19, Remove the
unnecessary `as unknown as PluginOption` cast from the `ttsc` plugin entry in
the `plugins` array. Keep the existing `ttsc` configuration and rely on its
compatible inferred type.
packages/tutorial/src/driver.ts (1)

540-548: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the guarded attribute list into one module constant.

The tuple ['aria-controls', 'aria-expanded', 'aria-haspopup'] appears at line 544, at line 623 in captureTutorialTargetSemantics, and again as three literal comparisons in isTutorialTargetAttribute at lines 617-619. A future change to the list must update three places. Define the constant once and derive the type and the guard from it.

♻️ Proposed refactor
+const tutorialTargetAttributes = [
+  'aria-controls',
+  'aria-expanded',
+  'aria-haspopup',
+] as const;
+
+type TutorialTargetAttribute = (typeof tutorialTargetAttributes)[number];
 function isTutorialTargetAttribute(
   attribute: string | null,
-): attribute is 'aria-controls' | 'aria-expanded' | 'aria-haspopup' {
-  return attribute === 'aria-controls'
-    || attribute === 'aria-expanded'
-    || attribute === 'aria-haspopup';
+): attribute is TutorialTargetAttribute {
+  return tutorialTargetAttributes.includes(attribute as TutorialTargetAttribute);
 }

Replace the local attributes bindings at lines 544 and 623 with tutorialTargetAttributes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tutorial/src/driver.ts` around lines 540 - 548, Define a
module-level tutorialTargetAttributes constant containing the guarded ARIA
attribute tuple, derive the Attribute type and isTutorialTargetAttribute checks
from it, and replace the local attributes bindings in
preserveTutorialTargetSemantics and captureTutorialTargetSemantics with the
shared constant.
packages/tutorial/test/dist-import.mjs (1)

24-33: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Call director.destroy() and validate the driver subpath export.

Two gaps in this smoke test:

  1. The script never calls director.destroy(). The director interface declares destroy() as the teardown method. Without it, a pending timer or persistence handle can keep the Node process alive and hang the smoke:tutorial:dist step in CI.
  2. The test does not check the @mpgd/tutorial/driver subpath. That entry needs a DOM, so a full import is not appropriate, but a resolution check catches a broken exports map without loading the module.
♻️ Proposed additions
 director.acknowledge('welcome');
 assert.equal(director.getSnapshot().status, 'completed');
 await director.flush();
+director.destroy();
+
+assert.ok(import.meta.resolve('../dist/driver.js'));
 
 console.log('`@mpgd/tutorial` dist import smoke passed.');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tutorial/test/dist-import.mjs` around lines 24 - 33, Update the
dist-import smoke test to validate resolution of the `@mpgd/tutorial/driver`
subpath without importing or executing its DOM-dependent module, then call
director.destroy() after the existing flush to ensure teardown completes before
the script exits.
packages/tutorial/driver.css (1)

23-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The fixed title padding can collide with translated close-button text.

Line 24 reserves 96px for the close button. The close button uses width: auto and grows with its text. presentation.copy.skip is host-supplied and translated, so a longer label overlaps the title. Consider a flex or grid header layout, or cap the close button width.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tutorial/driver.css` around lines 23 - 46, Update the
.mpgd-tutorial-popover header layout so .driver-popover-title and
.driver-popover-close-btn reserve space dynamically for translated close-button
text instead of relying on the fixed 96px padding-right; use a flex or grid
arrangement while preserving the existing title and close-button styling.
examples/tutorial-driver/src/styles.css (1)

12-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider dynamic viewport units for the mobile layout.

Lines 14, 80, and 165 use 100vh. On mobile browsers 100vh excludes the collapsing URL bar, so the sticky mobile nav at lines 177-187 can sit below the visible area. The harness exists to reproduce mobile behavior, so 100dvh gives a more accurate reproduction.

Also applies to: 78-82, 163-167

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/tutorial-driver/src/styles.css` around lines 12 - 21, Update the
mobile layout declarations using 100vh, including the body min-height and the
corresponding rules near the mobile navigation, to use 100dvh so the layout
tracks the visible viewport when mobile browser chrome collapses. Preserve the
existing sizing and navigation behavior otherwise.
examples/tutorial-driver/index.html (1)

9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The mount point creates invalid landmark nesting.

examples/tutorial-driver/src/main.ts line 75 writes <header>, two <nav> elements, and role="dialog" into #app. Because #app is <main>, those landmarks nest inside the main landmark. A header inside main is not a banner, and the harness demonstrates modal and navigation semantics, so the nesting undercuts the demonstration.

♻️ Proposed fix
-    <main id="app"></main>
+    <div id="app"></div>

Then wrap the content section in main.ts with <main>.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/tutorial-driver/index.html` at line 9, Change the `#app` mount point
in the tutorial driver from a main element to a neutral container, then update
the content-rendering logic in main.ts to wrap the appropriate content section
in its own main element while preserving the existing header, nav, and dialog
landmark structure.
packages/tutorial/test/driver.test.ts (1)

56-127: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for target rebinding, the missingTarget policies, and the skip path.

The suite covers target resolution, ARIA ownership, and cleanup. Three changed behaviors have no test:

  • rebindActiveTarget at packages/tutorial/src/driver.ts lines 322-353, which the PR lists as responsive target rebinding.
  • The missingTarget policies error, wait, and unanchored at lines 196-209.
  • The close-button skip path at lines 139-144, including the re-drive behavior after onDestroyed.

Do you want me to draft these tests?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tutorial/test/driver.test.ts` around lines 56 - 127, Extend the
Driver tutorial presenter tests to cover rebindActiveTarget by replacing or
removing the active target and verifying rebinding, exercise missingTarget
policies error, wait, and unanchored with their expected outcomes, and test the
close-button skip flow including onDestroyed triggering re-drive behavior.
Anchor the additions to createDriverTutorialPresenter, presenter.present, and
the skip control while preserving existing cleanup and accessibility assertions.
packages/tutorial/test/director.test.ts (2)

194-216: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a case for the off debug mode.

Line 204 passes ?mpgd-tutorial=off together with enabled: false, so applyTutorialDebugLaunchPolicy returns at its first guard. The off branch that calls director.setSuspended(true) never runs in this suite. Add a case with enabled: true and search: '?mpgd-tutorial=off', then assert that suspended is true.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tutorial/test/director.test.ts` around lines 194 - 216, Extend the
test for applyTutorialDebugLaunchPolicy with an enabled debug policy using
search '?mpgd-tutorial=off', then assert director.getSnapshot().suspended is
true. Keep the existing disabled-policy and replay-mode assertions unchanged.

219-237: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a case for the skipped status invariants.

The parser rejects a skipped record when the step list is complete, when completedAt is set, or when skippedAt is null. This suite covers the active and completed invariants only. Add a skipped case so a regression in that branch fails a test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tutorial/test/director.test.ts` around lines 219 - 237, The tutorial
progress parser test in “tutorial progress parser” should add a skipped-status
case covering invalid skipped records: reject a complete step list, a non-null
completedAt, and a null skippedAt. Keep the existing active and completed
assertions unchanged and use parseTutorialProgress with the existing initial
progress fixture.
packages/tutorial/src/index.ts (1)

5-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the selectors from the attribute names.

tutorialDomSelectors repeats the literal attribute names from tutorialDomAttributes. A rename in one object does not update the other, so the two can drift silently. The Driver.js presenter reads tutorialDomAttributes.target while other code matches on the selector strings, which makes a drift hard to detect.

Build the selectors from the attribute constants. Add as const so consumers keep the literal types.

♻️ Proposed refactor
 export const tutorialDomAttributes = Object.freeze({
   active: 'data-mpgd-tutorial-active',
   next: 'data-mpgd-tutorial-next',
   popover: 'data-mpgd-tutorial-popover',
   skip: 'data-mpgd-tutorial-skip',
   step: 'data-mpgd-tutorial-step',
   target: 'data-mpgd-tutorial-target',
-});
+} as const);
 
 export const tutorialDomSelectors = Object.freeze({
-  active: '[data-mpgd-tutorial-active="true"]',
-  next: '[data-mpgd-tutorial-next]',
-  popover: '[data-mpgd-tutorial-popover]',
-  skip: '[data-mpgd-tutorial-skip]',
-  target: '[data-mpgd-tutorial-target]',
-});
+  active: `[${tutorialDomAttributes.active}="true"]`,
+  next: `[${tutorialDomAttributes.next}]`,
+  popover: `[${tutorialDomAttributes.popover}]`,
+  skip: `[${tutorialDomAttributes.skip}]`,
+  target: `[${tutorialDomAttributes.target}]`,
+} as const);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tutorial/src/index.ts` around lines 5 - 20, Update
tutorialDomSelectors to construct each selector from the corresponding values in
tutorialDomAttributes instead of repeating attribute-name literals, and add as
const so both exported objects preserve literal types. Keep the existing
selector behavior unchanged, including the true-value condition for active.
packages/tutorial/package.json (1)

57-60: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider making driver.js optional.

The package documents the Driver.js presenter as optional, but line 59 declares driver.js as a hard runtime dependency. Consumers that import only @mpgd/tutorial or @mpgd/tutorial/testing then install Driver.js without using it. Move it to peerDependencies with peerDependenciesMeta.optional, or to optionalDependencies, once the package leaves the incubator stage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tutorial/package.json` around lines 57 - 60, Update the dependencies
in the package manifest so driver.js is no longer a mandatory runtime dependency
for consumers; represent it as an optional peer dependency (including
peerDependenciesMeta.optional) or optionalDependencies, while preserving the
existing workspace dependency.
packages/tutorial/test/platform-storage.test.ts (1)

45-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add cases for a rejecting load and for migrate.

Two branches of createPlatformTutorialProgressStore have no coverage:

  • The catch around storage.load. A rejected load must set available to false and call onError.
  • The migrate hook. A migrated legacy record must parse into valid progress, and a throwing migrate must disable the store.

Both branches control fail-closed behavior, so a regression there silently disables or wrongly enables persistence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tutorial/test/platform-storage.test.ts` around lines 45 - 95, Extend
the tests for createPlatformTutorialProgressStore with a rejecting storage.load
case that verifies available is false and onError receives the failure. Add
migrate coverage proving a legacy record is converted into valid progress, and a
throwing migrate hook disables the store and reports the error through the
configured failure path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/TUTORIAL_ENGINE.md`:
- Around line 159-161: Update the replay documentation around
createTutorialDirector.replay() to state that skipping an in-session replay
restores the prior durable progress for every state, including active progress,
and restores null when no progress existed.

In `@examples/tutorial-driver/src/main.ts`:
- Around line 176-186: Update the pagehide handler around debug.destroy(),
unbindReplay(), unsubscribe(), presenter.destroy(), and director.destroy() to
skip teardown when the event is persisted for a back/forward-cache navigation,
or add pageshow re-initialization so the harness is restored after bfcache
navigation. Preserve teardown for non-persisted page hides and ensure the
once-only listener does not leave the restored page with destroyed components.

In `@packages/tutorial/src/director.ts`:
- Around line 227-244: Update the direct progressStore.save call in skip() to
use the same destroyed/replaying guard as persist, preventing the asynchronous
skip write when the director has been destroyed or replaying is active while
preserving existing error handling.

In `@packages/tutorial/src/driver.ts`:
- Around line 522-527: In the underlyingModal handling, only add popover.id to
ownedIds when it is non-empty; preserve the existing previousAriaOwns values and
avoid setting a stray token in ownedAriaOwns when the popover wrapper has no id.
- Around line 139-164: Track a local dismissed flag/key for the current
presentation in the tutorial driver, set it before invoking input.onSkip from
onCloseClick, and prevent onDestroyed’s scheduleRefresh from re-driving that
dismissed step. Clear the dismissed key in present() when activePresentationKey
differs from nextKey so a newly presented step can render normally, including
when onSkip rejects.

In `@packages/tutorial/src/platform-storage.ts`:
- Around line 28-43: Add timeout enforcement around the storage load and save
operations used by the tutorial storage flow, including StorageAdapter and the
AIT, Capacitor, and Devvit bridge adapters. On timeout, mark available as false
and invoke onError, while preserving existing migration and persistence behavior
for completed calls; add tests covering pending load and save operations,
including flush() through queue.

In `@packages/tutorial/src/testing.ts`:
- Around line 99-103: Validate the raw debug step id used by
applyTutorialDebugLaunchPolicy before constructing replay options or calling
director.replay. Only pass a verified TutorialStepIdOf<TDefinition>; when the
URL value is unknown, ignore it and perform a full replay (or route the
validation failure through the existing error callback) instead of allowing
createTutorialProgressAtStep to reject.

---

Nitpick comments:
In `@examples/tutorial-driver/index.html`:
- Line 9: Change the `#app` mount point in the tutorial driver from a main element
to a neutral container, then update the content-rendering logic in main.ts to
wrap the appropriate content section in its own main element while preserving
the existing header, nav, and dialog landmark structure.

In `@examples/tutorial-driver/src/styles.css`:
- Around line 12-21: Update the mobile layout declarations using 100vh,
including the body min-height and the corresponding rules near the mobile
navigation, to use 100dvh so the layout tracks the visible viewport when mobile
browser chrome collapses. Preserve the existing sizing and navigation behavior
otherwise.

In `@examples/tutorial-driver/vite.config.ts`:
- Around line 20-43: Update the alias find patterns in the Vite resolve.alias
configuration so every entry matches its complete package specifier exactly,
including the CSS, platform-storage, driver, testing, and root tutorial aliases.
Preserve each existing replacement target while removing the prefix-based
ordering dependency.
- Around line 14-19: Remove the unnecessary `as unknown as PluginOption` cast
from the `ttsc` plugin entry in the `plugins` array. Keep the existing `ttsc`
configuration and rely on its compatible inferred type.

In `@packages/tutorial/driver.css`:
- Around line 23-46: Update the .mpgd-tutorial-popover header layout so
.driver-popover-title and .driver-popover-close-btn reserve space dynamically
for translated close-button text instead of relying on the fixed 96px
padding-right; use a flex or grid arrangement while preserving the existing
title and close-button styling.

In `@packages/tutorial/package.json`:
- Around line 57-60: Update the dependencies in the package manifest so
driver.js is no longer a mandatory runtime dependency for consumers; represent
it as an optional peer dependency (including peerDependenciesMeta.optional) or
optionalDependencies, while preserving the existing workspace dependency.

In `@packages/tutorial/src/driver.ts`:
- Around line 540-548: Define a module-level tutorialTargetAttributes constant
containing the guarded ARIA attribute tuple, derive the Attribute type and
isTutorialTargetAttribute checks from it, and replace the local attributes
bindings in preserveTutorialTargetSemantics and captureTutorialTargetSemantics
with the shared constant.

In `@packages/tutorial/src/index.ts`:
- Around line 5-20: Update tutorialDomSelectors to construct each selector from
the corresponding values in tutorialDomAttributes instead of repeating
attribute-name literals, and add as const so both exported objects preserve
literal types. Keep the existing selector behavior unchanged, including the
true-value condition for active.

In `@packages/tutorial/test/director.test.ts`:
- Around line 194-216: Extend the test for applyTutorialDebugLaunchPolicy with
an enabled debug policy using search '?mpgd-tutorial=off', then assert
director.getSnapshot().suspended is true. Keep the existing disabled-policy and
replay-mode assertions unchanged.
- Around line 219-237: The tutorial progress parser test in “tutorial progress
parser” should add a skipped-status case covering invalid skipped records:
reject a complete step list, a non-null completedAt, and a null skippedAt. Keep
the existing active and completed assertions unchanged and use
parseTutorialProgress with the existing initial progress fixture.

In `@packages/tutorial/test/dist-import.mjs`:
- Around line 24-33: Update the dist-import smoke test to validate resolution of
the `@mpgd/tutorial/driver` subpath without importing or executing its
DOM-dependent module, then call director.destroy() after the existing flush to
ensure teardown completes before the script exits.

In `@packages/tutorial/test/driver.test.ts`:
- Around line 56-127: Extend the Driver tutorial presenter tests to cover
rebindActiveTarget by replacing or removing the active target and verifying
rebinding, exercise missingTarget policies error, wait, and unanchored with
their expected outcomes, and test the close-button skip flow including
onDestroyed triggering re-drive behavior. Anchor the additions to
createDriverTutorialPresenter, presenter.present, and the skip control while
preserving existing cleanup and accessibility assertions.

In `@packages/tutorial/test/platform-storage.test.ts`:
- Around line 45-95: Extend the tests for createPlatformTutorialProgressStore
with a rejecting storage.load case that verifies available is false and onError
receives the failure. Add migrate coverage proving a legacy record is converted
into valid progress, and a throwing migrate hook disables the store and reports
the error through the configured failure path.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 76e04f70-4b53-4e07-9581-d5112c7fdf59

📥 Commits

Reviewing files that changed from the base of the PR and between 39bb180 and 3e61375.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • README.md
  • docs/TUTORIAL_ENGINE.md
  • examples/tutorial-driver/index.html
  • examples/tutorial-driver/package.json
  • examples/tutorial-driver/src/main.ts
  • examples/tutorial-driver/src/styles.css
  • examples/tutorial-driver/tsconfig.json
  • examples/tutorial-driver/vite.config.ts
  • package.json
  • packages/tutorial/README.md
  • packages/tutorial/driver.css
  • packages/tutorial/package.json
  • packages/tutorial/src/definition.ts
  • packages/tutorial/src/director.ts
  • packages/tutorial/src/driver.ts
  • packages/tutorial/src/index.ts
  • packages/tutorial/src/platform-storage.ts
  • packages/tutorial/src/progress.ts
  • packages/tutorial/src/testing.ts
  • packages/tutorial/test/director.test.ts
  • packages/tutorial/test/dist-import.mjs
  • packages/tutorial/test/driver.test.ts
  • packages/tutorial/test/platform-storage.test.ts
  • packages/tutorial/tsconfig.json
  • tsconfig.base.json

Comment thread docs/TUTORIAL_ENGINE.md Resolved
Comment thread examples/tutorial-driver/src/main.ts Resolved
Comment thread packages/tutorial/src/director.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/testing.ts Resolved
@imjlk
imjlk marked this pull request as ready for review August 12, 2026 08:56
@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review\n\nPR is now ready for review. Please review the latest commit 3e613754.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e6137543b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tutorial/src/director.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

Review follow-up pushed in commits 40804e1, 2851b82, and 44644fb. All 15 actionable threads now have evidence replies and are resolved. Latest validation: @mpgd/tutorial 35/35 tests, package and demo typechecks, tutorial-driver production build, real Chromium 7-scenario smoke, full workspace check/tests, package build, dist import, dry-run tarball, package pack smoke, graph preflight, and git diff --check. The package remains an intentionally private 0.0.0 incubator; no registry publish was performed.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review\n\nPlease review the latest commit 44644fb, including the resolved review lifecycle and persistence edge cases.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review\n\nPlease review the latest commit 44644fb and the resolved thread evidence.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Pull request metadata ==='
gh pr view 136 --repo imjlk/mpgd-kit --json number,headRefOid,baseRefOid,commits,reviews,comments 2>/dev/null \
  | jq '{number, headRefOid, baseRefOid, commits: [.commits[] | {oid, messageHeadline}], reviews: [.reviews[] | {author: .author.login, state, body}], comments: [.comments[] | {author: .author.login, body, createdAt}]}' \
  || true

echo '=== Follow-up commit files and stat ==='
git show --stat --oneline --decorate --no-renames 44644fb3
git diff-tree --no-commit-id --name-status -r 44644fb3

echo '=== Changed TypeScript symbols ==='
while IFS= read -r file; do
  case "$file" in
    *.ts) ast-grep outline "$file" --items all --type function,class,interface,type --view compact || true ;;
  esac
done < <(git diff-tree --no-commit-id --name-only -r 44644fb3)

echo '=== Review thread status ==='
gh api graphql -F owner='imjlk' -F name='mpgd-kit' -F number=136 -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 20) {
            nodes { author { login } body createdAt }
          }
        }
      }
    }
  }
}' 2>/dev/null | jq -r '
.data.repository.pullRequest.reviewThreads.nodes[] |
"resolved=\(.isResolved) path=\(.path // "general") line=\(.line // .originalLine // 0)\n" +
(.comments.nodes[] | "  \(.author.login): \(.body | gsub("\n"; " "))")' || true

Length of output: 45670


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Follow-up range summary ==='
git diff --stat --no-renames 3e613754..44644fb3
git diff --name-status --no-renames 3e613754..44644fb3

echo '=== Follow-up diff ==='
git diff --no-ext-diff --unified=80 3e613754..44644fb3 -- \
  docs/TUTORIAL_ENGINE.md \
  examples/tutorial-driver/src/main.ts \
  packages/tutorial/src/director.ts \
  packages/tutorial/src/driver.ts \
  packages/tutorial/src/platform-storage.ts \
  packages/tutorial/src/testing.ts \
  packages/tutorial/test/director.test.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/test/platform-storage.test.ts

echo '=== Source and test outlines ==='
for file in \
  packages/tutorial/src/director.ts \
  packages/tutorial/src/driver.ts \
  packages/tutorial/src/platform-storage.ts \
  packages/tutorial/src/testing.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/test/platform-storage.test.ts \
  packages/tutorial/test/director.test.ts
do
  printf '\n--- %s ---\n' "$file"
  ast-grep outline "$file" --items all --type function,interface,type --view compact || true
done

echo '=== Resolved review threads ==='
gh api graphql -F owner='imjlk' -F name='mpgd-kit' -F number=136 -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 30) {
            nodes { author { login } body }
          }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == true) | {path, line, originalLine, comments: [.comments.nodes[] | {author: .author.login, body}]}' \
  || echo 'Review-thread metadata is unavailable from the GitHub API in this sandbox.'

Length of output: 163734


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Driver presentation and skip state ==='
sed -n '90,390p' packages/tutorial/src/driver.ts

echo '=== Driver target and modal ownership cleanup ==='
sed -n '390,760p' packages/tutorial/src/driver.ts

echo '=== Director replay and skip persistence ==='
sed -n '70,320p' packages/tutorial/src/director.ts

echo '=== Platform-storage timeout and queue logic ==='
sed -n '1,180p' packages/tutorial/src/platform-storage.ts

echo '=== Exact final commit diff ==='
git show --no-ext-diff --unified=30 --check 44644fb3 -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts

echo '=== Unresolved review threads ==='
gh api graphql -F owner='imjlk' -F name='mpgd-kit' -F number=136 -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 1) { nodes { author { login } body } }
        }
      }
    }
  }
}' --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, originalLine, author: .comments.nodes[0].author.login, body: .comments.nodes[0].body}]' \
  || echo 'Unresolved-thread metadata is unavailable from the GitHub API in this sandbox.'

Length of output: 32928


@imjlk Reviewed the follow-up range through 44644fb3.

I found no new actionable issues.

The resolved-thread evidence matches the current lifecycle, persistence, target-rebinding, modal-ARIA, and skip-dismissal implementations. The final commit preserves successful same-key skip dismissal. GitHub reports zero unresolved review threads. git show --check reports no whitespace errors.

I could not run the reported test and browser-validation commands in this sandbox.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 44644fb3e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/director.ts Resolved
Comment thread packages/tutorial/src/director.ts Resolved
@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

Second review round addressed in 1e8cfc28: dismissal survives transient null presentations, reentrant publications keep callback/getSnapshot aligned without duplicate subscribers, and uncancellable saves remain tracked through actual settlement while only initialization loads are bounded. Validation: tutorial 36/36, full workspace check, package build + dist import, demo typecheck/build, Chromium 7-scenario smoke, and diff check.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review\n\nPlease review latest commit 1e8cfc28; all four new threads have evidence replies and are resolved.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review\n\nPlease review latest commit 1e8cfc28.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Commit metadata ==='
git show -s --format='%H%n%P%n%s%n%b' 1e8cfc28
echo

echo '=== Changed files and stat ==='
git diff-tree --no-commit-id --name-status -r 1e8cfc28
git show --stat --oneline --no-renames 1e8cfc28
echo

echo '=== Exact diff ==='
git diff --no-ext-diff --unified=80 1e8cfc28^ 1e8cfc28 -- \
  packages/tutorial/src/director.ts \
  packages/tutorial/src/driver.ts \
  packages/tutorial/src/platform-storage.ts \
  packages/tutorial/test/director.test.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/test/platform-storage.test.ts

echo '=== Source outlines ==='
for file in \
  packages/tutorial/src/director.ts \
  packages/tutorial/src/driver.ts \
  packages/tutorial/src/platform-storage.ts \
  packages/tutorial/test/director.test.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/test/platform-storage.test.ts
do
  if [ -f "$file" ]; then
    printf '\n--- %s ---\n' "$file"
    ast-grep outline "$file" --items all --type function,interface,type --view compact || true
  fi
done

echo '=== Whitespace check ==='
git show --check --format= 1e8cfc28 || true

echo '=== Unresolved review threads ==='
gh api graphql -F owner='imjlk' -F name='mpgd-kit' -F number=136 -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 3) {
            nodes { author { login } body }
          }
        }
      }
    }
  }
}' --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, originalLine, comments: [.comments.nodes[] | {author: .author.login, body: .body}]}]' \
  || echo 'Review-thread metadata is unavailable in this sandbox.'

Length of output: 56940


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Documentation change ==='
git diff --no-ext-diff --unified=40 1e8cfc28^ 1e8cfc28 -- docs/TUTORIAL_ENGINE.md

echo '=== Complete director implementation ==='
sed -n '1,380p' packages/tutorial/src/director.ts

echo '=== Complete changed Driver dismissal and presentation flow ==='
sed -n '40,380p' packages/tutorial/src/driver.ts

echo '=== Complete storage implementation ==='
sed -n '1,220p' packages/tutorial/src/platform-storage.ts

echo '=== Timeout API references ==='
rg -n -C2 --glob '!**/node_modules/**' \
  '(defaultTutorialStorageTimeoutMs|defaultTutorialStorageLoadTimeoutMs|timeoutMs|loadTimeoutMs|withStorageTimeout|loadWithTimeout)' \
  .

echo '=== Storage adapter declarations and tutorial storage imports ==='
rg -n -C3 --glob '*.{ts,json}' \
  "(interface StorageAdapter|type StorageAdapter|export.*StorageAdapter|from '`@mpgd/tutorial/platform-storage`'|from './platform-storage\\.js')" \
  packages tsconfig.base.json package.json

echo '=== Relevant current test declarations ==='
ast-grep outline packages/tutorial/test/director.test.ts --items all --type function --view signatures || true
ast-grep outline packages/tutorial/test/driver.test.ts --items all --type function --view signatures || true
ast-grep outline packages/tutorial/test/platform-storage.test.ts --items all --type function --view signatures || true

Length of output: 91932


@imjlk Reviewed commit 1e8cfc28.

I found no new actionable issues.

The director now keeps each callback argument aligned with getSnapshot() during reentrant publication. It also prevents a listener added during publication from receiving the current publication twice.

The Driver presenter preserves dismissal through transient null presentations. It permits a retry after onSkip fails.

The progress store applies a timeout only to initialization loads. It keeps uncancellable saves serialized until the storage adapter settles. The documentation matches this behavior.

GitHub reports zero unresolved review threads. git show --check reports no whitespace errors.

I could not run the reported test, build, or browser-validation commands in this sandbox.

You are interacting with an AI system.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e8cfc2894

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/testing.ts Resolved
Comment thread packages/tutorial/src/testing.ts Resolved
@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

Review round 3 addressed in cb209812: explicit replay-session dismissal reset, ancestor-aware target visibility, out-of-order debug bridge teardown, and unavailable memory-store parity. Validation: tutorial 40/40, full workspace check, package build + dist import, demo typecheck/build, Chromium 7-scenario smoke, three independent read-only final audits, and git diff check.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Please review latest commit cb209812; all four new threads have evidence replies and are resolved.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Please review latest commit cb209812 and resolved thread evidence.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Commit metadata and changed files ==='
git show -s --format='%H%n%P%n%s%n%b' cb209812
git diff-tree --no-commit-id --name-status -r cb209812
git show --stat --oneline --no-renames cb209812

echo '=== Commit diff ==='
git diff --no-ext-diff --unified=70 cb209812^ cb209812 -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/src/testing.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/test/director.test.ts \
  packages/tutorial/test/platform-storage.test.ts \
  docs/TUTORIAL_ENGINE.md

echo '=== Affected source and test outlines ==='
for file in \
  packages/tutorial/src/driver.ts \
  packages/tutorial/src/testing.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/test/director.test.ts \
  packages/tutorial/test/platform-storage.test.ts
do
  if [ -f "$file" ]; then
    printf '\n--- %s ---\n' "$file"
    ast-grep outline "$file" --items all --type function,interface,type --view compact || true
  fi
done

echo '=== Focused implementation references ==='
rg -n -C4 --glob '!**/node_modules/**' \
  '(dismiss|replay|isVisibleTutorialTarget|installTutorialDebugBridge|createMemoryTutorialProgressStore|unavailable)' \
  packages/tutorial/src packages/tutorial/test docs/TUTORIAL_ENGINE.md

echo '=== Whitespace check ==='
git show --check --format= cb209812 || true

echo '=== Review thread status ==='
gh api graphql -F owner='imjlk' -F name='mpgd-kit' -F number=136 -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 20) {
            nodes { author { login } body createdAt }
          }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | {resolved: .isResolved, path, line, originalLine, comments: [.comments.nodes[] | {author: .author.login, body: .body}]}' \
  || echo 'Review-thread metadata is unavailable in this sandbox.'

Length of output: 165223


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== All replay-trigger call sites and imports ==='
rg -n -C4 --glob '*.{ts,tsx,mts,cts,md}' \
  '(bindTutorialReplayTrigger\s*\(|bindTutorialReplayTrigger\s*})' \
  .

echo '=== Debug bridge installation call sites ==='
rg -n -C5 --glob '*.{ts,tsx,mts,cts,md}' \
  '(installTutorialDebugBridge\s*\(|installTutorialDebugBridge\s*})' \
  .

echo '=== Tutorial harness replay and teardown wiring ==='
sed -n '1,340p' examples/tutorial-driver/src/main.ts

echo '=== New testing helper regression suite ==='
sed -n '1,260p' packages/tutorial/test/testing.test.ts

echo '=== Current public export maps and driver API declarations ==='
sed -n '1,180p' packages/tutorial/package.json
sed -n '1,180p' packages/tutorial/src/driver.ts
sed -n '130,260p' packages/tutorial/src/testing.ts

echo '=== Corrected source outlines ==='
ast-grep outline packages/tutorial/src/driver.ts --items all --type function,interface,type --view signatures || true
ast-grep outline packages/tutorial/src/testing.ts --items all --type function,interface,type --view signatures || true

Length of output: 34834


@imjlk Reviewed commit cb209812 and the resolved-thread evidence.

I found no new actionable issues.

The replay trigger resets a successful same-step dismissal only after director.replay() succeeds. The host trigger and debug bridge both use this lifecycle.

The target resolver now rejects targets hidden by an ancestor. It still supports an off-viewport renderable fallback.

The debug bridge teardown skips destroyed predecessors. It does not restore a stale bridge after out-of-order teardown.

The unavailable memory store now ignores saves. Its snapshot and save log remain unchanged.

GitHub reports zero unresolved review threads. git show --check reports no whitespace errors.

I could not run the reported test, build, or browser-validation commands in this sandbox.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cb2098122c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

Review round 4 addressed in 0c360648: pending skip/replay serialization (including skips begun during async preparation), same-key missing-target replay reporting, and rendered-control focus with unanchored fallback recovery. Validation: tutorial 43/43, package/demo typechecks, package build + dist import, Vite demo build, Chromium 7-scenario smoke, independent final audits, and git diff check.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Please review latest commit 0c360648; all three latest threads have evidence replies and are resolved.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Please review latest commit 0c360648 and resolved thread evidence.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/tutorial/src/driver.ts (1)

480-492: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Cache the target candidate scan per mutation batch.

isTargetSelectionMutation runs per record. For attribute, characterData, and childList records it calls collectTargetSelectionElements, which calls findComposedTutorialTargets and walks the composed tree including open shadow roots. A single batch with many attribute records repeats that whole scan for each record. Attribute churn on animated hosts makes this a hot path inside the MutationObserver callback.

Compute the candidate list once per processMutationRecords invocation and pass it into the per-record checks.

♻️ Suggested approach
   function processMutationRecords(records: readonly MutationRecord[]): void {
     if (activePresentation === null && instance === null) {
       observeDocumentOnlyMutations();
       return;
     }
 
     reconcileMutationRoots();
     const hostRecords = records.filter((record) => !isDriverOwnedMutation(record));
 
     if (hostRecords.length > 0) {
-      scheduleRefresh(hostRecords.some(isTargetSelectionMutation));
+      const presentation = activePresentation;
+      const candidates = presentation === null
+        ? []
+        : collectTargetSelectionElements(presentation);
+      scheduleRefresh(hostRecords.some(
+        (record) => isTargetSelectionMutation(record, candidates),
+      ));
     }
   }

Then use the passed candidates in isTargetSelectionMutation and stylesheetMutationAffectsLookupRoot instead of recomputing.

Also applies to: 620-646

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tutorial/src/driver.ts` around lines 480 - 492, Cache the target
candidate elements once inside processMutationRecords for each mutation batch,
then pass that list through the per-record target-selection checks. Update
isTargetSelectionMutation and stylesheetMutationAffectsLookupRoot to accept and
reuse the candidates instead of calling collectTargetSelectionElements or
findComposedTutorialTargets repeatedly, while preserving existing mutation
filtering and refresh behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/tutorial/src/driver.ts`:
- Around line 480-492: Cache the target candidate elements once inside
processMutationRecords for each mutation batch, then pass that list through the
per-record target-selection checks. Update isTargetSelectionMutation and
stylesheetMutationAffectsLookupRoot to accept and reuse the candidates instead
of calling collectTargetSelectionElements or findComposedTutorialTargets
repeatedly, while preserving existing mutation filtering and refresh behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 04d8a5cd-e1d5-4ca2-b3bb-b8fc50d3210c

📥 Commits

Reviewing files that changed from the base of the PR and between 38fa5d9 and cc2b54b.

📒 Files selected for processing (2)
  • packages/tutorial/src/driver.ts
  • packages/tutorial/test/driver.test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc2b54bfaf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Latest head: 41a8ca64

The three latest findings are fixed and resolved: target-only Shadow DOM pass-through without opening unrelated UI, durable persistence of valid migrated progress, and default alertdialog ownership. Tutorial 115/115, package/root checks, OCR plus manual final audit, and diff check pass.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Please review latest head 41a8ca64. All latest threads are resolved; tutorial tests are 115/115 and the full workspace check passes.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit='41a8ca64'

echo '=== Commit metadata and changed files ==='
git show -s --format='%H%n%P%n%s%n%b' "$commit"
git diff-tree --no-commit-id --name-status -r "$commit"
git show --stat --oneline --no-renames "$commit"

echo '=== Focused diff ==='
git diff --no-ext-diff --unified=50 "${commit}^" "$commit" -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/src/platform-storage.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/test/platform-storage.test.ts \
  packages/tutorial/test/director.test.ts \
  packages/tutorial/driver.css \
  docs/TUTORIAL_ENGINE.md

echo '=== Source outlines ==='
ast-grep outline packages/tutorial/src/driver.ts --items all --view signatures || true
ast-grep outline packages/tutorial/src/platform-storage.ts --items all --view signatures || true

echo '=== Relevant implementation and test references ==='
rg -n -C6 --glob '!**/node_modules/**' \
  '(ShadowRoot|shadowRoot|pointer|pass-through|elementFromPoint|elementsFromPoint|overlay|target-only|migration|migrate|parseTutorialProgress|snapshot|save|load|alertdialog|dialog|aria-modal|role|ownership|destroy|cleanup)' \
  packages/tutorial/src/driver.ts \
  packages/tutorial/src/platform-storage.ts \
  packages/tutorial/src/progress.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/test/platform-storage.test.ts \
  packages/tutorial/test/director.test.ts \
  docs/TUTORIAL_ENGINE.md

echo '=== Whitespace check ==='
git show --check --format= "$commit" || true

echo '=== Unresolved review threads ==='
gh api graphql \
  -F owner='imjlk' \
  -F name='mpgd-kit' \
  -F number=136 \
  -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 10) {
            nodes { author { login } body createdAt }
          }
        }
      }
    }
  }
}' \
  --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, originalLine, comments: [.comments.nodes[] | {author: .author.login, body: .body, createdAt}]}]' \
  || echo 'Review-thread metadata is unavailable in this sandbox.'

Length of output: 201246


🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit='41a8ca64'

echo '=== Compact changed hunks ==='
git diff --no-ext-diff --unified=18 "${commit}^" "$commit" -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/src/platform-storage.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/test/platform-storage.test.ts

echo '=== Changed line ranges ==='
git diff --no-ext-diff --unified=0 "${commit}^" "$commit" -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/src/platform-storage.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/test/platform-storage.test.ts \
  | rg '^diff --git|^@@' || true

echo '=== Pass-through and hit-test implementation ==='
rg -n -C10 \
  '(target-proxy|targetOnly|passThrough|pointerEvents|elementFromPoint|elementsFromPoint|sync.*[Pp]ointer|overlay)' \
  packages/tutorial/src/driver.ts

echo '=== Modal discovery and ownership implementation ==='
rg -n -C10 \
  '(alertdialog|modalSelector|find.*[Mm]odal|aria-modal|aria-owns|modal ownership|sync.*[Mm]odal)' \
  packages/tutorial/src/driver.ts

echo '=== Progress-store migration and save implementation ==='
cat -n packages/tutorial/src/platform-storage.ts
echo '=== Progress parser contract ==='
sed -n '1,220p' packages/tutorial/src/progress.ts

echo '=== Exact new regression test blocks ==='
sed -n '2185,2265p' packages/tutorial/test/driver.test.ts
sed -n '540,590p' packages/tutorial/test/driver.test.ts
rg -n -A75 -B10 '(migrat|migration)' packages/tutorial/test/platform-storage.test.ts

Length of output: 66139


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Driver.js dependency version ==='
rg -n -C3 '"driver\.js"|driver\.js@|driverjs' \
  packages/tutorial/package.json package.json pnpm-lock.yaml

echo '=== Overlay and proxy stacking rules in repository sources ==='
rg -n -C5 --glob '!**/node_modules/**' \
  '(driver-overlay|driver-active-element|mpgd-tutorial-target-proxy|z-index|zIndex)' \
  packages/tutorial \
  examples/tutorial-driver

echo '=== Driver.js package artifacts, if present ==='
fd -HI '^driver\.(css|js|mjs)$|^driverjs\.' . node_modules 2>/dev/null \
  | head -50 \
  | while IFS= read -r file; do
      echo "--- $file ---"
      rg -n -C3 '(driver-overlay|z-index|zIndex)' "$file" || true
    done

echo '=== Lockfile package resolution ==='
rg -n -A25 -B5 '^  driver\.js@|^  driver\.js:' pnpm-lock.yaml

Length of output: 61425


🤖 web_search executed:

Driver.js version 1.8.0 source CSS .driver-overlay z-index

Length of output: 8266


@imjlk I found one actionable issue.

packages/tutorial/src/driver.ts uses target.click() in the target-proxy listener at Line 1454. This creates an untrusted synthetic click event. It does not forward the user's pointer event, coordinates, button state, or pointerdown and pointerup events.

For a Shadow DOM action target that is blocked by the Driver overlay, host code that checks event.isTrusted, listens for pointer events, or uses click coordinates will not run correctly. The proxy test only verifies a basic click listener, so it does not detect this condition.

Keep actual pointer input on the target for the target rectangle. Do not replace it with HTMLElement.click(). Add a browser test that uses a target handler which requires event.isTrusted or listens for pointerdown.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 41a8ca64d4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

Addressed the latest CodeRabbit top-level finding in 15509dc7: removed synthetic target forwarding. If and only if the Driver overlay itself blocks a Shadow target, the overlay becomes hit-transparent while four package-owned regions block every point outside the target rect; the popover remains above them. No app ancestor layout/stacking mutation is used, and non-Driver occluders never open a hole. Real Chromium confirms actual mouse pointerdown + click reach the target, outside UI remains blocked, and destroy restores overlay/blockers. Tutorial 116/116, full workspace check, final audit, and diff check pass.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Latest head: 15509dc7. The trusted Shadow input redesign removes synthetic click forwarding and preserves blocking outside the target. Tutorial 116/116, full workspace check, real Chromium pointerdown/click and cleanup verification, and final audit pass.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Please review latest head 15509dc7. The latest top-level finding is addressed with real trusted pointer input and target-exterior blocking; 116/116 and full checks pass.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Latest head: 44e9b01a. All delayed threads are resolved. Trusted Shadow input uses a target-only real pointer path with outside blocking, merged host aria-owns removes only tutorial tokens, and foreign-document resolver results are rejected. Tutorial 118/118, full workspace check, real Chromium verification, final audit, and diff check pass.

@imjlk

imjlk commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Please review latest head 44e9b01a. Synthetic proxy forwarding is gone; real pointer input and outside blocking are verified in Chromium. All threads are resolved and 118/118/full checks pass.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit='44e9b01a'

echo '=== Commit metadata and changed files ==='
git show -s --format='%H%n%P%n%s%n%b' "$commit"
git diff-tree --no-commit-id --name-status -r "$commit"
git show --stat --oneline --no-renames "$commit"

echo '=== Focused diff ==='
git diff --no-ext-diff --unified=45 "${commit}^" "$commit" -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/driver.css \
  docs/TUTORIAL_ENGINE.md

echo '=== Source and test outlines ==='
ast-grep outline packages/tutorial/src/driver.ts --items all --view signatures || true
ast-grep outline packages/tutorial/test/driver.test.ts --items all --type function --view signatures || true

echo '=== Pointer pass-through, ARIA ownership, and document checks ==='
rg -n -C8 --glob '!**/node_modules/**' \
  '(pointer-events|pointerEvents|elementFromPoint|elementsFromPoint|overlay|blocker|target.*rect|ShadowRoot|shadowRoot|aria-owns|ariaOwns|ownerDocument|foreign|Document|resolver|click|pointerdown|destroy|restore)' \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/driver.css \
  docs/TUTORIAL_ENGINE.md

echo '=== Whitespace check ==='
git show --check --format= "$commit" || true

echo '=== Unresolved review threads ==='
gh api graphql \
  -F owner='imjlk' \
  -F name='mpgd-kit' \
  -F number=136 \
  -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 10) {
            nodes { author { login } body createdAt }
          }
        }
      }
    }
  }
}' \
  --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, originalLine, comments: [.comments.nodes[] | {author: .author.login, body: .body, createdAt}]}]' \
  || echo 'Review-thread metadata is unavailable in this sandbox.'

Length of output: 200937


🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit='44e9b01a'

echo '=== Exact changed hunks ==='
git diff --no-ext-diff --unified=18 "${commit}^" "$commit" -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts

echo '=== Changed line ranges ==='
git diff --no-ext-diff --unified=0 "${commit}^" "$commit" -- packages/tutorial/src/driver.ts \
  | rg '^@@' || true

echo '=== Target-only overlay opening and blocker lifecycle ==='
rg -n -C10 \
  '(outside-blocker|target.*hole|pointerEvents.*none|pointer-events.*none|sync.*(Blocker|Overlay|HitTest)|create.*Blocker|remove.*Blocker|blocker)' \
  packages/tutorial/src/driver.ts

echo '=== ARIA ownership and custom resolver document validation ==='
rg -n -C10 \
  '(aria-owns|ariaOwns|ownerDocument|must belong to the presenter document|resolveTarget)' \
  packages/tutorial/src/driver.ts

echo '=== Exact new tests ==='
sed -n '1015,1060p' packages/tutorial/test/driver.test.ts
sed -n '1805,1840p' packages/tutorial/test/driver.test.ts
sed -n '2238,2395p' packages/tutorial/test/driver.test.ts

Length of output: 56496


@imjlk The focused inspection confirms the resolver and ARIA ownership fixes. I need to verify the Driver.js overlay stacking value before I complete the review.

You are interacting with an AI system.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit='15509dc7'

echo '=== Commit metadata and changed files ==='
git show -s --format='%H%n%P%n%s%n%b' "$commit"
git diff-tree --no-commit-id --name-status -r "$commit"
git show --stat --oneline --no-renames "$commit"

echo '=== Focused diff ==='
git diff --no-ext-diff --unified=45 "${commit}^" "$commit" -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/driver.css \
  docs/TUTORIAL_ENGINE.md

echo '=== Source structure ==='
ast-grep outline packages/tutorial/src/driver.ts --items all --view signatures || true

echo '=== Pointer path, blocker lifecycle, and cleanup ==='
rg -n -C8 --glob '!**/node_modules/**' \
  '(target-proxy|\\.click\\(|pointer-events|pointerEvents|blocker|overlay|elementFromPoint|elementsFromPoint|ShadowRoot|shadowRoot|target.*rect|pointerdown|pointerup|click|destroy|cleanup|removeChild)' \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/driver.css \
  docs/TUTORIAL_ENGINE.md

echo '=== Exact implementation ranges ==='
rg -n \
  '(function sync|function clear|function remove|function create|outside-blocker|pointerEvents)' \
  packages/tutorial/src/driver.ts || true

echo '=== Relevant regression tests ==='
rg -n -C10 \
  '(pointerdown|isTrusted|outside.*block|block.*outside|Shadow.*target|destroy.*block|cleanup.*block)' \
  packages/tutorial/test/driver.test.ts || true

echo '=== Whitespace check ==='
git show --check --format= "$commit" || true

echo '=== Unresolved review threads ==='
gh api graphql \
  -F owner='imjlk' \
  -F name='mpgd-kit' \
  -F number=136 \
  -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 10) {
            nodes { author { login } body createdAt }
          }
        }
      }
    }
  }
}' \
  --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, originalLine, comments: [.comments.nodes[] | {author: .author.login, body: .body, createdAt}]}]' \
  || echo 'Review-thread metadata is unavailable in this sandbox.'

Length of output: 24505


🏁 Script executed:

#!/bin/bash
set -uo pipefail

commit='15509dc7'

echo '=== Pointer guard lifecycle and call sites ==='
rg -n -C8 -e 'allowShadowTargetThroughOverlay' \
  -e 'clearTargetOverlayPointerGuard' \
  -e 'targetOverlayPointerRestore' \
  -e 'scheduleShadowTargetHitTest' \
  -e 'cancelShadowTargetHitTest' \
  -e 'syncTargetAcknowledgeBinding' \
  packages/tutorial/src/driver.ts || true

echo '=== Source: presenter rendering, refresh, and destruction ==='
sed -n '430,780p' packages/tutorial/src/driver.ts
sed -n '1040,1560p' packages/tutorial/src/driver.ts

echo '=== Source: geometry monitor and target-state cleanup ==='
sed -n '780,1045p' packages/tutorial/src/driver.ts
sed -n '1560,1715p' packages/tutorial/src/driver.ts

echo '=== Stacking and pointer styles ==='
rg -n -C5 -e 'z-index' -e 'zIndex' -e 'driver-overlay' -e 'driver-popover' \
  packages/tutorial/driver.css \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts || true

echo '=== All blocker and proxy references in the requested commit ==='
git grep -n -E 'target-proxy|outside-blocker|mpgdTutorialOutsideBlocker|targetOverlayPointerRestore' "$commit" -- \
  packages/tutorial/src/driver.ts packages/tutorial/test/driver.test.ts packages/tutorial/driver.css \
  || true

echo '=== Tests adjacent to the changed target-input test ==='
sed -n '2140,2345p' packages/tutorial/test/driver.test.ts

echo '=== Whitespace ==='
git show --check --format= "$commit" || true

echo '=== Unresolved GitHub review threads ==='
gh api graphql \
  -F owner='imjlk' -F name='mpgd-kit' -F number=136 \
  -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 10) {
            nodes { author { login } body }
          }
        }
      }
    }
  }
}' \
  --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, originalLine, comments: [.comments.nodes[] | {author: .author.login, body: .body}]}]' \
  || true

Length of output: 83799


@imjlk Reviewed commit 15509dc7.

I found no new actionable issues.

The overlay pass-through path no longer uses HTMLElement.click(). The target can receive browser-dispatched pointer input.

The presenter disables overlay pointer interception only when .driver-overlay blocks the target center. It does not open a target region when another element blocks the target.

The presenter adds four package-owned exterior blockers. The cleanup paths remove these blockers and restore the previous inline overlay pointer-events value only when the presenter-owned value remains.

The tests cover blocker creation, cleanup on destroy(), and the non-Driver occluder case. GitHub reports zero unresolved review threads. git show --check reports no whitespace errors.

I could not independently run the reported tutorial tests, Chromium scenario, or workspace checks in this sandbox.

You are interacting with an AI system.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 44e9b01a03

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
@imjlk

imjlk commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Please review latest commit 7e00f4ea: post-disable composed hit verification, light-DOM target hit testing, skip-before-inactive ordering, and closed-ShadowRoot activation monitoring. Tutorial 122/122 and full workspace check pass; all prior threads are resolved.

@imjlk

imjlk commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Please review latest commit 7e00f4ea and the four resolved target/skip lifecycle threads. Tutorial 122/122 and full workspace check pass.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit='7e00f4ea'

echo '=== Commit metadata and changed files ==='
git show -s --format='%H%n%P%n%s%n%b' "$commit"
git diff-tree --no-commit-id --name-status -r "$commit"
git show --stat --oneline --no-renames "$commit"

echo '=== Focused diff ==='
git diff --no-ext-diff --unified=45 "${commit}^" "$commit" -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/driver.css \
  docs/TUTORIAL_ENGINE.md

echo '=== Source and test structure ==='
ast-grep outline packages/tutorial/src/driver.ts --items all --view signatures || true
ast-grep outline packages/tutorial/test/driver.test.ts --items all --type function --view signatures || true

echo '=== Target interaction, hit-test, Shadow DOM, and skip lifecycle references ==='
rg -n -C7 --glob '!**/node_modules/**' \
  '(post-disable|pointer-events|pointerEvents|elementFromPoint|elementsFromPoint|hit.test|light.DOM|ShadowRoot|shadowRoot|closed|attachShadow|activation|activatable|skip|onSkip|skipTutorial|inactive|destroy|refresh|restart)' \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/driver.css \
  docs/TUTORIAL_ENGINE.md

echo '=== Relevant implementation ranges ==='
sed -n '620,1080p' packages/tutorial/src/driver.ts
sed -n '1080,1620p' packages/tutorial/src/driver.ts
sed -n '1620,2060p' packages/tutorial/src/driver.ts

echo '=== Changed regression-test ranges ==='
git diff --no-ext-diff --unified=0 "${commit}^" "$commit" -- packages/tutorial/test/driver.test.ts \
  | rg -n -C3 '^\+.*(it\(|test\(|expect|pointer|hit|Shadow|shadow|skip|inactive|activation)' || true

echo '=== Whitespace check ==='
git show --check --format= "$commit"

echo '=== Unresolved GitHub review threads ==='
gh api graphql \
  -F owner='imjlk' \
  -F name='mpgd-kit' \
  -F number=136 \
  -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 10) {
            nodes { author { login } body createdAt }
          }
        }
      }
    }
  }
}' \
  --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, originalLine, comments: [.comments.nodes[] | {author: .author.login, body: .body, createdAt}]}]'

Length of output: 127126


🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit='7e00f4ea'

echo '=== Exact commit diff for implementation and tests ==='
git diff --no-ext-diff --unified=20 "${commit}^" "$commit" -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/driver.css

echo '=== Skip handler and state transitions ==='
rg -n -C12 \
  '(onSkip|pendingSkipOperations|dismissedPresentationKey|reportActive\\(false\\)|destroyInstancePreservingTarget)' \
  packages/tutorial/src/driver.ts

echo '=== Overlay guard and composed hit-test functions ==='
rg -n -A180 -B20 \
  '(function downgradeInactiveTargetClickMode|function isShadowTutorialTargetHitTestable|function scheduleShadowTargetHitTest|function allowShadowTargetThroughOverlay|function clearTargetOverlayPointerGuard)' \
  packages/tutorial/src/driver.ts

echo '=== Driver overlay, popover, and blocker stacking values ==='
rg -n -C5 \
  '(driver-overlay|driver-popover|mpgdTutorialOutsideBlocker|outside-blocker|z-index|zIndex)' \
  packages/tutorial/src/driver.ts \
  packages/tutorial/driver.css \
  packages/tutorial/package.json \
  pnpm-lock.yaml

echo '=== New tests in full context ==='
sed -n '2180,2390p' packages/tutorial/test/driver.test.ts
sed -n '3250,3375p' packages/tutorial/test/driver.test.ts

echo '=== Commit state ==='
git status --short

Length of output: 72815


@imjlk Reviewed commit 7e00f4ea.

I found no new actionable issues.

The presenter now verifies the composed hit path after it disables overlay pointer interception. If another element still blocks the target, it restores the overlay and does not create outside blockers.

The hit-test path now also applies to light-DOM targets. A light-DOM acknowledge target blocked by the overlay falls back to the Next control.

The skip callback starts before the presenter reports inactive state.

The 250 ms geometry monitor now detects activatability changes for a target inside a closed ShadowRoot.

The new regression tests cover these paths. The worktree is clean. git show --check reports no whitespace errors. GitHub reports zero unresolved review threads.

I could not independently run the reported tutorial tests or workspace check in this sandbox.

You are interacting with an AI system.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7e00f4ea2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tutorial/src/driver.ts Resolved
Comment thread packages/tutorial/src/driver.ts Resolved
@imjlk

imjlk commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Final scoped review request for 1423553d: composed overflow-clipped hit sampling and overlay-level outside blockers. Tutorial 123/123, full workspace check, scoped final audit, and diff check pass. Please report only release-blocking correctness regressions.

@imjlk

imjlk commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Final scoped review request for 1423553d. The two prior hit-test/blocker threads are fixed and resolved; tutorial 123/123 and full workspace check pass.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit='1423553d'

echo '=== Commit metadata and changed files ==='
git show -s --format='%H%n%P%n%s%n%b' "$commit"
git diff-tree --no-commit-id --name-status -r "$commit"
git show --stat --oneline --no-renames "$commit"

echo '=== Focused diff ==='
git diff --no-ext-diff --unified=55 "${commit}^" "$commit" -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/driver.css \
  docs/TUTORIAL_ENGINE.md

echo '=== Source and test outlines ==='
ast-grep outline packages/tutorial/src/driver.ts --items all --view signatures || true
ast-grep outline packages/tutorial/test/driver.test.ts --items all --type function --view signatures || true

echo '=== Scoped implementation and test references ==='
rg -n -C8 --glob '!**/node_modules/**' \
  '(overflow|clip|clipp|hit.test|elementFromPoint|elementsFromPoint|composed|ShadowRoot|shadowRoot|overlay|blocker|pointer-events|pointerEvents|target.*rect|outside|z-index|zIndex|destroy|cleanup)' \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/driver.css \
  docs/TUTORIAL_ENGINE.md

echo '=== Exact implementation ranges ==='
rg -n -A180 -B20 \
  '(function is.*TutorialTargetHitTestable|function .*HitTest|function .*Blocker|function allow.*ThroughOverlay|function clear.*Pointer|function sync.*Blocker)' \
  packages/tutorial/src/driver.ts || true

echo '=== Regression test ranges ==='
git diff --no-ext-diff --unified=0 "${commit}^" "$commit" -- packages/tutorial/test/driver.test.ts \
  | rg -n -C4 '^\+.*(it\(|test\(|expect|overflow|clip|hit|blocker|overlay|pointer|destroy)' || true

echo '=== Whitespace check ==='
git show --check --format= "$commit"

echo '=== Unresolved GitHub review threads ==='
gh api graphql \
  -F owner='imjlk' \
  -F name='mpgd-kit' \
  -F number=136 \
  -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          originalLine
          comments(first: 10) {
            nodes { author { login } body createdAt }
          }
        }
      }
    }
  }
}' \
  --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, originalLine, comments: [.comments.nodes[] | {author: .author.login, body: .body, createdAt}]}]' \
  || echo 'Review-thread metadata is unavailable in this sandbox.'

Length of output: 181199


🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit='1423553d'

echo '=== Exact scoped commit diff ==='
git diff --no-ext-diff --unified=25 "${commit}^" "$commit" -- \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts \
  packages/tutorial/driver.css

echo '=== Visible-rectangle and viewport helper definitions ==='
rg -n -A170 -B25 \
  '(function resolveTutorialTargetVisibleRect|function resolveViewportRect|function .*Clip|function .*Clipp|function .*Composed.*Ancestor|function .*Rect)' \
  packages/tutorial/src/driver.ts

echo '=== Call sites for the visible rectangle helper ==='
rg -n -C5 'resolveTutorialTargetVisibleRect\s*\(' \
  packages/tutorial/src/driver.ts

echo '=== Overlay, popover, and blocker stacking declarations ==='
rg -n -C8 \
  '(driver-overlay|driver-popover|mpgd-tutorial-outside-blocker|z-index|zIndex)' \
  packages/tutorial/driver.css \
  packages/tutorial/src/driver.ts \
  packages/tutorial/test/driver.test.ts

echo '=== Commit state ==='
git status --short

Length of output: 86874


@imjlk Reviewed commit 1423553d for release-blocking correctness regressions.

I found no release-blocking issues.

resolveTutorialTargetVisibleRect() now intersects the target rectangle with the viewport and every overflow-clipping composed ancestor. The hit test and target opening use this same visible rectangle. The new regression test verifies that sampling stays inside the visible clipped region.

The exterior blockers now use the computed .driver-overlay z-index. This keeps blockers at the overlay level without a duplicated hard-coded stacking value. The regression test verifies the blocker z-index and cleanup.

git show --check reports no whitespace errors. The worktree is clean. GitHub reports zero unresolved review threads.

I did not independently run the reported test suite or workspace check in this sandbox.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 1423553d89

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@imjlk
imjlk merged commit 7e26c62 into main Aug 13, 2026
6 checks passed
@imjlk
imjlk deleted the codex/tutorial-engine branch August 13, 2026 01:29
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.

1 participant