Skip to content

fix(a11y): require image alt intent on paste and drop - #155

Draft
seonghobae wants to merge 28 commits into
mainfrom
fix/image-ingress-alt-intent-20260811
Draft

fix(a11y): require image alt intent on paste and drop#155
seonghobae wants to merge 28 commits into
mainfrom
fix/image-ingress-alt-intent-20260811

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Problem

Protected main already requires explicit informative-versus-decorative intent for toolbar image upload, but shared Base64Image paste/drop ingress silently used alt: ''. The same asynchronous path also let multi-file conversion completion order control which source received each author response, and repeated insertion at a captured drop position could reverse source order.

A later reliability audit on this existing single-writer lane found a distinct trust-boundary defect: the host-supplied onError observer was called directly from parser rejection, asynchronous image-ingress rejection, and transaction-filter rejection. A host observer that throws could therefore escape and change Inkspan's fail-closed rejection result even though the observer owns presentation/telemetry only.

Test-first lineage

  • Initial accessibility RED 41044b76c45632de24bb7a51b43ebdd7df6acaae proved paste/drop did not obtain explicit alternative-text intent and that cancel semantics needed to leave document state unchanged.
  • Multi-file RED 38f38b948795d21fccef1e3db3890bb854f5aeac, CI 31432587385, build/test job 93599278916, failed exactly because completion order bound author intent to the wrong source file.
  • Source-order repair 138e955b601b7f2fb17327b4f2a7d58ee2590484, CI 31432869794, then exposed the fixed-coordinate insertion reversal; subsequent repair mapped the drop position through each ProseMirror transaction.
  • Observer-containment RED e4425c450c8dced8be7d7c71fd62b8052193cc9d, CI 31943277419, build/test job 95155409051, failed exactly because a private sentinel thrown by host onError escaped parser rejection.
  • Pre-synchronization repair head 52f7a1c3c0d0c59b27f7bef7168905a728a77da5 made host observer delivery best-effort/side-effect-only across parser rejection, asynchronous ingress rejection, and transaction-filter rejection while retaining fail-closed rejection authority.

Implemented boundary

  • image files are converted, prompted, and inserted in original source order;
  • alternative-text intent stays bound to the source file for which it was requested;
  • explicit empty text remains intentional decoration and cancel skips only that file;
  • conversion failures do not prevent later files from being considered;
  • host onError failures cannot override rejection;
  • hostile thrown values remain normalized without unsafe coercion;
  • destroyed/read-only editor state terminates stale asynchronous ingress;
  • fixed drop coordinates advance through ProseMirror transaction mapping; and
  • no transport, persistence, credential, model, collaboration-provider, tenant, network, or durable-audit authority is added.

Historical protected-main synchronization

Earlier protected-main synchronization used main@3b38ead2d00f44eb578d0689087b9293b3dabe1e. The then-current Draft head was 1c945a6ab0abd3e34cb215c543447ecf08573fd6, a non-destructive two-parent synchronization commit whose first parent was the pre-synchronization repair head and whose second parent was that protected-main tip. These identities are historical, not current.

That synchronization tree was the then-current protected main plus this lane's eight established src/extensions/Base64Image* source/test paths. Its historical comparison reported ahead_by=20, behind_by=0, and those eight changed paths. The release exact-checkout repair was inherited unchanged. No force push or destructive rebase was used.

Historical CI evidence

For predecessor head 1c945a6ab0abd3e34cb215c543447ecf08573fd6, the earlier exact-head acquisition recorded:

  • CI 32073282477: completed / success;
  • Security Scan 32073282463: completed / success;
  • SAST Semgrep 32073282439: completed / success;
  • formal reviews: 0;
  • unresolved inline review threads: 0;
  • GitHub reported that Draft mergeable at acquisition time.

These repository-local exact-head results are technical evidence only. They do not establish separately applicable central required workflows or qualifying independent approval. Successful predecessor evidence remains historical and does not transfer.

Current repair and verification

Current Draft head is 4a53c08314de87ba700acd75b6b8b5a17bc2c1e9; current protected main independently resolves to 0b88c16f14f51b54a87eb7164f0edfb06dd60902. No current-main synchronization or protected merge is claimed. GitHub currently reports BLOCKED and REVIEW_REQUIRED.

The shared paste/drop path previously checked editor state after conversion but not after host alternative-text prompting. RED 15b9b115228744038903a98c726ee7687704f746 reproduced both read-only document mutations. Repair da315e7bda3de114570d5207ee151dd602d6117b adds one shared destroyed/read-only guard immediately after prompting. Assurance commit 4a53c08314de87ba700acd75b6b8b5a17bc2c1e9 additionally covers editor destruction during prompting for both ingress paths.

  • Current focused suite: nine image-related files, 91 tests pass; changed Base64Image.ts has 100% statement/branch/function/line coverage. This is not whole-project coverage.
  • Current TypeScript check passes.
  • Controlled source-browser regression: all six paste/drop cases pass across Chromium, Firefox and WebKit with native prompt dialogs. Screenshots were opened for visual inspection: read-only bilingual text remains legible and unchanged, with no inserted image.
  • These use synthetic regression input, not physical clipboard/IME/device or full packaged-editor acceptance. An earlier acquisition's Firefox navigation timeout remains recorded, not erased by the subsequent pass.
  • Current complete build, verify:package and archive acquisition pass (terminal exit 0). All 13 package-configuration tests and existing ESM/CommonJS/TypeScript consumer checks pass, including independently staged and framework-free subpath consumers. Local contextualwisdomlab-cwl-editor-0.6.0.tgz SHA-256 is 2f4fdd915cfe4d69e8020c1d92d96ece9d20f54f1fb690e295b7b00fae341834; this is not publication evidence. Full project coverage, full browser acceptance and hosted exact-head gates remain outstanding.

Detailed current evidence and preserved predecessor results: #155 (comment).

Subsequent full current-head coverage acquisition failed: 887 passed / 18 failed / 905 tests across 161 files, 735.62 seconds, exit 1. Seventeen failures are existing 5000ms timeouts; one decorative-image toolbar test expected one prompt call and observed two. This distinct assertion must not be described as a timeout. Cause remains unverified. No full-suite or full-coverage acceptance; focused and package successes above remain limited to their own scopes.

Integration dependency

Open issue #118 owns next-stable publication and operational acceptance; it deliberately does not freeze a release version or source SHA as current truth. Keep this PR Draft until its dependencies and evidence are ready. Qualifying independent approval and every applicable central exact-head gate remain required before ordinary protected integration. Stable publication is a separate later decision under #118.

Live organization policy must be freshly established before any lifecycle transition. Automated/model/status evidence is not formal approval; do not self-approve, weaken gates, transfer predecessor evidence, or fabricate release identity.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

@opencode-agent opencode-agent Bot added area: accessibility Accessibility and assistive-technology support priority: medium Normal-priority or P2 work status: draft Draft pull request type: bug Defect or incorrect behavior labels Aug 22, 2026
@seonghobae

Copy link
Copy Markdown
Contributor Author

Restacked the existing image-ingress accessibility and reliability boundary onto exact protected main and propagated the event-specific Python matrix contract. Exact head: e7dcda5eba090bb950e5765d061b6f534539cd6c. Fresh local proof: focused Base64Image ingress/policy suite 8 files / 85 tests; TypeScript typecheck; full Vitest 161 files / 901 tests with 100% statements, branches, functions, and lines; production build; all packed package consumers; Python support contract 4/4. Draft and independent-approval gates remain unchanged.

@seonghobae

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Remove the duplicated Python support contract changes from this image-ingress branch. PR #405 remains the single writer while this branch keeps its accessible image intent delta.

Signed-off-by: Seongho Bae <seonghobae@users.noreply.github.com>

Commit-Message-Assisted-by: Claude (via Claude Code)
Signed-off-by: Seongho Bae <me@seonghobae.me>
Signed-off-by: Seongho Bae <me@seonghobae.me>
@seonghobae

seonghobae commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Post-prompt read-only lifecycle repair

Current assurance head

4a53c08314de87ba700acd75b6b8b5a17bc2c1e9 adds paste/drop destruction
regressions; runtime repair is unchanged. Twelve lifecycle/order/error tests
pass. All nine image-related files91tests pass with the changed
Base64Image.ts at100% statement/branch/function/line coverage; this is not
whole-project coverage. Current TypeScript passes and normal push is independently
verified.

A new complete controlled source-browser diagnostic passed all6 paste/drop
cases in Chromium, Firefox and WebKit using actual native dialogs. Screenshots
were opened and paired hashes verified; readable readonly bilingual content
remains with no image insertion. This is synthetic regression input, not
physical clipboard/IME/device support or full packaged-editor acceptance.
New evidence: /private/tmp/inkspan-pr155-prompt-visual.wgZkwl.

Current complete pnpm build, pnpm verify:package and external pnpm pack
passed as one sequential chain (terminal exit 0). All 13 package-configuration
tests pass alongside existing ESM/CommonJS/TypeScript and independently staged
or framework-free consumer checks. Local archive
contextualwisdomlab-cwl-editor-0.6.0.tgz has SHA-256
2f4fdd915cfe4d69e8020c1d92d96ece9d20f54f1fb690e295b7b00fae341834.
The editor source map contains the exact current Base64Image source. HEAD and
tracked worktree remained unchanged; no release directory was left behind.
Logs: /private/tmp/inkspan-pr155-current-package.DR54gG.
This does not establish full project coverage, full packaged-browser acceptance,
hosted CI, protected integration or publication.

Subsequent full current-head coverage acquisition FAILED (exit 1): 887 passed,
18 failed, 905 total tests; 145 passed / 16 failed files (161 total), 735.62s.
Seventeen failures report the existing 5000ms timeout. One decorative-image
toolbar assertion expected one prompt call and observed two; it is not a timeout.
Cause is unverified, including whether the preceding timed-out informative-image
test interfered. Full log remains in the package evidence directory. No timeout,
selection, worker, retry or threshold changes; no whole-project coverage claim.

Follow-up investigation: the unchanged five-case image-intent suite passed
(30.18s), so the earlier full-suite duplicate-prompt cause remains unverified.
Existing toolbar owner PR158
already guards conversion completion after editor destruction. A distinct
post-prompt lifecycle hole was reproduced and repaired there, not copied into
this paste/drop lane: current f83d1d9 adds one post-prompt state guard, 31 focused
tests and TypeScript pass, and all three controlled native-dialog browser cases
pass with directly inspected screenshots. This is not a fix claim for the earlier
full-suite timing failure, and PR155 remains at its unchanged assurance head.

Preserved repair and predecessor acquisition

The existing shared paste/drop path checked editor state after conversion but
not after alternative-text prompting. A host prompt implementation that makes
the editor read-only before returning could still insert an image.

RED 15b9b115228744038903a98c726ee7687704f746 reproduced both paste/drop
document mutations (2failed/3passed). Repair
da315e7bda3de114570d5207ee151dd602d6117b adds one shared destroyed/read-only
guard immediately after prompting. Four related files10tests and TypeScript
pass; existing cancellation, source ordering and error containment remain.

Actual Chromium native-dialog regression probes passed paste/drop assertions;
the paste screenshot was directly inspected and retains the readonly bilingual
document without an image. Drop visual preview was inconclusive. Firefox
navigation timed out before the fixture; WebKit did not run. This is partial
source-browser evidence using synthetic input, not physical clipboard support,
full package/cross-engine acceptance or protected release. No timeout changes.

Complete logs and screenshots: /private/tmp/inkspan-pr155-prompt-lifecycle.9r5LpQ.
Broader current-head checks remain required; predecessor evidence does not transfer.

Signed-off-by: Seongho Bae <me@seonghobae.me>
@seonghobae seonghobae added the bug Something isn't working label Sep 7, 2026 — with ChatGPT Codex Connector
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: accessibility Accessibility and assistive-technology support bug Something isn't working priority: medium Normal-priority or P2 work status: draft Draft pull request type: bug Defect or incorrect behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant