Skip to content

refactor: simplify verifyx gate - #19

Merged
aorumbayev merged 10 commits into
mainfrom
fix/verify-gate-gaps
Jul 11, 2026
Merged

refactor: simplify verifyx gate#19
aorumbayev merged 10 commits into
mainfrom
fix/verify-gate-gaps

Conversation

@aorumbayev

@aorumbayev aorumbayev commented Jul 10, 2026

Copy link
Copy Markdown
Member

Changes

  • removes redundant verifyx lint, type-check, and duplicate-code overrides; built-in checks now cover all source
  • deletes the duplicate main-push check workflow trigger
  • simplifies update-marker cleanup and removes obsolete guard tests
  • merges single-importer modules and consolidates TUI task actions
  • extracts shared/local TUI blocks so duplicate-code passes without source exclusions

Verification

  • bun run verify
  • bun run test (604 passing)
  • bunx verifyx lint
  • bunx verifyx check-types
  • bunx verifyx duplicate-code
  • bun run plugin:install

Manual Smoke

Restart OpenCode, then open the board, settings route, and create-task dialog to confirm rendering and submission.

Install knip, skott, and jscpd so verifyx all runs every built-in gate, dedupe
status resolution and break git/domain layering cycles flagged by the new checks,
and make the pre-commit hook check-only so staged content is never rewritten.

Co-authored-by: Cursor <cursoragent@cursor.com>
@socket-security

socket-security Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedskott@​0.35.119810010092100
Addedknip@​6.25.0991009596100
Addedjscpd@​5.0.1210010010095100

View full report

@socket-security

socket-security Bot commented Jul 10, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm @emnapi/runtime is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: ?npm/knip@6.25.0npm/@emnapi/runtime@1.11.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@emnapi/runtime@1.11.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm effect is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: ?npm/skott@0.35.11npm/effect@3.21.4

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/effect@3.21.4. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm formatly is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: ?npm/knip@6.25.0npm/formatly@0.3.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/formatly@0.3.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
License policy violation: npm typescript

License: LicenseRef-W3C-Community-Final-Specification-Agreement - The applicable license policy does not permit this license (5) (package/ThirdPartyNoticeText.txt)

From: ?npm/skott@0.35.11npm/typescript@5.9.3

ℹ Read more on: This package | This alert | What is a license policy violation?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Find a package that does not violate your license policy or adjust your policy to allow this package's license.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/typescript@5.9.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown

Greptile Summary

This PR simplifies the verify and automatic-update paths. The main changes are:

  • Removes extra verifyx overrides and duplicate workflow triggers.
  • Splits automatic update cleanup, launch, and path helpers.
  • Consolidates TUI task actions and shared UI blocks.
  • Updates tests and docs for the new verify and update behavior.

Confidence Score: 4/5

The automatic update rollback path can still leave kagan@latest missing after a failed restore.

The changed restore branch hides the failure that matters for recovering the active wrapper.

Startup cleanup can show updates as unavailable, but it does not recreate the missing wrapper from backup.

T-Rex T-Rex Logs

What T-Rex did

  • Ran a focused Bun test reproducing the restore failure rollback path by driving prepareUpdate with a fake TuiPluginApi and invoking the registered dispose callback.
  • Observed the controlled FileSystem rename sequence and the final runtime state, including current-to-backup success, prepared-to-current failure, and backup-to-current restore failure, with the dispose callback throwing only the prepared promotion error.
  • Collected and inspected repro artifacts, including the Bun test that exercises the restore rollback path and the verbose test output showing the swallowed restore error and missing current wrapper.
  • Reviewed broader UI and TUI coverage, including UI smoke before/after renders, targeted TUI tests (37 passing), and the duplicate-code verification (exited 0).

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
src/tui/update-manager.ts The promotion rollback path still drops a failed restore after moving the active wrapper to backup.
src/tui/update-cleanup.ts The new cleanup helper removes stale update state and reports broken cleanup through the launch flow.
src/tui/update-launch.ts The new launch helper runs cleanup before checking and preparing automatic updates.
src/tui/update-paths.ts The path helper now owns wrapper target derivation and the simplified update marker shape.

Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
src/tui/update-manager.ts:28-29
**Restore Failure Is Dropped**

When `current` has already moved to `backup`, a failed `prepared -> current` rename depends on this restore to recreate `kagan@latest`. If the restore rename also fails, this branch drops that failure and rethrows only the promotion error, leaving the active wrapper missing while startup cleanup can only mark updates unavailable instead of recovering the loadable wrapper.

Reviews (4): Last reviewed commit: "test: make merge-dialog command tests de..." | Re-trigger Greptile

* test: make git hermeticity structural via preload

* test: rename hermeticity wording to git isolation

* test: wait for the merge-conflict notice instead of asserting notice order

* test: allow slow CI git spawns to deliver the merge-conflict notice

* fix: make update promotion crash-safe and trim speculative machinery (#21)

* fix: make update promotion crash-safe and trim speculative machinery

Restore interrupted promotions on launch, self-heal stale cache state, surface
local update failures in the footer, and remove unused concurrency/path checks.

* fix: harden update cleanup against unvalidated deletes and false ready

Finding 1: removeStaleMarker only deletes marker.prepared when it is a
kagan@<x.y.z> sibling of the current wrapper in the same scope cache;
a corrupted marker naming any other path now leaves that path alone.

Finding 2: removed dead restoreCurrentFromBackup / interruptedPromotion
branch (the host re-downloads latest before the plugin loads, so it can
never run), aligned R18.8 and design.md to the real contract, and closed
the prepared-dir leak in the matched-marker cleanup path.

Finding 3: prepare failure on a ready check now sets broken status and
suppresses the ready toast instead of promising a restart that applies
nothing; a failed cleanup now returns broken before the network check.

* test: skip the host-dedupe pin when the vendored source is absent

references/opencode is git-ignored, so CI checkouts never have it.

---------
@aorumbayev

Copy link
Copy Markdown
Member Author

@greptileai

@aorumbayev aorumbayev changed the title fix: enforce all verifyx checks and check-only pre-commit refactor: simplify verifyx gate Jul 10, 2026
renderListEditorRows only forwarded its five arguments to <ListEditorRows>
and had a single caller; render it directly and drop the now-unused JSX import.
@aorumbayev

Copy link
Copy Markdown
Member Author

@greptileai

Comment thread src/tui/update-manager.ts
Comment on lines 27 to 30
} catch (error) {
try {
await fs.rename(paths.backup, paths.current)
} catch (restoreError) {
throw new AggregateError([error, restoreError], "Kagan update promotion and restore failed")
}
await fs.rename(paths.backup, paths.current).catch(() => {})
throw error
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Restore Failure Gets Hidden

When promotion fails after current has already moved to backup, this restore is the only path that recreates the active kagan@latest wrapper. Swallowing a failed restore reports only the promotion error and can leave the next OpenCode launch without a loadable Kagan wrapper.

Suggested change
} catch (error) {
try {
await fs.rename(paths.backup, paths.current)
} catch (restoreError) {
throw new AggregateError([error, restoreError], "Kagan update promotion and restore failed")
}
await fs.rename(paths.backup, paths.current).catch(() => {})
throw error
}
} catch (error) {
try {
await fs.rename(paths.backup, paths.current)
} catch (restoreError) {
throw new AggregateError([error, restoreError], "Kagan update promotion and restore failed")
}
throw error
}
Artifacts

Repro: focused Bun test harness for hidden rollback failure

  • Contains supporting evidence from the run (text/typescript; charset=utf-8).

Repro: Bun test output showing hidden restore failure and missing current wrapper

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/tui/update-manager.ts
Line: 27-30

Comment:
**Restore Failure Gets Hidden**

When promotion fails after `current` has already moved to `backup`, this restore is the only path that recreates the active `kagan@latest` wrapper. Swallowing a failed restore reports only the promotion error and can leave the next OpenCode launch without a loadable Kagan wrapper.

```suggestion
  } catch (error) {
    try {
      await fs.rename(paths.backup, paths.current)
    } catch (restoreError) {
      throw new AggregateError([error, restoreError], "Kagan update promotion and restore failed")
    }
    throw error
  }
```

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

The two "approving …" tests in test/tui/board/commands.test.ts flaked only on
CI. Root cause: bun's mock.module is process-global and persistent, and
test/tui/session/tasks.test.ts mocks src/git/runner (currentBranch -> "kagan/x")
and src/git/merge (mergeTaskBranch -> ok:true). ESM imports hoist above
mock.module, so whichever test file loads first wins; on the Linux runner
tasks.test.ts loaded first, so the merge-dialog tests saw the leaked mocks
(wrong branch, no conflict) and failed. macOS load order hid it locally.

- commands.test.ts now declares its own git/runner + git/merge mocks and drives
  the three merge-dialog tests through reset-per-test vars (currentBranchValue,
  localBranches, mergeResult), so its values win for its own tests regardless of
  which file ran first.
- tasks.test.ts's git/runner mock was incomplete (missing listLocalBranches and
  baseBranchFreshness); stub them so binding those exports elsewhere while the
  mock is active no longer throws "Export not found".
- The merge dialog's onSelect returns its handler promise so tests await the
  real work instead of racing a render/timer.

Reproduced the exact leak order locally (bun test tasks.test.ts commands.test.ts)
and via a Linux-container full-suite run; green there, commands-only, and reverse.
@aorumbayev
aorumbayev force-pushed the fix/verify-gate-gaps branch from bba1419 to 0b9b330 Compare July 11, 2026 17:29
@aorumbayev

Copy link
Copy Markdown
Member Author

@greptileai

Comment thread src/tui/update-manager.ts Outdated
Comment on lines 28 to 29
await fs.rename(paths.backup, paths.current).catch(() => {})
throw error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Restore Failure Is Dropped

When current has already moved to backup, a failed prepared -> current rename depends on this restore to recreate kagan@latest. If the restore rename also fails, this branch drops that failure and rethrows only the promotion error, leaving the active wrapper missing while startup cleanup can only mark updates unavailable instead of recovering the loadable wrapper.

Context Used: AGENTS.md (source)

Artifacts

Repro: generated Bun test exercising the restore failure rollback path

  • Contains supporting evidence from the run (text/typescript; charset=utf-8).

Repro: verbose Bun test output showing swallowed restore error and missing current wrapper

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/tui/update-manager.ts
Line: 28-29

Comment:
**Restore Failure Is Dropped**

When `current` has already moved to `backup`, a failed `prepared -> current` rename depends on this restore to recreate `kagan@latest`. If the restore rename also fails, this branch drops that failure and rethrows only the promotion error, leaving the active wrapper missing while startup cleanup can only mark updates unavailable instead of recovering the loadable wrapper.

**Context Used:** AGENTS.md ([source](https://app.greptile.com/kagan/github/kagan-sh/kagan/-/custom-context?memory=88a14340-9a15-4297-925d-3656d144ad2a))

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

promotePreparedUpdate runs in api.lifecycle.onDispose, whose thrown errors the
host logs via console.error rather than discarding. Swallowing a failed restore
therefore hid it: a lost kagan@latest wrapper logged only as a promotion
failure. Rethrow an AggregateError carrying both, and add a test covering the
double-failure path (the single-failure restore was already tested).
@aorumbayev
aorumbayev merged commit 7268798 into main Jul 11, 2026
4 checks passed
@aorumbayev
aorumbayev deleted the fix/verify-gate-gaps branch July 11, 2026 17:40
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 0.3.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant