feat(claude-correction-plugin): port correction prompt hooks - #65
Open
systemfsoftware-maker wants to merge 3 commits into
Open
feat(claude-correction-plugin): port correction prompt hooks#65systemfsoftware-maker wants to merge 3 commits into
systemfsoftware-maker wants to merge 3 commits into
Conversation
Both UserPromptSubmit hooks lived only in a vendored scratch checkout as Bun scripts bound to one project, and they disagreed on malformed stdin: the frustration guard called JSON.parse unguarded and exited non-zero, while the correction capture swallowed it. Detection, hook I/O, and notice text sat in one file each, so detectFrustration had no test at all. They now ship as one tsdown-built Claude Code plugin. Detection is two pure kernels, the boundary is a decode-only Schema ACL that fails open to silence, and process stdio sits behind a driven adapter so each executor's Deps tag borrows the port's method types. Two Gherkin suites drive both hooks end to end over an in-memory terminal, one outline row per pattern
withoutQuotedSpans strips five kinds of quoted span, and the outline that proves a quoted outburst stays silent had rows for four of them. Deleting the single-quote branch left all 46 cases green, so nothing defended it. The new row fails against that deletion and passes against the branch
CI typechecks with the 7.0.2 Go compiler, which loads @effect/language-service and enforces deterministicKeys. Locally pnpm exec tsc resolves 5.9.3, which loads no plugin, so four bare tag keys passed the local gate and failed CI. Keys are now the package/path/decl form the diagnostic expects. Also drops vitest.setup.ts and the fast-check devDependency: both existed only to configure property tests that no longer ship
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two
UserPromptSubmithooks lived only in a vendored scratch checkout (.repos/WireBlast/.claude/) as Bun scripts bound to one project. They now ship from this monorepo as one tsdown-built Claude Code plugin.Both read a JSON payload from stdin, run a pure detector over the
promptfield, and either write a notice to stdout or write nothing. Neither ever blocks a prompt.correction-capturefrustration-guardWhat changed beyond relocation
The originals disagreed on malformed stdin:
frustration-guardcalledJSON.parseunguarded and exited non-zero, whilecorrection-captureswallowed it. Both now fail open — a truncated or non-JSON payload leaves the turn running rather than failing it. That is the only intentional behavioral change; detection is byte-identical to the originals, verified pattern by pattern and notice text character by character.detectFrustrationhad no test at all in WireBlast.Shape
Detection is two pure kernels (
string => Option<string>). The boundary is a decode-only Schema ACL. Process stdio sits behind one driven adapter, so each executor's Deps tag borrows the port's method types and the composition tests bind an in-memory terminal to the same tag. Two Gherkin suites drive both hooks end to end — 47 cases.Verification
pnpm checkexits 0 from the repo root. Both hooks were smoke-run as built artifacts over real stdin across five payload shapes (tripping, neutral, frustrated, non-JSON, empty) — correct output, exit 0 on all five.The suite was checked by deletion rather than by counting: dropping
THRESHOLDfrom 2 to 3 and removing one correction regex turned 29 of the then-46 cases red, and the removed regex was killed by exactly the row that depends on it. Review then found the one branch that deletion left green — the single-quote arm ofwithoutQuotedSpans— which is the second commit here.A gap worth knowing about
The third commit fixes four
Context.Tagkeys that CI rejected and the local gate accepted. The cause is not specific to this package:pnpm checklocally typechecks with TypeScript 5.9.3, which loads noplugins, while CI runs the 7.0.2 Go compiler with@effect/language-serviceattached. Every diagnostic in@systemfsoftware/tsconfig/effect— all ~80 of them,deterministicKeysincluded — is therefore invisible locally and enforced in CI. Anyone can land a violation on a green local gate. Reproduce with:Out of scope to fix here, but it is a hole in the gate rather than a quirk of this branch.
CI Failures Unresolved
Four checks are red. None is caused by this branch, and none is fixed here.
Mutation (packages/stryker-js/core)mainat this branch's base commit2f9f2cc2d4Mutation (omp/plugins/omp-claude-compat)Mutation (packages/effect-daemon-spec)build · typecheck · test · lint · guardseffect-schema-law—∀r_EachWitness_≡DischargesItsOwnArmexceeds the 120s test timeoutThe mutation trio was confirmed by reading run
31069782083onmain: the same three jobs, no others. This branch adds nostryker.config.json, so it is not in the mutation matrix at all.The
effect-schema-lawtimeout reproduces locally at CI settings (CI=true pnpm --filter @systemfsoftware/effect-schema-law test) in 260s against a 120s budget — over 2x, not marginal. It is anumRuns: 1000property test that only exceeds the budget under CI's run count. This branch changes zero files underpackages/effect-schema-law, and the new package is a leaf nothing imports; the test last changed in93eee7606f, before this branch existed.Deferred — needs a human call
Review filed two findings on false-positive cost that are faithfully ported behavior, not port defects, and are left as-is:
THRESHOLD = 2a single weight-2 category fires the intervention, so ordinary professional prompts trip it: "that is incorrect", "we don't need that", "let's move on"./again\?/imatches any question containing "again?" with no surrounding context.Retuning the threshold, narrowing a pattern, or gating the hooks against each other changes what the hooks decide — a product judgment about how loud this guard should be. Recorded as KTD9 in the plan.
Plan:
docs/plans/2026-08-06-001-feat-claude-correction-plugin-plan.md