refactor(engine): extract the deny-hook pure logic into gittensory-engine - #5701
Conversation
…gine lib/deny-hooks.js's rule evaluator and lib/deny-hook-synthesis.js's proposal-synthesis logic were pure, deterministic business logic living directly in the self-host gittensory-miner package with no gittensory-engine counterpart -- unlike every other business-logic module (governor/chokepoint, portfolio-queue selection, worktree planning, ...), which already follow the "pure engine function + thin miner-lib wrapper" split. A hosted service wanting per-tenant deny-hook enforcement had to depend on the whole SQLite-backed miner package just to reach one pure function. Mirroring governor/chokepoint.ts's split: - New packages/gittensory-engine/src/miner/deny-hooks.ts holds the pure rule evaluator (DEFAULT_DENY_RULES, evaluateDenyHooks + types). - New packages/gittensory-engine/src/miner/deny-hook-synthesis.ts holds the pure proposal-synthesis logic (synthesizeDenyRuleProposals, aggregateBlockerHistory, resolveEffectiveDenyRules, setProposalStatuses, the normalize/canonicalize helpers, DEFAULT_SYNTHESIS_CONFIG + types). - The audit-stamp clock is now injected: the engine's buildPathProposal / synthesizeDenyRuleProposals take an explicit nowMs, so the extracted logic is fully deterministic given its inputs (no direct new Date()). - Both are re-exported through @loopover/engine's public barrel. - lib/deny-hooks.js is now a thin re-export; lib/deny-hook-synthesis.js keeps ONLY the SQLite store (initDenyHookSynthesisStore etc.) and a 2-arg synthesizeDenyRuleProposals wrapper that defaults nowMs to Date.now(), so its public API and behavior are unchanged. No behavior change: the four existing deny-hook/synthesis suites pass unchanged, plus a new regression test proving the extracted functions are callable from @loopover/engine alone (no miner/SQLite dependency) and that the injected clock makes synthesizedAt deterministic. Closes JSONbored#5667
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5701 +/- ##
=======================================
Coverage 95.02% 95.02%
=======================================
Files 577 578 +1
Lines 45978 45982 +4
Branches 14724 14779 +55
=======================================
+ Hits 43689 43694 +5
+ Misses 1530 1529 -1
Partials 759 759
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-14 05:36:23 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 6 non-blocking
Review context
Contributor next steps
Signal definitions
[BETA] Chat with GittensoryAsk Gittensory a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://gittensory.aethereal.dev/docs/gittensory-commands 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
What & why
Closes #5667.
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 "no IO, no globals, no Date/random") but lived directly in the self-hostgittensory-minerpackage with nogittensory-enginecounterpart — unlike every other business-logic module (governor/chokepoint, portfolio-queue selection, worktree planning, …), which already follow the "pure engine function + thin miner-lib wrapper" split. A hosted service wanting per-tenant deny-hook enforcement had to depend on the whole SQLite-backed miner package just to reach one pure function.How (mirrors
governor/chokepoint.ts's split)packages/gittensory-engine/src/miner/deny-hooks.ts— the pure rule evaluator (DEFAULT_DENY_RULES,evaluateDenyHooks+DenyRule/DenyVerdict/ProposedToolCalltypes).packages/gittensory-engine/src/miner/deny-hook-synthesis.ts— the pure proposal-synthesis logic (synthesizeDenyRuleProposals,aggregateBlockerHistory,resolveEffectiveDenyRules,setProposalStatuses, the normalize/canonicalize helpers,DEFAULT_SYNTHESIS_CONFIG+ types).buildPathProposal/synthesizeDenyRuleProposalstake an explicitnowMs, so the extracted logic is fully deterministic given its inputs (no directnew Date()), matchingiterate-loop.ts's own injected-clock convention.@loopover/engine's public barrel.lib/deny-hooks.jsis now a thin re-export;lib/deny-hook-synthesis.jskeeps ONLY the SQLite store (initDenyHookSynthesisStore, forge-scope migration,resolveDenyHookSynthesisDbPath, …) plus a 2-argsynthesizeDenyRuleProposalswrapper that defaultsnowMstoDate.now()— so its public API and behavior are unchanged.Acceptance criteria
gittensory-engine/src, re-exported through the barrellib/deny-hooks.js/deny-hook-synthesis.jsreduced to thin wrappers, existing behavior unchangedTests
miner-deny-hooks,miner-deny-hook-synthesis,deny-hooks-fixtures,miner-cli-deny-check) pass unchanged, and downstream consumers (miner-pretooluse-hook,miner-local-store-readme) stay green.test/unit/deny-hook-engine-extraction.test.ts— the required regression test: imports only from@loopover/engine(no miner/SQLite in the graph), asserts block/allow behavior, and proves the injected clock makesaudit.synthesizedAtdeterministic (synthesizeDenyRuleProposals(records, {}, 1_700_000_000_000)).npm run build --workspace @loopover/engine,tsc --noEmit,engine-parity:drift-check,manifest:drift-check, and the@loopover/engineunit suite all pass.