Skip to content

Harden the plugin code with types and contract checks - #7

Merged
gabe4coding merged 1 commit into
mainfrom
claude/codebase-hardening-audit-b8e47f
Oct 13, 2026
Merged

gabe4coding merged 1 commit into
mainfrom
claude/codebase-hardening-audit-b8e47f

Conversation

@gabe4coding

Copy link
Copy Markdown
Owner

Summary

This PR adds types and checks so that wrong plugin code fails before review.

  • Race fix. If two listings arrive together, they now share one tools decision. Before, the second listing could start a second classifier call and get a different answer. Each decision now goes into session memory before it is saved, so later reads in the same step see it.
  • One list of /loadout words. COMMANDS in hooks/scope.ts holds every word. The command.run switch ends in satisfies never, and the listing test runs each word. The command description, the usage text and commands/loadout.md come from this list.
  • Exhaustive code per listing kind. Code that differs by kind is a Record<Kind, ...> map or a switch with default: return kind satisfies never.
  • Safe group names. statusOf uses Object.hasOwn. Thus a group named constructor or __proto__ is not read as a built-in object key.
  • State rename. The effort atom is now effortState, because its shape changed.
  • New tests/contract.check.mjs. It checks these rules, and CI runs it:
    • no session state at module level
    • no two-way branch on the listing kind
    • no cast in tests other than as const
    • raw options are read only with readOptions(raw)
    • commands/loadout.md shows the words of COMMANDS
    • each trace event and field that a reader reads is in TraceLine
  • New tests/kit.ts. It has typed builders for hook inputs and fake replies.
  • Version. The version is now 0.6.4.

The type check still runs only on a contributor's machine, against .claude-plugin/types/. CI cannot make these types without a login, and the repo does not store a copy.

Test plan

  • claude plugin validate . passes, with the CLAUDE.md warning only.
  • claude plugin test .: 57 pass. This includes a new test for the race. It fails without the fix.
  • node tests/docs.check.mjs passes.
  • npx -p typescript@7.0.2 tsc -p tsconfig.json passes.
  • node tests/contract.check.mjs passes. Its self-test shows that each rule catches a bad example and accepts a good one.
  • node tests/smoke.e2e.mjs: all 8 checks pass.
  • /verify in real Claude Code:
    • /loadout, list, keep, unkeep, a bare keep and an unknown word give the correct output.
    • Only one tools classifier call runs.
    • A later prompt gets the note, and Claude loads and calls the hidden tool.
    • effort off and effort on work.
    • After the first request, the cache writes stay small.

🤖 Generated with Claude Code

Make wrong code fail in CI before review:

- Fix a race: two listings raised together share one tools decision.
  Each decision goes to session memory before the write, so later
  reads in the same dispatch see it.
- Keep the /loadout words in one COMMANDS record. The command switch
  ends with `satisfies never`, and the listing test runs each word.
- Write code per listing kind as a Record<Kind, ...> map or as an
  exhaustive switch.
- Use Object.hasOwn in statusOf, so a group named like an object key
  is not read as kept.
- Rename the effort atom to effortState, because its shape changed.
- Add tests/contract.check.mjs: module state, kind branches, casts in
  tests, option reads and the command doc. CI runs it.
- Type-check only against the engine types in .claude-plugin/types/,
  which git does not track. CI cannot run tsc: it has no login.
- Add tests/kit.ts with typed builders for hook inputs.
- Raise the version to 0.6.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gabe4coding
gabe4coding merged commit 3a5d911 into main Oct 13, 2026
5 checks passed
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.

2 participants