Skip to content

Fix rapid typing and save sequencing regressions - #198

Merged
flyingrobots merged 40 commits into
mainfrom
fix/typing-history-regression
Jul 2, 2026
Merged

flyingrobots merged 40 commits into
mainfrom
fix/typing-history-regression

Conversation

@flyingrobots

Copy link
Copy Markdown
Owner

Summary

This fixes the high-priority typing/history regression where rapid insert-mode typing could submit independent production text mutations concurrently and corrupt the Echo-backed text session.

Changes:

  • Serialize production text edit commands per ProductionTextSession so rapid keypresses admit in planned order.
  • Keep intermediate applied edit results in Echo history without clobbering the optimistic editor projection.
  • Add a shared text operation sequencer so save/export/checkpoint wait behind queued edits before touching the Echo frontier.
  • Let Ctrl-S and :wq queue save/export behind pending edits instead of exporting a partial frontier.
  • Keep :wq cautious: it queues the save but does not arm quit confirmation while an unresolved edit chain exists.
  • Make Ctrl-H consistently open Echo history, including after the drawer was previously in worldline mode.

Root Cause

Fast typing created multiple asynchronous edit commands from the optimistic local editor. Those commands could reach the production text session concurrently, so later characters could be applied before earlier characters or against stale session state.

The first visible symptom was dropped/scrambled text during normal-speed typing. The adjacent save risk was that Ctrl-S could otherwise materialize an older Echo frontier while the local optimistic editor already displayed the full text.

Validation

  • npm run check
  • Result: 1059 tests, 1050 pass, 9 skipped, 0 failed
  • Quality gate: regressions none
  • git diff --check

Focused coverage added/updated:

  • Rapid concurrent typing serializes production insert settlement and records each edit.
  • Ctrl-S during queued rapid typing does not call exportSnapshot() until queued edits settle, then saves the full sentence and checkpoints.
  • Pending production save requests queue export work rather than blocking on pending intent state.
  • Pending :wq queues save without arming quit confirmation during unresolved edit chains.
  • Ctrl-H reopens/switches to Echo history after worldline history mode was active.

Follow-Up

Queued obstruction semantics still deserve a dedicated follow-up regression for a applied, b obstructed, cdef already queued. Existing dependent-obstruction tests still pass, but that exact queue-level case should be made explicit before treating the queue model as complete under obstruction.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@flyingrobots, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 25 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e3951828-bfc3-4f84-acdc-0faffc8fc796

📥 Commits

Reviewing files that changed from the base of the PR and between f10f7c5 and 183f97b.

📒 Files selected for processing (12)
  • scenes/continuum-gate.jedit-scene
  • spec/fixtures/title-scene-visual-witness.json
  • spec/title-braille-sampling-policy.spec.mjs
  • spec/title-scene-loader.spec.mjs
  • spec/title-scene-neon-dispersion.spec.mjs
  • spec/title-screen-optics.spec.mjs
  • spec/workspace-title-braille-sampling.spec.mjs
  • src/adapters/title-scene-loader.ts
  • src/adapters/title-scene-object-decoder.ts
  • src/app/workspace/title-braille-sampling.ts
  • src/app/workspace/viewer-content.ts
  • src/ui/title-screen-optics.ts

Walkthrough

This PR adds a per-session workspace text-operation sequencer, threads it through edit/checkpoint/export flow, changes pending-save handling, updates history/settings key behavior, and introduces a new default title scene with orbit-based lighting. Tests and harnesses were expanded to cover the new sequencing, rendering, and scene-loading behavior.

Changes

Workspace cutover

Layer / File(s) Summary
Sequencer and command plumbing
src/app/workspace/workspace-text-operation-sequencer.ts, src/app/workspace/workspace-text-commands.ts, src/app/workspace/workspace-runtime-dependencies.ts, src/adapters/workspace-app.ts, src/adapters/workspace-production-text-dependencies.ts, src/app/workspace/editor-trust-preflight-runtime-fixtures.ts, src/app/workspace/key-binding-context.ts, src/app/workspace/runtime.ts, src/app/workspace/focused-pane-key-bindings.ts, src/app/workspace/viewer-key.ts, src/app/workspace/viewer.ts
Adds a WeakMap-backed per-session sequencer with obstruction tracking and export de-duplication, threads textOperationSequencer through runtime wiring and command request shapes, and routes viewer-key edit planning through the sequencer.
Intermediate edit results and authority state
src/app/workspace/workspace-text-runtime-state.ts, src/app/workspace/workspace-text-result-guards.ts, src/app/workspace/workspace-text-reading-cache.ts, src/app/workspace/editor-session.ts, src/app/workspace/workspace-save-key.ts
Adds intermediate edit-result handling, consolidates authority/editor derivation, refreshes highlighting from the active editor, and removes the unresolved-intent save block.
Drawer, settings, and title rendering
src/app/workspace/drawer.ts, src/app/settings-session.ts, src/app/workspace/settings-key-bindings.ts, src/app/keybindings.ts, src/ui/settings-drawer.ts
Switches the history drawer to Echo view on open and toggle, adds a q close key, updates settings focus wrapping and row labels, and revises settings surface rendering and tokens.
Workspace and sequencer tests
spec/workspace-echo-test-utils.mjs, spec/workspace-echo-app-harness.mjs, spec/workspace-helpers.mjs, spec/workspace-text-operation-sequencer.spec.mjs, spec/workspace-text-operation-sequencer-guard.spec.mjs, spec/workspace-text-result-guards.spec.mjs, spec/workspace-app-echo-typing-sequencing.spec.mjs, spec/workspace-text-settlement-refresh.spec.mjs, spec/workspace-text-cutover.spec.mjs, spec/workspace-command-line.spec.mjs, spec/workspace-text-boundaries.spec.mjs, spec/workspace-reading-cache-guard.spec.mjs, spec/workspace-app-echo-cutover.spec.mjs, spec/workspace-key-bindings.spec.mjs, spec/workspace-lifecycle.spec.mjs, spec/settings-drawer.spec.mjs, spec/settings-session.spec.mjs, spec/workspace-runtime.spec.mjs, spec/workspace-title-screen.spec.mjs
Adds shared harness helpers and expands coverage for queued typing, save coalescing, intermediate refresh, key handling, and byte-offset window calculations.
Echo cutover tests
spec/workspace-app-echo-typing-sequencing.spec.mjs, spec/workspace-text-settlement-refresh.spec.mjs, spec/workspace-text-cutover.spec.mjs, spec/workspace-command-line.spec.mjs
Updates save/queue expectations to allow pending production intents, validates coalesced exports, and exercises queued typing and settlement behavior.
Settings and history key tests
spec/settings-drawer.spec.mjs, spec/settings-session.spec.mjs, spec/workspace-lifecycle.spec.mjs, spec/workspace-key-bindings.spec.mjs
Updates specs for the new close key, wrap-around focus movement, settings drawer text, and Echo history drawer switching.

Title scene lighting

Layer / File(s) Summary
Scene assets and loader ordering
scenes/continuum-gate.jedit-scene, src/ports/title-scene-loader.ts, scenes/bunny.jedit-scene
Adds continuum-gate.jedit-scene as the new default built-in scene, and updates the bunny scene camera and chrome backdrop objects.
Orbit light decoding and environment math
src/adapters/title-scene-environment-decoder.ts, src/ui/title-scene-environment.ts, src/ui/title-screen.ts
Decodes optional light orbit data, computes time-aware light direction, and changes day/night lighting and title-screen ray context to use the new orbit-aware helpers.
Title rendering and material selection
src/app/workspace/title-mesh-materials.ts, src/app/workspace/viewer.ts, spec/workspace-title-screen.spec.mjs, spec/title-scene-render.spec.mjs
Broadens title mesh material targeting, applies title tokens cell-by-cell during title painting, and updates render tests for animated light direction and title-row styling.
Scene and rendering specs
spec/title-scene-loader.spec.mjs, spec/title-scene-neon-dispersion.spec.mjs, spec/title-scene-profile-cli.spec.mjs, spec/title-scene-render.spec.mjs, spec/workspace-runtime.spec.mjs, spec/workspace-title-screen.spec.mjs
Reworks scene loader, renderer, profile, and workspace scene tests to cover the new default scene and orbit-driven lighting behavior.

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

Possibly related PRs

Poem

A sequencer hums, then saves fall into line,
Echo waits its turn; the exports behave just fine.
Press q, the drawers obey, the settings rows won’t drift,
A new scene wakes with chrome and orbit in its gift.
Review the WeakMaps — they’re watching in the night.

🚥 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 matches the main change set: rapid typing and save sequencing fixes.
Description check ✅ Passed The description is detailed and directly describes the typing, history, save, and Ctrl-H changes in the PR.
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.

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.

@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

createWorkspaceTextExportCmd({

P1 Badge Abort queued saves after an edit obstruction

When Ctrl-S/:w is now accepted while a predicted edit is still pending, this export command is queued behind the edit operations. If any earlier edit returns Obstructed, applyTextEditObstruction marks the authority obstructed, but the queued export still runs and is accepted because its request id is now the latest; a successful export then clears dirty/obstruction state and can write a snapshot after a failed edit. The queued save needs to be canceled or rejected when a preceding edit obstruction is recorded.

ℹ️ 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 src/app/workspace/workspace-text-runtime-state.ts Outdated
Comment thread src/app/workspace/workspace-text-commands.ts Outdated

@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: 4

🤖 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 `@spec/workspace-app-echo-cutover.spec.mjs`:
- Around line 120-130: The receipt generation in the insertText mock is using
calls.insert.length after awaiting pending.promise, which can cause multiple
queued inserts to resolve with the same receiptId. Update the insertText helper
in the spec so it captures the sequence number before awaiting and uses that
saved value when building the receiptId, and apply the same fix to the other
matching mock block referenced by the spec so per-edit history sequencing
remains unique.
- Around line 643-656: The observed-window helper is computing offsets with
text.length, which is UTF-16 code units instead of bytes, so non-ASCII cases can
produce invalid adapter expectations. Update observedDocumentWindow (and the
related lines 694-699 logic) to derive startByte/endByte from byte-accurate text
encoding rather than string length, keeping byteOffsetAtLine as the source of
truth and using the encoded byte size for each visible line’s end offset.

In `@spec/workspace-reading-cache-guard.spec.mjs`:
- Around line 40-42: The current guard in workspace-reading-cache-guard.spec.mjs
only checks that workspaceModelWithTextAuthorityEditor appears somewhere in
runtimeSource, which is too weak to validate the intended rebuild path. Tighten
the assertion by matching a more specific pattern tied to the actual runtime
flow under test, such as the exact call site or surrounding identifiers used
when the cache read/rebuild logic executes, so unrelated imports or dead code
cannot satisfy it. Use the existing runtimeSource check in this spec and anchor
it to the concrete behavior exercised by the cache guard test.

In `@src/app/workspace/workspace-text-operation-sequencer.ts`:
- Around line 3-20: Replace the module-scoped textOperationQueues state in
sequenceWorkspaceTextOperation with an injected sequencer owned by the workspace
wiring. Move the WeakMap into a factory-created sequencer or dependency class,
then pass that sequencer into the command path that currently calls
sequenceWorkspaceTextOperation so lifecycle is managed by DI instead of ambient
globals. Keep the sequencing behavior the same, but make the session queue
storage live on the injected dependency rather than at module scope.
🪄 Autofix (Beta)

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: ASSERTIVE

Plan: Pro

Run ID: 2193fea8-4ac8-4473-9174-7511e8654d7e

📥 Commits

Reviewing files that changed from the base of the PR and between 4027fbf and 54b7a1d.

📒 Files selected for processing (12)
  • spec/workspace-app-echo-cutover.spec.mjs
  • spec/workspace-command-line.spec.mjs
  • spec/workspace-key-bindings.spec.mjs
  • spec/workspace-reading-cache-guard.spec.mjs
  • spec/workspace-text-cutover.spec.mjs
  • src/app/workspace/drawer.ts
  • src/app/workspace/workspace-save-key.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • src/app/workspace/workspace-text-reading-cache.ts
  • src/app/workspace/workspace-text-result-guards.ts
  • src/app/workspace/workspace-text-runtime-state.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: release-gate / jedit-echo
🧰 Additional context used
📓 Path-based instructions (3)
**

⚙️ CodeRabbit configuration file

**: # jedit Agent Contract

Git Safety

  • Never amend commits. Make a new commit.
  • Never rebase unless the user explicitly approves it after a concrete explanation.
  • Never force git operations.
  • Draft pull requests are allowed only for design-cycle kickoff or active cycle
    work. Convert them to ready before merge. If draft PRs are unavailable, use a
    normal PR with the work-in-progress label.

Quality Doctrine

These are hard repo rules, not suggestions:

  1. No any.
  2. No unknown.
  3. Hexagonal architecture, strict.
  4. Encoding and decoding happen only at the boundary, inside a port/adapter.
  5. One file equals one runtime truth.
  6. Runtime truth beats compile-time theater.
  7. No ad hoc string comparison in core logic. Prefer instanceof, numeric tags, symbols, or explicit runtime objects.
  8. No magic strings.
  9. No magic numbers.
  10. SOLID.
  11. DRY.
  12. DI.
  13. Red to green.
  14. Tests are spec.
  15. Agent-first.
  16. No file over 500 lines of code.

Structure

  • ARCHITECTURE.md is the canonical repo architecture doctrine.
  • docs/BEARING.md records the current execution gravity.
  • docs/method/process.md is the canonical cycle workflow.
  • docs/method/roadmap-planning.md defines release-gate, roadmap, slice, and
    proof policy for multi-cycle planning.
  • docs/design/TEMPLATE.md is the required template for full cycle designs.
  • docs/design/0034-design-cycle-template-and-lifecycle.md records the policy
    decision that made executable design docs official.
  • docs/design/0024-jedit-powered-by-echo-release-gate.md is the completed
    baseline plan for the first thirty slices proving jedit has an installed
    package/evidence path while Echo remains generic.
  • docs/design/0025-echo-application-hosting-pattern.md is the completed
    post-release-pressure ten-slice plan for proving the reusable Echo
    application-hosting pattern before applying it to Graft, Think, or other apps.
  • docs/design/0026-echo-hosting-hardening-first-twenty.md is th...

Files:

  • spec/workspace-key-bindings.spec.mjs
  • src/app/workspace/workspace-text-result-guards.ts
  • spec/workspace-reading-cache-guard.spec.mjs
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • spec/workspace-command-line.spec.mjs
  • src/app/workspace/drawer.ts
  • src/app/workspace/workspace-text-reading-cache.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/app/workspace/workspace-save-key.ts
  • spec/workspace-app-echo-cutover.spec.mjs
  • spec/workspace-text-cutover.spec.mjs
  • src/app/workspace/workspace-text-runtime-state.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: No any type usage in TypeScript code
No unknown type usage in TypeScript code
No TypeScript file over 500 lines of code
Make invalid states unrepresentable at runtime where possible
Prefer constructor or factory injection over ambient singletons for new code

Files:

  • src/app/workspace/workspace-text-result-guards.ts
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • src/app/workspace/drawer.ts
  • src/app/workspace/workspace-text-reading-cache.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/app/workspace/workspace-save-key.ts
  • src/app/workspace/workspace-text-runtime-state.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js}: No ad hoc string comparison in core logic; prefer instanceof, numeric tags, symbols, or explicit runtime objects
No magic strings in code
No magic numbers in code

Files:

  • src/app/workspace/workspace-text-result-guards.ts
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • src/app/workspace/drawer.ts
  • src/app/workspace/workspace-text-reading-cache.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/app/workspace/workspace-save-key.ts
  • src/app/workspace/workspace-text-runtime-state.ts
🔇 Additional comments (9)
src/app/workspace/drawer.ts (1)

9-9: LGTM!

Also applies to: 51-55, 91-93

src/app/workspace/workspace-text-reading-cache.ts (1)

3-4: LGTM!

Also applies to: 145-155, 193-210

src/app/workspace/workspace-text-result-guards.ts (1)

36-44: LGTM!

src/app/workspace/workspace-text-runtime-state.ts (1)

32-43: LGTM!

Also applies to: 178-186, 188-206, 332-334, 452-452

src/app/workspace/workspace-text-commands.ts (1)

5-8: LGTM!

Also applies to: 34-34, 164-206

src/app/workspace/workspace-save-key.ts (1)

1-1: LGTM!

Also applies to: 53-79

spec/workspace-text-cutover.spec.mjs (1)

1021-1021: LGTM!

Also applies to: 1063-1070

spec/workspace-command-line.spec.mjs (1)

729-729: LGTM!

Also applies to: 767-779

spec/workspace-key-bindings.spec.mjs (1)

130-179: LGTM!

Comment thread spec/workspace-app-echo-cutover.spec.mjs Outdated
Comment thread spec/workspace-app-echo-cutover.spec.mjs Outdated
Comment thread spec/workspace-reading-cache-guard.spec.mjs Outdated
Comment thread src/app/workspace/workspace-text-operation-sequencer.ts Outdated
@flyingrobots

Copy link
Copy Markdown
Owner Author

Code Lawyer Activity Summary

All actionable review threads have been addressed and resolved. Local full check is green on head da6d394.

# Source Severity Area Commit Outcome
1 PR thread P1 workspace-text-runtime-state.ts 4c52cad Preserved applied queued-edit cache/receipt before queued save settles.
2 PR review body P1 workspace-save-key.ts / sequencing 7399f16 Queued save/export now aborts after prior edit obstruction.
3 PR thread P2 workspace-text-commands.ts 34dfd2a Repeated same-buffer saves coalesce active export instead of racing host fingerprint.
4 PR thread P3 workspace-app-echo-cutover.spec.mjs 805ae23 Insert receipt sequence captured before await in async mocks.
5 PR thread P3 workspace-app-echo-cutover.spec.mjs 8f6f5f9 Observed-window fixture offsets are byte-accurate for UTF-8 text.
6 PR thread P4 workspace-reading-cache-guard.spec.mjs 099551b Cache guard now anchors to runtime rebuild call sites.
7 PR thread P2 workspace-text-operation-sequencer.ts da6d394 Sequencer state moved from module scope into an injected runtime-owned factory dependency.

Verification:

Command Result
node --test --test-concurrency=1 spec/workspace-text-operation-sequencer-guard.spec.mjs pass
JEDIT_DIST_PREBUILT=1 node --test --test-concurrency=1 spec/workspace-app-echo-cutover.spec.mjs pass
JEDIT_DIST_PREBUILT=1 node --test --test-concurrency=1 spec/workspace-text-cutover.spec.mjs pass
JEDIT_DIST_PREBUILT=1 node --test --test-concurrency=1 spec/workspace-text-boundaries.spec.mjs spec/workspace-command-line.spec.mjs pass
npm run check pass: 1063 tests, 1054 pass, 9 skipped; quality regressions none
git diff --check pass

Current gate state:

  • Review threads: zero unresolved in GraphQL query.
  • Local tree: clean at da6d394.
  • Remote CI: pending on the latest push at the time of this comment.
  • Review decision: GitHub still reports CHANGES_REQUESTED from the prior CodeRabbit review until re-review/status refresh.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 30, 2026
@flyingrobots

Copy link
Copy Markdown
Owner Author

Code Lawyer Self-Audit Finding

Cc @codex for second opinion.

# Severity Source File Lines Issue Required proof
1 P1 Self src/app/workspace/workspace-text-operation-sequencer.ts 72-81 Queued dependent edit operations can still execute after an earlier edit obstruction. The runtime later marks their results blocked, but Echo may already have admitted/mutated the dependent edit. Add a deterministic regression proving a second queued edit is not submitted after the first edit obstructs, then gate sequenceEdit behind prior obstruction state.

@flyingrobots

Copy link
Copy Markdown
Owner Author

Code Lawyer Recheck Activity Summary

Recheck completed on head 273bac0.

# Source Severity Area Commit Outcome
1 Self-audit P1 workspace-text-operation-sequencer.ts 273bac0 Queued dependent edit operations now stop after an earlier edit obstruction instead of submitting further mutations to Echo.

Verification:

Command Result
node --test --test-concurrency=1 spec/workspace-text-operation-sequencer.spec.mjs pass
JEDIT_DIST_PREBUILT=1 node --test --test-concurrency=1 spec/workspace-app-echo-cutover.spec.mjs pass
JEDIT_DIST_PREBUILT=1 node --test --test-concurrency=1 spec/workspace-text-cutover.spec.mjs pass
JEDIT_DIST_PREBUILT=1 node --test --test-concurrency=1 spec/workspace-text-boundaries.spec.mjs spec/workspace-command-line.spec.mjs spec/workspace-reading-cache-guard.spec.mjs spec/workspace-text-operation-sequencer-guard.spec.mjs pass
npm run check pass: 1064 tests, 1055 pass, 9 skipped; quality regressions none
git diff --check origin/main...HEAD pass

Current gate state:

  • Review threads: zero unresolved in GraphQL query.
  • Local tree: clean at 273bac0.
  • Remote Actions: green on the latest head.
  • CodeRabbit status: failing because prepaid credits are exhausted.
  • Review decision: reset after the new push; current-head CodeRabbit approval is not present.

@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: da6d394e58

ℹ️ 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 src/app/workspace/workspace-text-operation-sequencer.ts
Comment thread src/app/workspace/workspace-text-operation-sequencer.ts Outdated
Comment thread src/app/workspace/workspace-text-operation-sequencer.ts Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 30, 2026
@flyingrobots

Copy link
Copy Markdown
Owner Author

Code Lawyer Activity Summary

# Severity Source File Commit Outcome
1 P1 PR review src/app/workspace/workspace-text-operation-sequencer.ts 273bac0 Queued edits now stop before invoking Echo after a prior edit obstruction.
2 P1 PR review src/app/workspace/workspace-text-operation-sequencer.ts 4e0c5d4 Export coalescing now refuses stale active exports once a newer text operation has been queued.
3 P2 PR review src/app/workspace/workspace-text-operation-sequencer.ts f4074a0 Text obstruction state is now scoped by buffer, not just the shared production session.

Verification:

  • npm run check passed locally: 1066 tests, 1057 passed, 9 skipped; quality regressions none.
  • git diff --check origin/main...HEAD passed.
  • GitHub review threads: 0 unresolved.
  • CodeRabbit: approved.
  • GitHub Actions: still pending on head f4074a057663301ae330edff5a36bf1606f4f034 at the time of this summary.

@flyingrobots

Copy link
Copy Markdown
Owner Author

Code Lawyer Re-Audit Summary

No new actionable PR review threads or self-audit issues were found on head f4074a057663301ae330edff5a36bf1606f4f034.

Area Result
Lockdown Worktree clean before audit; origin fetched.
Review threads 0 unresolved in GraphQL review-thread query.
Self-audit git diff origin/main...HEAD reviewed; no new logic/race/SoC/style issues found beyond already-fixed queue/export/obstruction items.
Local verification npm run check passed: 1066 tests, 1057 passed, 9 skipped; quality regressions none.
Diff hygiene git diff --check origin/main...HEAD passed.
GitHub Actions Project checks green.
Residual status CodeRabbit status context still fails because prepaid credits are exhausted, while latest CodeRabbit review state is approved.

No commits were made in this re-audit pass.

@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: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/workspace/workspace-text-runtime-state.ts (1)

430-435: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore graft refresh after applied text edits.

Line 434 now only re-runs source highlighting. That removes the previous post-edit graft/projection refresh path, so an open or current-file Graft view will stay stale after text edits until some unrelated action refreshes it. Reintroduce the conditional graft refresh here instead of downgrading this path to highlight-only.

🤖 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 `@src/app/workspace/workspace-text-runtime-state.ts` around lines 430 - 435,
The refreshAfterEdit path currently only calls
beginEditorSourceHighlightRefresh, which drops the post-edit graft/projection
refresh and leaves open Graft views stale. Update refreshAfterEdit in
workspace-text-runtime-state to restore the conditional graft refresh logic
alongside the existing source highlight refresh, using the same
WorkspaceRuntimeDependencies/WorkspaceModel flow so current-file or open graft
state is refreshed after applied text edits.
♻️ Duplicate comments (1)
spec/workspace-reading-cache-guard.spec.mjs (1)

42-46: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

These regex guards are still not function-local.

[\s\S]*workspaceModelWithTextAuthorityEditor\( can still match a later occurrence elsewhere in runtimeSource, so editorAfterTextEdit or withTextAuthority can stop calling it and this test will still pass. Match the concrete call/return site inside each function body instead.

As per coding guidelines, "Tests are spec."

🤖 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 `@spec/workspace-reading-cache-guard.spec.mjs` around lines 42 - 46, The regex
guards in the spec are too broad and can still match a later
workspaceModelWithTextAuthorityEditor call outside the target function, so
tighten each assertion to the concrete call/return site within
editorAfterTextEdit and withTextAuthority. Update the patterns in
workspace-reading-cache-guard.spec.mjs so they anchor to the relevant function
bodies and only pass when those specific functions directly invoke or return
workspaceModelWithTextAuthorityEditor, rather than any later occurrence in
runtimeSource.

Source: Coding guidelines

🤖 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 `@spec/settings-drawer.spec.mjs`:
- Around line 50-56: The settings drawer spec now only checks rendered text, so
keep at least one cell-level style assertion to cover the focused row’s
selected-marker/row styling. In spec/settings-drawer.spec.mjs, retain or
reintroduce a surface-cell assertion alongside the text checks in the drawer
render test so the token/wiring regression can’t pass on glyph text alone.

In `@spec/settings-session.spec.mjs`:
- Around line 60-65: Update the settings focus movement test around
moveSettingsFocusIndex to also cover the rowCount <= 0 guard, not just wrapping
behavior. Add an assertion in the existing settings focus movement test (or a
nearby new test in the same spec) that exercises the zero-row case and verifies
the function returns the expected fallback index when rowCount is 0, so the
escape hatch stays covered.

In `@spec/workspace-app-echo-cutover.spec.mjs`:
- Around line 293-367: This spec file is growing past the 500-line limit, so
split the newly added queue/UTF-8 helper logic and/or the cutover cases into a
dedicated spec/helper module instead of keeping them in
workspace-app-echo-cutover.spec.mjs. Move the related test blocks around the
real workspace app path and any shared setup utilities into a separate file, and
update the existing spec to import or reference those helpers so the current
file stays focused and under the size cap.
- Around line 320-323: The `multiRangeEdit` stub is returning a nested
obstruction object instead of the obstruction payload itself, so it no longer
matches the `insertText` shape. Update `multiRangeEdit` in
`workspace-app-echo-cutover.spec.mjs` to return the direct result of
`productionTextObstruction('multi-range unsupported')` using the same payload
structure as the existing `insertText` stub, so the spec reflects the intended
obstruction response.
- Around line 353-366: The queued save result is being ignored in the spec, so
the follow-up model/commands from harness.runtime.update(...) are never
asserted. Update the workspace-app echo cutover test to capture and apply the
result of runtime.update(...) from saveMessage, then assert on the returned
model and any emitted commands after pendingInsert.resolve() and
applyWorkspaceMessage(...), using harness.runtime.update and harness.model so
the test verifies the queued save is not dropped.

In `@spec/workspace-text-cutover.spec.mjs`:
- Around line 1431-1438: The first coalesced save path is not being validated
for duplicate checkpoint emission. In the test around runtime.update, capture
the command list returned from the first update using firstMessage and
secondSave, then assert it is empty before proceeding to the second
runtime.update call. Keep the existing second assertion that checkpointCommands
has length 1 so the test covers both the no-op first export and the single
checkpoint on the second export.

In `@spec/workspace-text-operation-sequencer-guard.spec.mjs`:
- Around line 8-11: The current spec only blocks a top-level WeakMap
declaration, so it can miss module-scoped shared sequencer state created through
an object or helper. Update the test around
createWorkspaceTextOperationSequencer to assert that any WeakMap-backed state is
instantiated inside the factory body, not at module scope, by checking the
source for factory-local creation patterns rather than just a single top-level
spelling.

In `@src/adapters/workspace-production-text-session.ts`:
- Line 5: The adapter currently re-exports
createWorkspaceTextOperationSequencer, which makes
src/adapters/workspace-production-text-session.ts expose an unrelated runtime
factory. Remove that export from the adapter and update the call sites to import
createWorkspaceTextOperationSequencer directly from its own module, keeping the
adapter focused on the production session factory only.

In `@src/app/settings-session.ts`:
- Line 91: `KEY_Q` in `settings-session.ts` duplicates the close-key truth
already defined in `WorkspaceKeys.Q` from
`app/workspace/settings-key-bindings.ts`, which can drift from the drawer
hint/specs and tests. Remove the local `KEY_Q` constant and import/reuse the
shared close binding from the centralized key registry instead, updating any
references in `settings-session` to use that shared symbol so there is only one
runtime source of truth.

In `@src/app/workspace/workspace-text-operation-sequencer.ts`:
- Around line 10-20: The sequencer is using raw filePath/bufferId strings as its
identity, which leads to ad hoc string comparisons in the core logic. Update the
ActiveTextExportOperation and BlockedTextOperation types, plus the related logic
in workspace-text-operation-sequencer and the export-reuse check around the
matching/lookup path, to carry a dedicated runtime identity object or nominal id
instead of comparing strings directly. Move the string-based boundary data into
that runtime object immediately at the edge, and use that object consistently
throughout the sequencer.

In `@src/app/workspace/workspace-text-result-guards.ts`:
- Around line 69-79: The dependent edit blocker message in
dependentEditBlockedIssue should not synthesize a fake request id when
blockedByClientSeq is undefined. Update the message construction so it uses an
explicit unknown marker or omits the request suffix entirely when the sequence
is absent, instead of defaulting to request:0. Keep the change localized to
dependentEditBlockedIssue and preserve the existing prefix, filePath, and
RuntimeIssue shape.

In `@src/ui/settings-drawer.ts`:
- Around line 74-90: The fallback in settings-drawer rendering returns the
already-shrunk body from renderSettingsDrawerBody, so when drawer() yields no
overlay.surface the caller gets a smaller, misaligned surface. Update the logic
in the settings-drawer function so the fallback path produces a full-size
surface matching options.width and options.height, while still using the
shrunken content only for the inner drawer body. Use the
renderSettingsDrawerBody and drawer symbols to locate the return path and keep
the fallback consistent with the outer overlay dimensions.

---

Outside diff comments:
In `@src/app/workspace/workspace-text-runtime-state.ts`:
- Around line 430-435: The refreshAfterEdit path currently only calls
beginEditorSourceHighlightRefresh, which drops the post-edit graft/projection
refresh and leaves open Graft views stale. Update refreshAfterEdit in
workspace-text-runtime-state to restore the conditional graft refresh logic
alongside the existing source highlight refresh, using the same
WorkspaceRuntimeDependencies/WorkspaceModel flow so current-file or open graft
state is refreshed after applied text edits.

---

Duplicate comments:
In `@spec/workspace-reading-cache-guard.spec.mjs`:
- Around line 42-46: The regex guards in the spec are too broad and can still
match a later workspaceModelWithTextAuthorityEditor call outside the target
function, so tighten each assertion to the concrete call/return site within
editorAfterTextEdit and withTextAuthority. Update the patterns in
workspace-reading-cache-guard.spec.mjs so they anchor to the relevant function
bodies and only pass when those specific functions directly invoke or return
workspaceModelWithTextAuthorityEditor, rather than any later occurrence in
runtimeSource.
🪄 Autofix (Beta)

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: ASSERTIVE

Plan: Pro

Run ID: a9a70d08-62f4-4f4b-bfa7-29a3f141c50d

📥 Commits

Reviewing files that changed from the base of the PR and between 54b7a1d and 7b7f334.

📒 Files selected for processing (30)
  • spec/settings-drawer.spec.mjs
  • spec/settings-session.spec.mjs
  • spec/workspace-app-echo-cutover.spec.mjs
  • spec/workspace-echo-app-harness.mjs
  • spec/workspace-helpers.mjs
  • spec/workspace-lifecycle.spec.mjs
  • spec/workspace-reading-cache-guard.spec.mjs
  • spec/workspace-text-boundaries.spec.mjs
  • spec/workspace-text-cutover.spec.mjs
  • spec/workspace-text-operation-sequencer-guard.spec.mjs
  • spec/workspace-text-operation-sequencer.spec.mjs
  • spec/workspace-title-screen.spec.mjs
  • src/adapters/workspace-app.ts
  • src/adapters/workspace-production-text-session.ts
  • src/app/settings-session.ts
  • src/app/workspace/editor-session.ts
  • src/app/workspace/editor-trust-preflight-runtime-fixtures.ts
  • src/app/workspace/focused-pane-key-bindings.ts
  • src/app/workspace/key-binding-context.ts
  • src/app/workspace/runtime.ts
  • src/app/workspace/settings-key-bindings.ts
  • src/app/workspace/viewer-key.ts
  • src/app/workspace/viewer.ts
  • src/app/workspace/workspace-runtime-dependencies.ts
  • src/app/workspace/workspace-save-key.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • src/app/workspace/workspace-text-result-guards.ts
  • src/app/workspace/workspace-text-runtime-state.ts
  • src/ui/settings-drawer.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: test / echo-authority
  • GitHub Check: test / cycle-proofs
  • GitHub Check: test / contract-api
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: No any type usage in TypeScript code
No unknown type usage in TypeScript code
No TypeScript file over 500 lines of code
Make invalid states unrepresentable at runtime where possible
Prefer constructor or factory injection over ambient singletons for new code

Files:

  • src/app/workspace/workspace-runtime-dependencies.ts
  • src/app/workspace/runtime.ts
  • src/app/workspace/settings-key-bindings.ts
  • src/adapters/workspace-production-text-session.ts
  • src/app/workspace/key-binding-context.ts
  • src/app/workspace/focused-pane-key-bindings.ts
  • src/app/workspace/editor-session.ts
  • src/app/workspace/editor-trust-preflight-runtime-fixtures.ts
  • src/adapters/workspace-app.ts
  • src/app/workspace/viewer.ts
  • src/ui/settings-drawer.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • src/app/workspace/workspace-save-key.ts
  • src/app/settings-session.ts
  • src/app/workspace/workspace-text-result-guards.ts
  • src/app/workspace/viewer-key.ts
  • src/app/workspace/workspace-text-runtime-state.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js}: No ad hoc string comparison in core logic; prefer instanceof, numeric tags, symbols, or explicit runtime objects
No magic strings in code
No magic numbers in code

Files:

  • src/app/workspace/workspace-runtime-dependencies.ts
  • src/app/workspace/runtime.ts
  • src/app/workspace/settings-key-bindings.ts
  • src/adapters/workspace-production-text-session.ts
  • src/app/workspace/key-binding-context.ts
  • src/app/workspace/focused-pane-key-bindings.ts
  • src/app/workspace/editor-session.ts
  • src/app/workspace/editor-trust-preflight-runtime-fixtures.ts
  • src/adapters/workspace-app.ts
  • src/app/workspace/viewer.ts
  • src/ui/settings-drawer.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • src/app/workspace/workspace-save-key.ts
  • src/app/settings-session.ts
  • src/app/workspace/workspace-text-result-guards.ts
  • src/app/workspace/viewer-key.ts
  • src/app/workspace/workspace-text-runtime-state.ts
**

⚙️ CodeRabbit configuration file

**: # jedit Agent Contract

Git Safety

  • Never amend commits. Make a new commit.
  • Never rebase unless the user explicitly approves it after a concrete explanation.
  • Never force git operations.
  • Draft pull requests are allowed only for design-cycle kickoff or active cycle
    work. Convert them to ready before merge. If draft PRs are unavailable, use a
    normal PR with the work-in-progress label.

Quality Doctrine

These are hard repo rules, not suggestions:

  1. No any.
  2. No unknown.
  3. Hexagonal architecture, strict.
  4. Encoding and decoding happen only at the boundary, inside a port/adapter.
  5. One file equals one runtime truth.
  6. Runtime truth beats compile-time theater.
  7. No ad hoc string comparison in core logic. Prefer instanceof, numeric tags, symbols, or explicit runtime objects.
  8. No magic strings.
  9. No magic numbers.
  10. SOLID.
  11. DRY.
  12. DI.
  13. Red to green.
  14. Tests are spec.
  15. Agent-first.
  16. No file over 500 lines of code.

Structure

  • ARCHITECTURE.md is the canonical repo architecture doctrine.
  • docs/BEARING.md records the current execution gravity.
  • docs/method/process.md is the canonical cycle workflow.
  • docs/method/roadmap-planning.md defines release-gate, roadmap, slice, and
    proof policy for multi-cycle planning.
  • docs/design/TEMPLATE.md is the required template for full cycle designs.
  • docs/design/0034-design-cycle-template-and-lifecycle.md records the policy
    decision that made executable design docs official.
  • docs/design/0024-jedit-powered-by-echo-release-gate.md is the completed
    baseline plan for the first thirty slices proving jedit has an installed
    package/evidence path while Echo remains generic.
  • docs/design/0025-echo-application-hosting-pattern.md is the completed
    post-release-pressure ten-slice plan for proving the reusable Echo
    application-hosting pattern before applying it to Graft, Think, or other apps.
  • docs/design/0026-echo-hosting-hardening-first-twenty.md is th...

Files:

  • src/app/workspace/workspace-runtime-dependencies.ts
  • src/app/workspace/runtime.ts
  • spec/workspace-text-operation-sequencer-guard.spec.mjs
  • src/app/workspace/settings-key-bindings.ts
  • src/adapters/workspace-production-text-session.ts
  • spec/settings-drawer.spec.mjs
  • spec/workspace-title-screen.spec.mjs
  • src/app/workspace/key-binding-context.ts
  • src/app/workspace/focused-pane-key-bindings.ts
  • spec/workspace-reading-cache-guard.spec.mjs
  • spec/workspace-lifecycle.spec.mjs
  • src/app/workspace/editor-session.ts
  • src/app/workspace/editor-trust-preflight-runtime-fixtures.ts
  • src/adapters/workspace-app.ts
  • spec/workspace-text-boundaries.spec.mjs
  • spec/workspace-text-operation-sequencer.spec.mjs
  • src/app/workspace/viewer.ts
  • spec/workspace-echo-app-harness.mjs
  • spec/settings-session.spec.mjs
  • src/ui/settings-drawer.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • spec/workspace-helpers.mjs
  • src/app/workspace/workspace-save-key.ts
  • src/app/settings-session.ts
  • src/app/workspace/workspace-text-result-guards.ts
  • src/app/workspace/viewer-key.ts
  • src/app/workspace/workspace-text-runtime-state.ts
  • spec/workspace-app-echo-cutover.spec.mjs
  • spec/workspace-text-cutover.spec.mjs
🔇 Additional comments (16)
src/app/workspace/workspace-text-operation-sequencer.ts (1)

56-68: LGTM!

src/app/workspace/workspace-text-commands.ts (1)

34-34: LGTM!

Also applies to: 73-73, 108-108, 120-120, 168-173, 186-191, 204-209

src/app/workspace/workspace-runtime-dependencies.ts (1)

15-15: LGTM!

Also applies to: 34-34

src/app/workspace/editor-trust-preflight-runtime-fixtures.ts (1)

39-39: LGTM!

Also applies to: 139-151

src/app/workspace/key-binding-context.ts (1)

11-11: LGTM!

Also applies to: 26-26

src/app/workspace/runtime.ts (1)

412-428: LGTM!

src/app/workspace/focused-pane-key-bindings.ts (1)

37-43: LGTM!

src/app/workspace/viewer-key.ts (1)

4-4: LGTM!

Also applies to: 50-107, 127-149, 235-243, 262-289, 346-420

src/app/workspace/editor-session.ts (1)

161-172: LGTM!

src/app/workspace/workspace-save-key.ts (1)

53-79: LGTM!

spec/workspace-echo-app-harness.mjs (1)

10-18: LGTM!

Also applies to: 57-57

spec/workspace-text-operation-sequencer.spec.mjs (1)

5-163: LGTM!

spec/workspace-helpers.mjs (1)

106-118: LGTM!

Also applies to: 285-319

spec/workspace-text-boundaries.spec.mjs (1)

4-8: LGTM!

Also applies to: 193-193, 245-245

spec/workspace-text-cutover.spec.mjs (1)

5-5: LGTM!

Also applies to: 344-344, 419-419, 452-452, 514-521, 583-590, 645-645, 709-709, 850-942, 1234-1234

spec/workspace-title-screen.spec.mjs (1)

632-664: LGTM!

Comment thread spec/settings-drawer.spec.mjs
Comment thread spec/settings-session.spec.mjs
Comment thread spec/workspace-app-echo-cutover.spec.mjs Outdated
Comment thread spec/workspace-app-echo-cutover.spec.mjs Outdated
Comment thread spec/workspace-app-echo-cutover.spec.mjs Outdated
Comment thread src/adapters/workspace-production-text-session.ts Outdated
Comment thread src/app/settings-session.ts Outdated
Comment thread src/app/workspace/workspace-text-operation-sequencer.ts
Comment thread src/app/workspace/workspace-text-result-guards.ts
Comment thread src/ui/settings-drawer.ts
@flyingrobots

Copy link
Copy Markdown
Owner Author

Code Lawyer Activity Summary\n\n| # | Severity | Source | File | Commit | Outcome |\n| --- | --- | --- | --- | --- | --- |\n| 1 | P2 | PR | spec/workspace-app-echo-cutover.spec.mjs | 9b630b3 | Applied queued save result and asserted no follow-up commands. |\n| 2 | P3 | PR | spec/workspace-app-echo-cutover.spec.mjs | 2caf872 | Fixed malformed multi-range obstruction stub. |\n| 3 | P3 | PR | spec/workspace-text-cutover.spec.mjs | 793436c | Asserted ignored coalesced export emits no checkpoint. |\n| 4 | P3 | PR | src/app/workspace/workspace-text-result-guards.ts | e82e595 | Replaced fake request:0 with request:unknown and added regression. |\n| 5 | P3 | PR | src/ui/settings-drawer.ts | 8c04f7f | Added full-size settings drawer fallback surface. |\n| 6 | P4 | PR | spec/settings-drawer.spec.mjs | f7b1dfe | Restored cell-level selected-row token assertion. |\n| 7 | P4 | PR | spec/settings-session.spec.mjs | fd23b38 | Added zero-row focus movement coverage. |\n| 8 | P4 | PR | spec/workspace-text-operation-sequencer-guard.spec.mjs | d8237ff | Tightened sequencer factory-ownership guard. |\n| 9 | P2 | PR | src/adapters/workspace-production-text-session.ts | ec1f05c | Removed sequencer factory re-export from production-session adapter. |\n| 10 | P3 | PR | src/app/settings-session.ts | 7a9779e | Centralized settings close key in shared keybinding metadata. |\n| 11 | P2 | PR | src/app/workspace/workspace-text-operation-sequencer.ts | f8aaaee | Replaced raw filePath/bufferId API with WorkspaceTextOperationTarget. |\n| 12 | P3 | Self | spec/workspace-app-echo-cutover.spec.mjs | 3dd2556 | Fixed remaining nested obstruction stubs found during split audit. |\n| 13 | P2 | PR | spec/workspace-app-echo-cutover.spec.mjs | 71a1e7a | Split rapid typing/save sequencing into a dedicated spec and helper module. |\n| 14 | P3 | Self | src/adapters/workspace-app.ts | 9be42f6 | Fixed import-budget regression with an adapter dependency factory. |\n\nLocal verification after all fixes:\n\n- npm run build passed.\n- npm run --silent quality passed with no regressions.\n- npm run check passed: 1070 tests, 1061 pass, 9 skipped.\n- git diff --check passed.\n\nPost-push status at the time of this comment:\n\n- Current unresolved review threads: 0.\n- CI: pending.\n- CodeRabbit: blocked by prepaid credits exhausted, not an actionable review thread.\n

@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: 7b7f334c5b

ℹ️ 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 spec/workspace-app-echo-cutover.spec.mjs Outdated
Comment thread src/app/workspace/workspace-text-runtime-state.ts Outdated
Comment thread src/app/workspace/workspace-text-runtime-state.ts Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 30, 2026
@flyingrobots

Copy link
Copy Markdown
Owner Author

Code Lawyer Activity Summary

Current head: 7c146cc

# Severity Source File Commit Outcome
1 P1 PR spec/workspace-app-echo-typing-sequencing.spec.mjs 9b630b3 Tightened queued-save obstruction assertion.
2 P1 PR spec/workspace-app-echo-cutover.spec.mjs 2caf872 Fixed malformed multi-range obstruction stub.
3 P2 PR spec/workspace-text-cutover.spec.mjs 793436c Asserted ignored export emits no checkpoint.
4 P1 PR src/app/workspace/workspace-text-operation-sequencer.ts e82e595 Fixed unknown dependent edit blocker label.
5 P3 PR src/ui/settings-drawer.ts 8c04f7f Fixed settings drawer fallback sizing.
6 P3 PR spec/settings-drawer.spec.mjs f7b1dfe Asserted selected settings row token.
7 P3 PR spec/settings-session.spec.mjs fd23b38 Covered empty settings focus movement.
8 P1 PR src/app/workspace/workspace-text-operation-sequencer.ts d8237ff Tightened sequencer ownership guard.
9 P2 PR src/app/workspace/workspace-text-commands.ts ec1f05c Fixed sequencer import boundary.
10 P3 PR src/app/workspace/settings-key-bindings.ts 7a9779e Centralized settings close key.
11 P1 PR src/app/workspace/workspace-text-commands.ts f8aaaee Used text operation target identity.
12 P2 Self spec/workspace-app-echo-cutover.spec.mjs 3dd2556 Fixed remaining obstruction stub shapes.
13 P3 Self spec/workspace-app-echo-typing-sequencing.spec.mjs 71a1e7a Split Echo app typing sequencing specs.
14 P2 Self spec/workspace-app-echo-typing-sequencing.spec.mjs 9be42f6 Fixed workspace app import budget.
15 P2 PR spec/workspace-echo-app-harness.mjs b87ef03 Fixed Echo harness multi-range obstruction.
16 P1 PR src/app/workspace/workspace-text-runtime-state.ts d97389f Admitted final queued edit before export obstruction.
17 P1 PR src/app/workspace/workspace-text-runtime-state.ts b24815f Refreshed highlighting after intermediate edits.
18 P3 Self src/app/workspace/workspace-text-runtime-state.ts 7c146cc Kept intermediate edit helper inside quality budget.

Verification:

Gate Result
Unresolved current review threads 0
Local npm run build pass
Local npm run --silent quality pass, regressions none
Local npm run check pass: 1072 tests, 1063 pass, 9 skipped
Local git diff --check origin/main...HEAD pass
GitHub Actions pass: plan, quality, static/build, release-gate, check, all test shards
CodeRabbit status context fail: prepaid credits exhausted
Merge gate locked by stale CodeRabbit CHANGES_REQUESTED reviews and no current approvals

Self-audit note: the full diff was captured locally at /tmp/jedit-pr198-code-lawyer-full.diff. The nondeterminism scan only matched injected default dependencies in src/adapters/workspace-app.ts; no uncontrolled random/time usage was introduced in the text sequencing path.

Cc: @codex

@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

Testing

  • ✅ npm run --silent quality
  • ⚠️ npm run check — failed during scripts/gen-structural-history-wesley.mjs because Cargo could not download https://index.crates.io/config.json through the environment proxy (CONNECT tunnel failed, response 403).
  • ✅ git diff --check

██████████ 100% (11/11 slices)
WIP: WF-0108 Editor Trust Gate preflight

🙈 OFF-ROADMAP

⎇ work +?/-?
🚫 none

View task →

@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: 6

🤖 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 `@scenes/bunny.jedit-scene`:
- Around line 3-4: Update the bunny scene camera setup so the X component of
camera.target in bunny.jedit-scene is aligned with the bunny mesh footprint
center instead of staying at -1.05, or increase
MAX_FACE_TARGET_CENTERLINE_OFFSET in title-scene-neon-dispersion.spec.mjs if the
wider offset is intentional. Use the camera.target values in the scene file and
the MAX_FACE_TARGET_CENTERLINE_OFFSET constant in the spec to keep the target
within the accepted centerline tolerance.

In `@spec/workspace-app-echo-typing-sequencing.spec.mjs`:
- Around line 79-82: The sequencing specs are coupled to a fixed number of
microtasks by the double Promise.resolve() waits, which makes them brittle;
update the assertions in the echo typing tests to wait for the observable state
instead. In the affected tests around pendingInserts and calls.insert, replace
the hardcoded microtask hops with a condition-based wait until the expected
in-flight mutation count is reached, using the existing sequencer/echo test
helpers so the assertions stay stable if internal await depth changes.

In `@spec/workspace-echo-test-utils.mjs`:
- Around line 46-48: The echoTextDocument.insert helper is still treating
startByte as a JavaScript string index, so multibyte content will insert at the
wrong location. Update the insert method in echoTextDocument to convert the byte
offset to a code-unit position before slicing, or change the backing
representation to bytes throughout the helper. Keep the fix localized to the
insert implementation and any related position handling so the Echo test utility
matches real runtime behavior.
- Around line 104-107: `applyWorkspaceMessage` is discarding the command queue
returned by `runtime.update`, which hides follow-up behavior from tests. Update
this helper in `spec/workspace-echo-test-utils.mjs` to preserve the commands
from `harness.runtime.update(message, harness.model)` and either return them to
the caller or immediately execute them with `harness.runAll(commands)`, while
still calling `harness.setModel(nextModel)` with the updated model.

In `@spec/workspace-text-cutover.spec.mjs`:
- Around line 943-1003: The test only applies
WorkspaceMessageTypes.TextEditResult and never starts the save it is meant to
race against, so it does not verify highlight ordering. Update the spec in
workspace-text-cutover.spec.mjs to first queue the save/export path on the
runtime, then dispatch TextEditResult and assert sourceHighlighter.highlight in
createWorkspaceRuntime runs before the queued save settles. Use the existing
symbols WorkspaceMessageTypes.TextEditResult, createWorkspaceRuntime, and
sourceHighlighter.highlight to keep the scenario tied to the real async
ordering.
- Around line 943-1003: The new TextEditResult sequencing/highlighting test
belongs in its own spec module instead of extending
spec/workspace-text-cutover.spec.mjs. Move the test case that exercises
createWorkspaceRuntime, TextEditResult, and workspaceTextAuthority into a
dedicated spec file with related coverage so this file stops growing beyond the
repo limit. Keep the existing behavior and assertions intact, but relocate the
new case and any similar save/highlighting sequencing coverage to a separate
runtime-focused spec.
🪄 Autofix (Beta)

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: ASSERTIVE

Plan: Pro

Run ID: e2c1745b-35fb-440c-b3db-9eaa61777a81

📥 Commits

Reviewing files that changed from the base of the PR and between 7b7f334 and 4ee060c.

📒 Files selected for processing (23)
  • scenes/bunny.jedit-scene
  • spec/settings-drawer.spec.mjs
  • spec/settings-session.spec.mjs
  • spec/title-scene-neon-dispersion.spec.mjs
  • spec/workspace-app-echo-cutover.spec.mjs
  • spec/workspace-app-echo-typing-sequencing.spec.mjs
  • spec/workspace-echo-app-harness.mjs
  • spec/workspace-echo-test-utils.mjs
  • spec/workspace-helpers.mjs
  • spec/workspace-text-cutover.spec.mjs
  • spec/workspace-text-operation-sequencer-guard.spec.mjs
  • spec/workspace-text-operation-sequencer.spec.mjs
  • spec/workspace-text-result-guards.spec.mjs
  • src/adapters/workspace-app.ts
  • src/adapters/workspace-production-text-dependencies.ts
  • src/app/keybindings.ts
  • src/app/settings-session.ts
  • src/app/workspace/settings-key-bindings.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • src/app/workspace/workspace-text-result-guards.ts
  • src/app/workspace/workspace-text-runtime-state.ts
  • src/ui/settings-drawer.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: test / contract-api
  • GitHub Check: test / echo-authority
  • GitHub Check: test / cycle-proofs
  • GitHub Check: release-gate / jedit-echo
🧰 Additional context used
📓 Path-based instructions (3)
**

⚙️ CodeRabbit configuration file

**: # jedit Agent Contract

Git Safety

  • Never amend commits. Make a new commit.
  • Never rebase unless the user explicitly approves it after a concrete explanation.
  • Never force git operations.
  • Draft pull requests are allowed only for design-cycle kickoff or active cycle
    work. Convert them to ready before merge. If draft PRs are unavailable, use a
    normal PR with the work-in-progress label.

Quality Doctrine

These are hard repo rules, not suggestions:

  1. No any.
  2. No unknown.
  3. Hexagonal architecture, strict.
  4. Encoding and decoding happen only at the boundary, inside a port/adapter.
  5. One file equals one runtime truth.
  6. Runtime truth beats compile-time theater.
  7. No ad hoc string comparison in core logic. Prefer instanceof, numeric tags, symbols, or explicit runtime objects.
  8. No magic strings.
  9. No magic numbers.
  10. SOLID.
  11. DRY.
  12. DI.
  13. Red to green.
  14. Tests are spec.
  15. Agent-first.
  16. No file over 500 lines of code.

Structure

  • ARCHITECTURE.md is the canonical repo architecture doctrine.
  • docs/BEARING.md records the current execution gravity.
  • docs/method/process.md is the canonical cycle workflow.
  • docs/method/roadmap-planning.md defines release-gate, roadmap, slice, and
    proof policy for multi-cycle planning.
  • docs/design/TEMPLATE.md is the required template for full cycle designs.
  • docs/design/0034-design-cycle-template-and-lifecycle.md records the policy
    decision that made executable design docs official.
  • docs/design/0024-jedit-powered-by-echo-release-gate.md is the completed
    baseline plan for the first thirty slices proving jedit has an installed
    package/evidence path while Echo remains generic.
  • docs/design/0025-echo-application-hosting-pattern.md is the completed
    post-release-pressure ten-slice plan for proving the reusable Echo
    application-hosting pattern before applying it to Graft, Think, or other apps.
  • docs/design/0026-echo-hosting-hardening-first-twenty.md is th...

Files:

  • spec/workspace-text-result-guards.spec.mjs
  • scenes/bunny.jedit-scene
  • src/adapters/workspace-production-text-dependencies.ts
  • spec/workspace-text-operation-sequencer.spec.mjs
  • spec/workspace-text-operation-sequencer-guard.spec.mjs
  • src/app/workspace/settings-key-bindings.ts
  • spec/workspace-app-echo-typing-sequencing.spec.mjs
  • src/app/keybindings.ts
  • spec/workspace-helpers.mjs
  • src/adapters/workspace-app.ts
  • spec/settings-drawer.spec.mjs
  • spec/workspace-echo-test-utils.mjs
  • spec/workspace-echo-app-harness.mjs
  • spec/title-scene-neon-dispersion.spec.mjs
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • src/app/settings-session.ts
  • spec/settings-session.spec.mjs
  • src/app/workspace/workspace-text-commands.ts
  • src/ui/settings-drawer.ts
  • src/app/workspace/workspace-text-result-guards.ts
  • spec/workspace-app-echo-cutover.spec.mjs
  • spec/workspace-text-cutover.spec.mjs
  • src/app/workspace/workspace-text-runtime-state.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: No any type usage in TypeScript code
No unknown type usage in TypeScript code
No TypeScript file over 500 lines of code
Make invalid states unrepresentable at runtime where possible
Prefer constructor or factory injection over ambient singletons for new code

Files:

  • src/adapters/workspace-production-text-dependencies.ts
  • src/app/workspace/settings-key-bindings.ts
  • src/app/keybindings.ts
  • src/adapters/workspace-app.ts
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • src/app/settings-session.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/ui/settings-drawer.ts
  • src/app/workspace/workspace-text-result-guards.ts
  • src/app/workspace/workspace-text-runtime-state.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js}: No ad hoc string comparison in core logic; prefer instanceof, numeric tags, symbols, or explicit runtime objects
No magic strings in code
No magic numbers in code

Files:

  • src/adapters/workspace-production-text-dependencies.ts
  • src/app/workspace/settings-key-bindings.ts
  • src/app/keybindings.ts
  • src/adapters/workspace-app.ts
  • src/app/workspace/workspace-text-operation-sequencer.ts
  • src/app/settings-session.ts
  • src/app/workspace/workspace-text-commands.ts
  • src/ui/settings-drawer.ts
  • src/app/workspace/workspace-text-result-guards.ts
  • src/app/workspace/workspace-text-runtime-state.ts
🔇 Additional comments (22)
src/ui/settings-drawer.ts (2)

91-99: 📐 Maintainability & Code Quality

Fallback now returns a full-size surface.

Correctly addresses the prior finding: renderSettingsDrawerFallback builds a options.width×options.height surface, fills it with the drawer background, and blits the shrunk content at an offset instead of returning the undersized content directly.


73-90: LGTM!

src/app/keybindings.ts (2)

26-26: 📐 Maintainability & Code Quality

Good: single source of truth for the close key.

Centralizing 'q' behind JEDIT_SETTINGS_CLOSE_KEY/_LABEL and having both settings-session.ts and settings-key-bindings.ts import from here resolves the earlier duplicate-constant finding.

Also applies to: 36-37


36-37: 📐 Maintainability & Code Quality

Keep settings-close on a single source of truth
Route every settings-close consumer through JEDIT_SETTINGS_CLOSE_KEY; do not leave a separate q path anywhere else.

src/app/settings-session.ts (2)

4-4: 📐 Maintainability & Code Quality

Duplicate-truth issue resolved.

Importing JEDIT_SETTINGS_CLOSE_KEY from keybindings.js instead of a local KEY_Q fixes the previously flagged runtime-truth duplication.

Also applies to: 131-131


116-118: LGTM!

spec/settings-drawer.spec.mjs (2)

50-60: 📐 Maintainability & Code Quality

Cell-level assertion restored.

Keeps the selectedMarker char/fg/bg checks alongside the text assertions, addressing the earlier "text-only assertions could mask a styling regression" concern.


87-111: LGTM! Good regression coverage for the fallback surface-size fix, including tiny dimensions.

spec/settings-session.spec.mjs (2)

69-71: 📐 Maintainability & Code Quality

Zero-row guard now covered.

Adds the rowCount <= 0 boundary assertions for moveSettingsFocusIndex, resolving the earlier coverage gap.


8-20: LGTM!

Also applies to: 73-121

src/app/workspace/settings-key-bindings.ts (1)

44-58: LGTM!

spec/title-scene-neon-dispersion.spec.mjs (5)

237-249: Chrome-sphere check is well scoped; minor stray blank line.

Line 243 has a stray blank line inside the function body — purely cosmetic, not blocking.


23-32: LGTM!


58-65: 🎯 Functional Correctness

Target-centerline assertion depends on unverified footprint-center semantics.

This assertion is the counterpart to the camera-target concern raised in scenes/bunny.jedit-scene (lines 3-4): if titleSceneObjectFootprintCenter doesn't return an x near -1.05 for the unpositioned bunny, this check fails. Flagging here since it's the actual assertion site.


87-87: LGTM!


136-136: LGTM!

Also applies to: 151-151

scenes/bunny.jedit-scene (1)

68-103: LGTM!

src/app/workspace/workspace-text-operation-sequencer.ts (1)

51-54: This still reduces target identity to raw string equality.

WorkspaceTextOperationTarget is still just a { filePath, bufferId } bag, and sameWorkspaceTextOperationTarget() still drives reuse off left.bufferId === right.bufferId. That keeps the core matching logic stringly in exactly the spot previously called out. As per coding guidelines, "No ad hoc string comparison in core logic. Prefer instanceof, numeric tags, symbols, or explicit runtime objects" and "Move stringly external data into runtime objects at the boundary immediately."

Also applies to: 224-229

Source: Coding guidelines

spec/workspace-text-operation-sequencer-guard.spec.mjs (1)

16-23: The negative scan still misses module-owned state after the factory.

moduleScopeBeforeFactory only checks text before createWorkspaceTextOperationSequencer(). A later top-level new WeakMap() or helper-created state would still satisfy this test, so it still doesn't prove the sequencer is factory-owned across the whole module.

spec/workspace-echo-app-harness.mjs (1)

10-19: LGTM!

Also applies to: 57-57, 161-161

spec/workspace-helpers.mjs (1)

112-117: LGTM!

spec/workspace-app-echo-cutover.spec.mjs (1)

4-11: LGTM!

Also applies to: 408-413

Comment thread scenes/bunny.jedit-scene Outdated
Comment thread spec/workspace-app-echo-typing-sequencing.spec.mjs Outdated
Comment thread spec/workspace-echo-test-utils.mjs
Comment thread spec/workspace-echo-test-utils.mjs
Comment thread spec/workspace-text-cutover.spec.mjs Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 1, 2026

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/adapters/title-scene-environment-decoder.ts (1)

129-138: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Decoder silently allows both direction and orbit on the same light with no invariant enforced.

decodeLight merges both decodeLightDirection and decodeLightOrbit unconditionally. If a scene author sets both direction and orbit, the decoder accepts it silently, and only titleSceneLightDirectionAt (in a different file) resolves the ambiguity by giving orbit priority. Nothing here makes that precedence discoverable or rejects the ambiguous authored state at the boundary where validation is supposed to happen.

Consider rejecting (or at least warning/asserting) the combination at decode time so the invalid/ambiguous state can't be represented past the boundary.

As per coding guidelines, "Make invalid states unrepresentable at runtime where possible."

🤖 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 `@src/adapters/title-scene-environment-decoder.ts` around lines 129 - 138,
decodeLight currently merges both decodeLightDirection and decodeLightOrbit
without enforcing the invariant that a light cannot author both direction and
orbit, so the ambiguous state slips through the decoder boundary. Update
decodeLight in title-scene-environment-decoder.ts to validate the decoded light
object before returning it: detect when both direction and orbit are present and
either reject the input with a decode error or emit a clear warning/assertion
and prevent both from coexisting. Keep the rule centralized here so downstream
logic like titleSceneLightDirectionAt does not need to resolve invalid authored
state.

Source: Coding guidelines

🤖 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 `@spec/title-scene-render.spec.mjs`:
- Around line 264-295: The title scene orbit-light test only verifies diffuse
passthrough, leaving ambient, specularStrength, rimStrength, and direction
untested. Update the test around titleSceneDayNightEnvironmentAtHour and
titleSceneLightDirectionAt to assert those orbit light fields are preserved or
correctly derived for orbit-enabled environments, so the coverage matches the
behavior in titleSceneDayNightLight and dayNightLightAmbient.

In `@src/app/workspace/title-mesh-materials.ts`:
- Around line 15-18: The title mesh matching logic has regressed to ad hoc label
string comparison, so update `isTitleMeshMaterialObject` and
`applyTitleMeshMaterial` to use a dedicated runtime tag/boolean or object-type
check instead of raw `object.label` strings. Restore a non-string guard for
mesh-kind or decode an explicit field like `isMaterialTarget` at the adapter
boundary, and keep `TITLE_MESH_MATERIAL_OBJECT_LABELS` out of core matching
logic.

In `@src/ui/title-scene-environment.ts`:
- Around line 257-264: The `dayNightLightAmbient` helper has an inconsistent
orbit fallback compared with `dayNightLightDiffuse`, `dayNightLightSpecular`,
and `dayNightLightRim`. Update `dayNightLightAmbient` so that when `light.orbit`
is present but `light.ambient` is missing, it uses the same day-side default
pattern as the sibling helpers rather than `NIGHT_AMBIENT`. Keep the change
localized to the `dayNightLightAmbient` function in `title-scene-environment`
and preserve the existing non-orbit interpolation behavior.
- Around line 257-295: The four helpers dayNightLightAmbient,
dayNightLightDiffuse, dayNightLightSpecular, and dayNightLightRim duplicate the
same orbit-vs-mix pattern with only the property, default, and mix behavior
changing. Refactor them in src/ui/title-scene-environment.ts by introducing one
shared parameterized helper that handles the light?.orbit null check and returns
either the authored value or mixScalar result, then have each of the four
functions delegate to it with the appropriate SceneLight field and constants.
Ensure the helper preserves the existing ambient, diffuse, specularStrength, and
rimStrength fallback behavior.
- Around line 247-255: The SceneLight builder in titleSceneEnvironment is still
assigning a static direction even when light.orbit is present, which leaves the
returned object internally inconsistent. Update the direction handling in the
same orbit-aware pattern used by ambient, diffuse, specularStrength, and
rimStrength: in the function that returns the merged light object, keep
direction undefined when orbit is driving the light, and only fall back to
titleSceneSunDirection(hour, factors) for non-orbit lights. This should be
applied in the logic around titleSceneEnvironment so any direct readers of
.light.direction do not see a misleading value.

---

Outside diff comments:
In `@src/adapters/title-scene-environment-decoder.ts`:
- Around line 129-138: decodeLight currently merges both decodeLightDirection
and decodeLightOrbit without enforcing the invariant that a light cannot author
both direction and orbit, so the ambiguous state slips through the decoder
boundary. Update decodeLight in title-scene-environment-decoder.ts to validate
the decoded light object before returning it: detect when both direction and
orbit are present and either reject the input with a decode error or emit a
clear warning/assertion and prevent both from coexisting. Keep the rule
centralized here so downstream logic like titleSceneLightDirectionAt does not
need to resolve invalid authored state.
🪄 Autofix (Beta)

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: ASSERTIVE

Plan: Pro

Run ID: 8d6dd497-7fa3-44e5-9d22-acd7f4a0720c

📥 Commits

Reviewing files that changed from the base of the PR and between 4ee060c and f10f7c5.

📒 Files selected for processing (17)
  • scenes/bunny.jedit-scene
  • scenes/continuum-gate.jedit-scene
  • spec/title-scene-loader.spec.mjs
  • spec/title-scene-neon-dispersion.spec.mjs
  • spec/title-scene-profile-cli.spec.mjs
  • spec/title-scene-render.spec.mjs
  • spec/workspace-app-echo-typing-sequencing.spec.mjs
  • spec/workspace-echo-test-utils.mjs
  • spec/workspace-runtime.spec.mjs
  • spec/workspace-text-cutover.spec.mjs
  • spec/workspace-text-settlement-refresh.spec.mjs
  • spec/workspace-title-screen.spec.mjs
  • src/adapters/title-scene-environment-decoder.ts
  • src/app/workspace/title-mesh-materials.ts
  • src/ports/title-scene-loader.ts
  • src/ui/title-scene-environment.ts
  • src/ui/title-screen.ts
💤 Files with no reviewable changes (1)
  • spec/workspace-text-cutover.spec.mjs
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: test / echo-authority
  • GitHub Check: test / contract-api
  • GitHub Check: test / cycle-proofs
  • GitHub Check: release-gate / jedit-echo
🧰 Additional context used
📓 Path-based instructions (3)
**

⚙️ CodeRabbit configuration file

**: # jedit Agent Contract

Git Safety

  • Never amend commits. Make a new commit.
  • Never rebase unless the user explicitly approves it after a concrete explanation.
  • Never force git operations.
  • Draft pull requests are allowed only for design-cycle kickoff or active cycle
    work. Convert them to ready before merge. If draft PRs are unavailable, use a
    normal PR with the work-in-progress label.

Quality Doctrine

These are hard repo rules, not suggestions:

  1. No any.
  2. No unknown.
  3. Hexagonal architecture, strict.
  4. Encoding and decoding happen only at the boundary, inside a port/adapter.
  5. One file equals one runtime truth.
  6. Runtime truth beats compile-time theater.
  7. No ad hoc string comparison in core logic. Prefer instanceof, numeric tags, symbols, or explicit runtime objects.
  8. No magic strings.
  9. No magic numbers.
  10. SOLID.
  11. DRY.
  12. DI.
  13. Red to green.
  14. Tests are spec.
  15. Agent-first.
  16. No file over 500 lines of code.

Structure

  • ARCHITECTURE.md is the canonical repo architecture doctrine.
  • docs/BEARING.md records the current execution gravity.
  • docs/method/process.md is the canonical cycle workflow.
  • docs/method/roadmap-planning.md defines release-gate, roadmap, slice, and
    proof policy for multi-cycle planning.
  • docs/design/TEMPLATE.md is the required template for full cycle designs.
  • docs/design/0034-design-cycle-template-and-lifecycle.md records the policy
    decision that made executable design docs official.
  • docs/design/0024-jedit-powered-by-echo-release-gate.md is the completed
    baseline plan for the first thirty slices proving jedit has an installed
    package/evidence path while Echo remains generic.
  • docs/design/0025-echo-application-hosting-pattern.md is the completed
    post-release-pressure ten-slice plan for proving the reusable Echo
    application-hosting pattern before applying it to Graft, Think, or other apps.
  • docs/design/0026-echo-hosting-hardening-first-twenty.md is th...

Files:

  • scenes/bunny.jedit-scene
  • scenes/continuum-gate.jedit-scene
  • spec/workspace-runtime.spec.mjs
  • spec/title-scene-loader.spec.mjs
  • src/ports/title-scene-loader.ts
  • spec/title-scene-render.spec.mjs
  • spec/title-scene-profile-cli.spec.mjs
  • src/ui/title-screen.ts
  • spec/workspace-text-settlement-refresh.spec.mjs
  • src/adapters/title-scene-environment-decoder.ts
  • spec/workspace-app-echo-typing-sequencing.spec.mjs
  • src/app/workspace/title-mesh-materials.ts
  • spec/workspace-title-screen.spec.mjs
  • src/ui/title-scene-environment.ts
  • spec/workspace-echo-test-utils.mjs
  • spec/title-scene-neon-dispersion.spec.mjs
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: No any type usage in TypeScript code
No unknown type usage in TypeScript code
No TypeScript file over 500 lines of code
Make invalid states unrepresentable at runtime where possible
Prefer constructor or factory injection over ambient singletons for new code

Files:

  • src/ports/title-scene-loader.ts
  • src/ui/title-screen.ts
  • src/adapters/title-scene-environment-decoder.ts
  • src/app/workspace/title-mesh-materials.ts
  • src/ui/title-scene-environment.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js}: No ad hoc string comparison in core logic; prefer instanceof, numeric tags, symbols, or explicit runtime objects
No magic strings in code
No magic numbers in code

Files:

  • src/ports/title-scene-loader.ts
  • src/ui/title-screen.ts
  • src/adapters/title-scene-environment-decoder.ts
  • src/app/workspace/title-mesh-materials.ts
  • src/ui/title-scene-environment.ts
🔇 Additional comments (19)
scenes/bunny.jedit-scene (2)

3-4: 🎯 Functional Correctness

Re-verify camera target alignment now that it's an explicit vector.

Past review flagged camera.target[0] = -1.05 as 1.03 units off from the bunny mesh footprint center (-0.017), exceeding MAX_FACE_TARGET_CENTERLINE_OFFSET = 0.25 in the neon-dispersion spec, and it was marked addressed in earlier commits. This diff still emits target: [-1.05, 1.2, -0.24] with the same x-coordinate that was originally flagged. Please confirm spec/title-scene-neon-dispersion.spec.mjs still passes with this exact vector (e.g., re-run it) rather than assuming the prior fix covers this restated value.


68-103: LGTM!

spec/title-scene-loader.spec.mjs (1)

138-193: LGTM!

src/ports/title-scene-loader.ts (1)

11-11: 🩺 Stability & Availability

Confirm the new default scene decodes cleanly.

This makes continuum-gate.jedit-scene the app's default startup scene. Since that file isn't in this review batch, please confirm it passes the new decodeLightOrbitObject zero-vector guard (radius/height not both 0) — a decode failure here would break app startup for every user on first load.

spec/title-scene-render.spec.mjs (1)

326-329: LGTM!

spec/title-scene-profile-cli.spec.mjs (1)

128-128: LGTM!

src/ui/title-screen.ts (1)

26-31: LGTM!

Also applies to: 330-334

src/adapters/title-scene-environment-decoder.ts (1)

154-188: LGTM!

src/ui/title-scene-environment.ts (1)

27-42: LGTM!

Also applies to: 121-139

spec/title-scene-neon-dispersion.spec.mjs (2)

39-94: 🎯 Functional Correctness

Downstream of the scene-data issue flagged in scenes/continuum-gate.jedit-scene.

This test's camera/target-to-primaryCenter alignment assertions (lines 59-64) depend on stanford-bunny having an authored position; see the root-cause comment on the scene file. No separate fix needed here — flagging only so the two are read together.


1-38: LGTM!

Constant renames, the new loadDefaultSceneModules/loadDefaultScene helpers, and the remaining migrated tests are consistent with the switch from the bunny scene to continuum-gate.jedit-scene.

Also applies to: 96-227

spec/workspace-runtime.spec.mjs (1)

305-305: LGTM!

spec/workspace-text-settlement-refresh.spec.mjs (2)

21-253: LGTM!

Rest of the sequencing/highlighting assertions (Test 1 lines 21-112, Test 2 flow lines 114-253) are internally consistent and correctly exercise the intermediate-refresh-before-export ordering.


174-185: 🗄️ Data Integrity & Integration

No issue: the sequencer uses bufferId equality, so this inline target is a valid input and object identity does not affect queueing.

			> Likely an incorrect or invalid review comment.
spec/workspace-app-echo-typing-sequencing.spec.mjs (1)

14-20: LGTM!

The UTF-8 byte-coordinate test checks out by hand-trace, and the prior "fixed double await Promise.resolve()" flakiness concern is now resolved via waitForItemCount.

Also applies to: 88-88, 177-177, 258-258, 328-328

src/app/workspace/title-mesh-materials.ts (1)

89-89: LGTM!

Rename to titleObjectWithMaterial and the broadened TitleSceneObject signature are mechanically consistent with the callers.

Also applies to: 111-122

spec/workspace-title-screen.spec.mjs (1)

140-151: LGTM!

Replacing the hardcoded scene name with port.DEFAULT_BUILT_IN_TITLE_SCENE_NAME removes a magic string from the test.

spec/workspace-echo-test-utils.mjs (1)

44-58: LGTM!

Both previously-flagged issues are resolved: insert now converts byte offsets via stringIndexFromUtf8ByteOffset (verified correct by hand-trace, including surrogate-pair code points), and applyWorkspaceMessage now drains follow-up commands/messages in a loop instead of discarding them. waitForItemCount's bounded poll also addresses the earlier "don't key assertions off a fixed tick count" feedback.

Also applies to: 96-111, 113-130, 140-159

scenes/continuum-gate.jedit-scene (1)

2-5: 🎯 Functional Correctness

position is not a valid mesh placement field here.

Mesh scene objects intentionally have no position; the loader ignores it and uses offset for mesh placement instead. Adding position here would be a no-op.

			> Likely an incorrect or invalid review comment.

Comment thread spec/title-scene-render.spec.mjs Outdated
Comment on lines +15 to +18
const TITLE_MESH_MATERIAL_OBJECT_LABELS = new Set([
"stanford-bunny",
"title-primary-mesh",
]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Label-based matching reintroduces the ad hoc string comparison the doctrine forbids.

isTitleMeshMaterialObject now drops the mesh-kind check entirely and matches purely by testing object.label against a Set of raw strings ("stanford-bunny", "title-primary-mesh"). This is exactly the "ad hoc string comparison in core logic" / "magic strings" pattern the repo's hard doctrine calls out (prefer instanceof, numeric tags, symbols, or explicit runtime objects). It's also a step back from the prior kind+label guard: any future object (mesh or primitive) that happens to reuse one of these two label strings will now silently receive mesh-material treatment, since kind is no longer checked.

Consider decoding a dedicated boolean/tag field (e.g. isMaterialTarget) onto the runtime scene object at the adapter boundary instead of comparing labels in applyTitleMeshMaterial, keeping the string→tag translation confined to the decoder per "Encoding and decoding happen only at the boundary."
As per coding guidelines, "No ad hoc string comparison in core logic; prefer instanceof, numeric tags, symbols, or explicit runtime objects" and "No magic strings in code."

Also applies to: 103-109

🤖 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 `@src/app/workspace/title-mesh-materials.ts` around lines 15 - 18, The title
mesh matching logic has regressed to ad hoc label string comparison, so update
`isTitleMeshMaterialObject` and `applyTitleMeshMaterial` to use a dedicated
runtime tag/boolean or object-type check instead of raw `object.label` strings.
Restore a non-string guard for mesh-kind or decode an explicit field like
`isMaterialTarget` at the adapter boundary, and keep
`TITLE_MESH_MATERIAL_OBJECT_LABELS` out of core matching logic.

Source: Coding guidelines

Comment on lines 247 to 255
return {
...light,
direction: titleSceneSunDirection(hour, factors),
ambient: mixScalar(NIGHT_AMBIENT, DAY_AMBIENT, factors.daylight),
diffuse: mixScalar(NIGHT_DIFFUSE, DAY_DIFFUSE, Math.max(factors.daylight, factors.twilight * 0.72)),
specularStrength: mixScalar(NIGHT_SPECULAR, DAY_SPECULAR, factors.daylight),
rimStrength: mixScalar(NIGHT_RIM, DAY_RIM, factors.daylight),
direction: light?.direction ?? titleSceneSunDirection(hour, factors),
ambient: dayNightLightAmbient(light, factors),
diffuse: dayNightLightDiffuse(light, factors),
specularStrength: dayNightLightSpecular(light, factors),
rimStrength: dayNightLightRim(light, factors),
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

direction field isn't orbit-aware, unlike its three sibling fields.

ambient/diffuse/specularStrength/rimStrength were all refactored to check light?.orbit != null before falling back to day/night mixing (lines 257-295), but direction at line 249 still unconditionally computes titleSceneSunDirection(hour, factors) whenever light.direction is unset — ignoring light.orbit entirely. The returned SceneLight ends up carrying both an orbit and a semantically bogus static direction.

Currently masked because titleSceneLightDirectionAt checks orbit first and ignores direction when orbit is present, but any other code path reading .light.direction directly on this day/night-processed result gets a misleading value. Making the field orbit-aware (e.g. leaving it undefined when orbit drives the light) keeps the object internally consistent.

As per coding guidelines, "Make invalid states unrepresentable at runtime where possible."

🐛 Proposed fix
   return {
     ...light,
-    direction: light?.direction ?? titleSceneSunDirection(hour, factors),
+    direction:
+      light?.orbit != null
+        ? light.direction
+        : light?.direction ?? titleSceneSunDirection(hour, factors),
     ambient: dayNightLightAmbient(light, factors),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return {
...light,
direction: titleSceneSunDirection(hour, factors),
ambient: mixScalar(NIGHT_AMBIENT, DAY_AMBIENT, factors.daylight),
diffuse: mixScalar(NIGHT_DIFFUSE, DAY_DIFFUSE, Math.max(factors.daylight, factors.twilight * 0.72)),
specularStrength: mixScalar(NIGHT_SPECULAR, DAY_SPECULAR, factors.daylight),
rimStrength: mixScalar(NIGHT_RIM, DAY_RIM, factors.daylight),
direction: light?.direction ?? titleSceneSunDirection(hour, factors),
ambient: dayNightLightAmbient(light, factors),
diffuse: dayNightLightDiffuse(light, factors),
specularStrength: dayNightLightSpecular(light, factors),
rimStrength: dayNightLightRim(light, factors),
};
}
return {
...light,
direction:
light?.orbit != null
? light.direction
: light?.direction ?? titleSceneSunDirection(hour, factors),
ambient: dayNightLightAmbient(light, factors),
diffuse: dayNightLightDiffuse(light, factors),
specularStrength: dayNightLightSpecular(light, factors),
rimStrength: dayNightLightRim(light, factors),
};
}
🤖 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 `@src/ui/title-scene-environment.ts` around lines 247 - 255, The SceneLight
builder in titleSceneEnvironment is still assigning a static direction even when
light.orbit is present, which leaves the returned object internally
inconsistent. Update the direction handling in the same orbit-aware pattern used
by ambient, diffuse, specularStrength, and rimStrength: in the function that
returns the merged light object, keep direction undefined when orbit is driving
the light, and only fall back to titleSceneSunDirection(hour, factors) for
non-orbit lights. This should be applied in the logic around
titleSceneEnvironment so any direct readers of .light.direction do not see a
misleading value.

Source: Coding guidelines

Comment thread src/ui/title-scene-environment.ts Outdated
Comment thread src/ui/title-scene-environment.ts Outdated
@flyingrobots
flyingrobots merged commit a19abca into main Jul 2, 2026
12 of 13 checks passed
@flyingrobots
flyingrobots deleted the fix/typing-history-regression branch July 2, 2026 02:05
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