Skip to content

fix(a11y): add type=button and aria-expanded to bare <button> elements — Batch 22 (QUA-200)#1548

Closed
hungdqdesign wants to merge 246 commits intopaperclipai:masterfrom
hungdqdesign:fix/a11y-button-type-aria-expanded-qua-200
Closed

fix(a11y): add type=button and aria-expanded to bare <button> elements — Batch 22 (QUA-200)#1548
hungdqdesign wants to merge 246 commits intopaperclipai:masterfrom
hungdqdesign:fix/a11y-button-type-aria-expanded-qua-200

Conversation

@hungdqdesign
Copy link

Summary

Thêm type=\"button\" vào các <button> không có explicit type, và aria-expanded vào CollapsibleSection.

Root cause: <button> không có type mặc định là type=\"submit\" bên trong <form>. Dù các dialogs này không dùng <form>, đây vẫn là best practice theo HTML spec và giúp tránh bug tiềm ẩn khi code được refactor.

Files changed

File Changes
agent-config-primitives.tsx CollapsibleSection type=\"button\" + aria-expanded={open}
NewGoalDialog.tsx 7 buttons (3 PopoverTrigger + 4 options)
NewProjectDialog.tsx 4 buttons (2 PopoverTrigger + 2 options)
GoalProperties.tsx PopoverTrigger button
PriorityIcon.tsx Priority selector trigger
MarkdownEditor.tsx @mention dropdown option buttons
SidebarAgents.tsx "New agent" icon button
FilterBar.tsx "Remove filter" badge button

A11Y notes

  • CollapsibleSection: aria-expanded={open} communicates expand/collapse state to screen readers
  • All other changes: type=\"button\" — programmatic correctness

Test plan

  • npx tsc --noEmit pass ✅
  • Manual: No accidental form submissions from any of these buttons
  • Collapse/expand sections in AgentConfigForm — screen reader announces expanded state

Closes QUA-200

🤖 Generated with Claude Code

hungdqdesign and others added 30 commits March 19, 2026 21:13
…03 flood

Add assertAgentRunCheckoutOwnership check to the document PUT endpoint,
matching behavior of other issue mutation endpoints (PATCH, release, comments).
Include retryable: false in conflict error details so agents stop retrying
when checkout has expired or been reassigned.

Closes #1256

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ing tables

When a database has existing tables but no drizzle migration journal
(the "no-migration-journal-non-empty-db" case), applyPendingMigrations
previously threw "automatic migration is unsafe". This blocked system
PostgreSQL users from starting the server after upgrades.

Now, applyPendingMigrations bootstraps the journal by walking each
migration in order and checking whether its DDL objects (tables, columns,
indexes, constraints) already exist in the schema. Migrations whose
objects are present are recorded in the journal without re-running.
The first migration with clearly absent objects is treated as genuinely
pending and applied normally.

Closes #1211

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add test covering the checkout adoption path where logActivity is
called with action "issue.checkout_lock_adopted" when a run takes
over a stale checkout lock.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
…itor, preserve config on re-onboard

- Add "gemini_local" to AGENT_ADAPTER_TYPES constant (fixes #1195)
- Add null guard for MarkdownEditor value prop to prevent setMarkdown crash (fixes #1227)
- Merge existing config.json values during onboard --yes instead of overwriting,
  preserving user customizations like server.host and deploymentMode (fixes #1196)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
…on insertion

Chrome/Chromium and Electron silently ignore document.execCommand("insertText"),
causing @mention selection to insert nothing. Switch to Range.deleteContents() +
Range.insertNode() with a synthetic InputEvent so Lexical picks up the change.
Adds defensive markdown state sync in case the InputEvent does not propagate.

Fixes #1294

Co-Authored-By: Paperclip <noreply@paperclip.ing>
…al status

When a subtask status changes to done or cancelled, the parent task's
assignee agent is now woken immediately via the heartbeat system. This
enables manager agents to advance multi-step workflows without waiting
for the next timer tick.

Fixes #1280

Co-Authored-By: Paperclip <noreply@paperclip.ing>
After dependency updates, drizzle-orm 0.38.4 could not be resolved by
some package managers, causing "Cannot find package drizzle-orm" errors
at runtime. Upgrading to 0.41.0 across cli, db, and server packages
resolves the issue.

Closes GH #1243

Co-Authored-By: Paperclip <noreply@paperclip.ing>
… error

better-auth@1.4.18 requires drizzle-orm >= 0.41.0 as a peer dependency.
The previous version (0.38.4) did not satisfy this requirement, causing
npm/npx installs to fail with "Cannot find package 'drizzle-orm'" when
better-auth's drizzle adapter tried to import it.

Fixes #1243

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a child subtask transitions to done or cancelled, the parent
issue's assigned agent now receives a wakeup with reason
"child_issue_completed". This enables manager agents to react
immediately to subtask completions instead of waiting for the next
timer-based heartbeat interval.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… local_trusted mode

When a bearer token is present but fails validation (expired JWT, invalid key),
the default local_trusted board actor was not cleared, allowing unauthenticated
requests to inherit full board-level access. This enabled agents with expired
auth to perform destructive operations like deleting other agents.

Now clears the actor to { type: "none" } when a bearer token is present before
validation, so only successful verification restores the proper actor type.

Fixes #1314

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…1245)

The release() method only cleared assigneeAgentId and checkoutRunId but
left executionRunId, executionAgentNameKey, and executionLockedAt intact.
This caused permanent stale locks after unclean agent session ends,
making subsequent checkout attempts return 409 Conflict with no recovery
path other than direct SQL.

Now release() nulls all three execution lock fields, matching the
behavior of releaseIssueExecutionAndPromote() in the heartbeat service.

Closes #1245

Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ned deferred wakes (#1269)

When an agent reassigns an issue via PATCH, the update cleared
checkoutRunId but left executionRunId/executionAgentNameKey/
executionLockedAt intact. This caused the new assignee's wake to be
deferred (waiting for the old execution to release), but
releaseIssueExecutionAndPromote could not find the issue if the
execution context had shifted, leaving the deferred wake permanently
orphaned.

Now, changing the assignee also clears all three execution lock fields,
so the new assignee's wake goes through immediately without hitting the
deferred path.

Closes #1269

Co-Authored-By: Paperclip <noreply@paperclip.ing>
…wn crash (#1227)

MDXEditor's setMarkdown() crashes with TypeError when receiving null
(null.trim()). This happened when AgentConfigForm stored null for empty
markdown fields (capabilities, bootstrapPromptTemplate) via the
`v || null` pattern.

Add `?? ""` guards in MarkdownEditor at the setMarkdown() call site and
the initial markdown prop, so null values are safely coerced to empty
strings.

Closes #1227

Co-Authored-By: Paperclip <noreply@paperclip.ing>
…tibility (#1263)

On Windows, Node.js ESM loader requires file:// URLs for dynamic imports.
Raw absolute paths like C:\Users\... fail with ERR_UNSUPPORTED_ESM_URL_SCHEME.
Use pathToFileURL() before calling import(), matching the pattern already used
in cli/src/commands/run.ts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Node.js fetch/undici throws when GET or HEAD requests include a body.
Skip body serialization and content-type header for these methods.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ts' errors

Fixes two issues causing GH #1352 (migration fails on fresh install):

1. applyPendingMigrationsManually now checks each SQL statement against
   the database state before executing it, skipping statements for objects
   that already exist (tables, columns, indexes, constraints). This makes
   migration application idempotent and safe for shared databases or
   partially-failed prior runs.

2. applyPendingMigrations now handles the 'no-migration-journal-non-empty-db'
   case gracefully instead of throwing. It reconciles the migration history
   from existing schema state and then applies remaining migrations with
   per-statement safety checks.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
- heartbeat.ts: fix deriveNormalizedUsageDelta to return 0 (not current value)
  when token counts are non-monotonic, preventing billing over/under-charge
- costs.ts: add 'from > to' date range validation in parseDateRange to reject
  invalid date ranges early
- issues.ts: replace non-null assertion (!) with explicit null check + notFound
  error on issue query results after checkout adoption
- ui/vite.config.ts: add allowedHosts for paperclip.openclawbot.vn

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nt permission auto-reject (#1362)

When an agent's instructions file lives outside the project cwd (e.g. in
~/.paperclip/instances/.../agents/), Claude CLI treats that directory as
"external_directory" and auto-rejects permission requests. This causes a
loop where the agent repeatedly requests and gets denied permission.

Fix: pass the instructions file directory via --add-dir so Claude has
read access to sibling files. Skip when the directory is already inside
the project cwd to avoid redundant entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and add missing models (#1357)

Cursor CLI now rejects "auto" as a model selection with "Cannot use this
model: auto". Change the default to "composer-2" and add "composer-2" +
"composer-2-fast" to the fallback model list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…awbot.vn

Add allowedHosts: "all" to vite server config so that requests from
custom domain behind Cloudflare are not blocked.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gateway only supports v2 payload format (without platform/deviceFamily fields).
Adapter was building v3 payload causing Ed25519 signature mismatch → "device signature invalid".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove no-op reconcilePendingMigrationHistory call in the
  no-migration-journal-non-empty-db branch (P1: the function always
  returned early because inspectMigrations reports a different reason
  when the journal table doesn't exist yet)
- Destructure reconcilePendingMigrationHistory result instead of
  assigning to unused variable (P2)
- Remove hardcoded allowedHosts domain from shared Vite config (P2)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous exact-equality check only caught when instructionsFileDir
was the skillsDir root itself, missing subdirectories. Using startsWith
ensures any path under skillsDir is correctly skipped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…#1391)

Commander's .requiredOption() validates before the action handler runs,
preventing resolveCommandContext() from applying context profile fallback
for companyId. Switch to .option() and let resolveCommandContext() with
requireCompany: true handle validation, which provides a better error
message listing all alternatives (--company-id, env var, or context
profile).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… without journal

The no-migration-journal-non-empty-db branch was dead code: it called
inspectMigrations again (which returned the same state since no journal
table was created), then checked for "pending-migrations" reason which
could never match, always falling through to throw.

Fix: call applyPendingMigrationsManually directly with
initialState.pendingMigrations. This function already handles the full
flow: creates the journal table via ensureMigrationJournalTable, skips
already-applied statements, and records each migration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-id (#1391)

Commander.js .requiredOption() validates before the action handler runs,
preventing resolveCommandContext() from applying the context profile or
env var fallback. Change to .option() in all 7 occurrences across 5
files. The requireCompany check in resolveCommandContext() already
provides a clear error message when no company ID is available from any
source (flag, env var, or context profile).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cancelActiveForAgent sent SIGTERM but immediately deleted the process from
runningProcesses, orphaning children that ignore SIGTERM. Now matches
cancelRunInternal: schedules SIGKILL after graceSec and defers cleanup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
hungdqdesign and others added 26 commits March 22, 2026 13:44
Two branches (parent-wake-on-child-completion and subtask-completion-wakes-parent)
added the same parent-wake logic. Removed the duplicate declaration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolved trivial ordering conflict in issues.ts patch fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolved conflict keeping HEAD variable naming (isBodyless).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolved conflict keeping master's simpler applyPendingMigrationsManually
approach for non-empty DB migration bootstrap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolved conflicts in onboarding assets keeping master versions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolved MarkdownEditor conflict keeping null-safe value guard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Post-merge fix: body was renamed to payload but reference wasn't updated.
Also removed unused hasBody variable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s — Batch 22 (QUA-200)

HTML spec: <button> without explicit type defaults to type="submit" inside <form>.
Best practice: always declare type="button" for non-submit buttons.

Files fixed:
- agent-config-primitives.tsx CollapsibleSection: type=button + aria-expanded={open}
- NewGoalDialog.tsx: 7 popover trigger/option buttons — type=button
- NewProjectDialog.tsx: 4 popover trigger/option buttons — type=button
- GoalProperties.tsx: PopoverTrigger button — type=button
- PriorityIcon.tsx: priority selector trigger — type=button
- MarkdownEditor.tsx: @mention dropdown options — type=button
- SidebarAgents.tsx: "New agent" button — type=button
- FilterBar.tsx: "Remove filter" badge button — type=button

WCAG 2.1: programmatic correctness, prevents accidental form submission.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 22, 2026

Too many files changed for review. (171 files found, 100 file limit)

@hungdqdesign hungdqdesign closed this by deleting the head repository Mar 23, 2026
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