Context
lib/deny-hooks.js's rule evaluator and lib/deny-hook-synthesis.js's proposal-synthesis logic are pure, deterministic business logic (each self-documented as such — "no IO, no globals, no Date/random") but live directly in the self-host gittensory-miner package with no gittensory-engine counterpart. This is unlike every other business-logic module in the miner package — governor/chokepoint, portfolio-queue selection, worktree planning, attempt-log event shape, coding-agent drivers — which already follow a "pure engine function + thin miner-lib persistence wrapper" split. A hosted service wanting per-tenant deny-hook enforcement would have to depend on the whole SQLite-backed miner package just to reach one pure function, or duplicate the file.
Dependencies
None — independently shippable refactor.
Requirements
- Move
lib/deny-hooks.js's rule evaluator into gittensory-engine/src, mirroring governor/chokepoint.ts's own pure-function-plus-miner-wrapper split (the engine half computes a verdict with no IO; the existing miner lib file becomes a thin wrapper calling into it).
- Move
lib/deny-hook-synthesis.js's synthesizeDenyRuleProposals/aggregateBlockerHistory/resolveEffectiveDenyRules the same way.
- Inject the clock into
buildPathProposal's audit-stamp construction (an injectable nowMs, not a direct new Date() call) to match iterate-loop.ts's own convention, so the extracted logic is fully deterministic given its inputs.
- Re-export the extracted functions through
gittensory-engine's public barrel.
- Out of scope: no new deny-hook rule types, no schema change, no behavior change to existing rule evaluation or proposal synthesis.
Deliverables / Acceptance Criteria
Test Coverage Requirements
This PR must ship with full test coverage for every changed line and branch — the repo's Codecov patch gate requires 99%+ coverage. A pure refactor with no behavior change should carry over existing test coverage; add a regression test asserting the extracted functions are callable from @loopover/engine alone (no @loopover/miner/SQLite dependency).
Expected Outcome
Closes the one concrete "should be portable, isn't" gap in the self-host/hosted architecture seam: a hosted service can now reuse deny-hook enforcement without depending on the SQLite-backed self-host stack.
Links & Resources
Context
lib/deny-hooks.js's rule evaluator andlib/deny-hook-synthesis.js's proposal-synthesis logic are pure, deterministic business logic (each self-documented as such — "no IO, no globals, no Date/random") but live directly in the self-hostgittensory-minerpackage with nogittensory-enginecounterpart. This is unlike every other business-logic module in the miner package — governor/chokepoint, portfolio-queue selection, worktree planning, attempt-log event shape, coding-agent drivers — which already follow a "pure engine function + thin miner-lib persistence wrapper" split. A hosted service wanting per-tenant deny-hook enforcement would have to depend on the whole SQLite-backed miner package just to reach one pure function, or duplicate the file.Dependencies
None — independently shippable refactor.
Requirements
lib/deny-hooks.js's rule evaluator intogittensory-engine/src, mirroringgovernor/chokepoint.ts's own pure-function-plus-miner-wrapper split (the engine half computes a verdict with no IO; the existing miner lib file becomes a thin wrapper calling into it).lib/deny-hook-synthesis.js'ssynthesizeDenyRuleProposals/aggregateBlockerHistory/resolveEffectiveDenyRulesthe same way.buildPathProposal's audit-stamp construction (an injectablenowMs, not a directnew Date()call) to matchiterate-loop.ts's own convention, so the extracted logic is fully deterministic given its inputs.gittensory-engine's public barrel.Deliverables / Acceptance Criteria
gittensory-engine/src, re-exported through its public barrelpackages/gittensory-miner/lib/deny-hooks.jsanddeny-hook-synthesis.jsreduced to thin persistence wrappers calling the engine functions, with existing behavior unchangedTest Coverage Requirements
This PR must ship with full test coverage for every changed line and branch — the repo's Codecov patch gate requires 99%+ coverage. A pure refactor with no behavior change should carry over existing test coverage; add a regression test asserting the extracted functions are callable from
@loopover/enginealone (no@loopover/miner/SQLite dependency).Expected Outcome
Closes the one concrete "should be portable, isn't" gap in the self-host/hosted architecture seam: a hosted service can now reuse deny-hook enforcement without depending on the SQLite-backed self-host stack.
Links & Resources
packages/gittensory-engine/src/governor/chokepoint.ts(the pattern to mirror)packages/gittensory-miner/lib/deny-hooks.js,deny-hook-synthesis.js