Skip to content

feat(claude-correction-plugin): port correction prompt hooks - #65

Open
systemfsoftware-maker wants to merge 3 commits into
mainfrom
correction-plugin
Open

feat(claude-correction-plugin): port correction prompt hooks#65
systemfsoftware-maker wants to merge 3 commits into
mainfrom
correction-plugin

Conversation

@systemfsoftware-maker

@systemfsoftware-maker systemfsoftware-maker commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Two UserPromptSubmit hooks 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 prompt field, and either write a notice to stdout or write nothing. Neither ever blocks a prompt.

Hook Fires when Tells the agent to
correction-capture the prompt corrects the agent's previous work extract the durable rule, persist it, then apply the fix
frustration-guard the prompt reads as frustration or a competence challenge stop, re-read the last few messages, name the mistake, prove it saved the correction

What changed beyond relocation

The originals disagreed on malformed stdin: frustration-guard called JSON.parse unguarded and exited non-zero, while correction-capture swallowed 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.

detectFrustration had 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 check exits 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 THRESHOLD from 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 of withoutQuotedSpans — which is the second commit here.

A gap worth knowing about

The third commit fixes four Context.Tag keys that CI rejected and the local gate accepted. The cause is not specific to this package: pnpm check locally typechecks with TypeScript 5.9.3, which loads no plugins, while CI runs the 7.0.2 Go compiler with @effect/language-service attached. Every diagnostic in @systemfsoftware/tsconfig/effect — all ~80 of them, deterministicKeys included — is therefore invisible locally and enforced in CI. Anyone can land a violation on a green local gate. Reproduce with:

node_modules/.pnpm/@[email protected]/node_modules/@typescript/typescript-linux-x64/lib/tsc --noEmit

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.

Check Status
Mutation (packages/stryker-js/core) fails identically on main at this branch's base commit 2f9f2cc2d4
Mutation (omp/plugins/omp-claude-compat) same
Mutation (packages/effect-daemon-spec) same
build · typecheck · test · lint · guards effect-schema-law∀r_EachWitness_≡DischargesItsOwnArm exceeds the 120s test timeout

The mutation trio was confirmed by reading run 31069782083 on main: the same three jobs, no others. This branch adds no stryker.config.json, so it is not in the mutation matrix at all.

The effect-schema-law timeout 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 a numRuns: 1000 property test that only exceeds the budget under CI's run count. This branch changes zero files under packages/effect-schema-law, and the new package is a leaf nothing imports; the test last changed in 93eee7606f, 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:

  • At THRESHOLD = 2 a 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\?/i matches any question containing "again?" with no surrounding context.
  • A prompt like "that is incorrect, fix it" trips both hooks, injecting two different behavioral directives for one calm correction.

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

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
@ryanleecode ryanleecode changed the title feat(claude-correction-plugin): port wireblast prompt hooks feat(claude-correction-plugin): port correction prompt hooks Aug 6, 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.

2 participants