opensquid 0.5.441 → 0.5.449
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/functions/arm_scope.d.ts +27 -0
- package/dist/functions/arm_scope.d.ts.map +1 -0
- package/dist/functions/arm_scope.js +52 -0
- package/dist/functions/arm_scope.js.map +1 -0
- package/dist/functions/index.d.ts +1 -0
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +1 -0
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/recall_pre_inject.d.ts.map +1 -1
- package/dist/functions/recall_pre_inject.js +12 -0
- package/dist/functions/recall_pre_inject.js.map +1 -1
- package/dist/rag/store_git.d.ts +23 -0
- package/dist/rag/store_git.d.ts.map +1 -0
- package/dist/rag/store_git.js +57 -0
- package/dist/rag/store_git.js.map +1 -0
- package/dist/runtime/bootstrap.d.ts.map +1 -1
- package/dist/runtime/bootstrap.js +2 -0
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/handoff/render.d.ts +5 -4
- package/dist/runtime/handoff/render.d.ts.map +1 -1
- package/dist/runtime/handoff/render.js +7 -7
- package/dist/runtime/handoff/render.js.map +1 -1
- package/dist/runtime/hooks/active_task_mirror.js +0 -0
- package/dist/runtime/hooks/apply_patch.js +0 -0
- package/dist/runtime/hooks/dispatch.js +0 -0
- package/dist/runtime/hooks/hook_output.js +0 -0
- package/dist/runtime/hooks/memory_reconcile.js +0 -0
- package/dist/runtime/hooks/new_project_detect.js +0 -0
- package/dist/runtime/hooks/profession_resolver.js +0 -0
- package/dist/runtime/hooks/scope_intent.js +0 -0
- package/dist/runtime/hooks/session-end.js +11 -0
- package/dist/runtime/hooks/session-end.js.map +1 -1
- package/dist/runtime/hooks/session_id.js +0 -0
- package/dist/runtime/hooks/session_liveness.js +0 -0
- package/dist/runtime/hooks/stop_drive.js +0 -0
- package/dist/runtime/hooks/stop_stream.js +0 -0
- package/dist/runtime/hooks/subagent_guard.js +0 -0
- package/dist/runtime/hooks/transcript.js +0 -0
- package/dist/runtime/hooks/transcript_tasks.js +0 -0
- package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
- package/dist/runtime/ralph/orchestrator.js +2 -1
- package/dist/runtime/ralph/orchestrator.js.map +1 -1
- package/dist/setup/cli/limits_state.d.ts.map +1 -1
- package/dist/setup/cli/limits_state.js +6 -40
- package/dist/setup/cli/limits_state.js.map +1 -1
- package/dist/setup/cli/pack_walk.d.ts +32 -0
- package/dist/setup/cli/pack_walk.d.ts.map +1 -0
- package/dist/setup/cli/pack_walk.js +76 -0
- package/dist/setup/cli/pack_walk.js.map +1 -0
- package/dist/setup/cli/permissions_state.d.ts.map +1 -1
- package/dist/setup/cli/permissions_state.js +6 -37
- package/dist/setup/cli/permissions_state.js.map +1 -1
- package/dist/setup/cli/triggers_state.d.ts.map +1 -1
- package/dist/setup/cli/triggers_state.js +3 -29
- package/dist/setup/cli/triggers_state.js.map +1 -1
- package/dist/workgraph/events.d.ts.map +1 -1
- package/dist/workgraph/events.js +10 -0
- package/dist/workgraph/events.js.map +1 -1
- package/dist/workgraph/store.d.ts.map +1 -1
- package/dist/workgraph/store.js +5 -0
- package/dist/workgraph/store.js.map +1 -1
- package/dist/workgraph/types.d.ts +2 -1
- package/dist/workgraph/types.d.ts.map +1 -1
- package/docs/ARCHITECTURE.md +268 -0
- package/docs/pack-runtime.md +15 -12
- package/docs/skill-grammar-guide.md +4 -4
- package/package.json +5 -3
- package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
- package/dist/anti-drift/evaluator.d.ts +0 -88
- package/dist/anti-drift/evaluator.d.ts.map +0 -1
- package/dist/anti-drift/evaluator.js +0 -417
- package/dist/anti-drift/evaluator.js.map +0 -1
- package/dist/anti-drift/evaluator.test.js +0 -78
- package/dist/anti-drift/rules.d.ts +0 -80
- package/dist/anti-drift/rules.d.ts.map +0 -1
- package/dist/anti-drift/rules.js +0 -368
- package/dist/anti-drift/rules.js.map +0 -1
- package/dist/anti-drift/rules.test.js +0 -213
- package/dist/anti-drift/state.d.ts +0 -107
- package/dist/anti-drift/state.d.ts.map +0 -1
- package/dist/anti-drift/state.js +0 -177
- package/dist/anti-drift/state.js.map +0 -1
- package/dist/anti-drift/state.test.js +0 -120
- package/dist/chat/adapters/discord.d.ts +0 -41
- package/dist/chat/adapters/discord.d.ts.map +0 -1
- package/dist/chat/adapters/discord.js +0 -176
- package/dist/chat/adapters/discord.js.map +0 -1
- package/dist/chat/adapters/discord.test.js +0 -25
- package/dist/chat/adapters/slack.d.ts +0 -43
- package/dist/chat/adapters/slack.d.ts.map +0 -1
- package/dist/chat/adapters/slack.js +0 -172
- package/dist/chat/adapters/slack.js.map +0 -1
- package/dist/chat/adapters/slack.test.js +0 -30
- package/dist/chat/adapters/telegram.d.ts +0 -148
- package/dist/chat/adapters/telegram.d.ts.map +0 -1
- package/dist/chat/adapters/telegram.js +0 -498
- package/dist/chat/adapters/telegram.js.map +0 -1
- package/dist/chat/adapters/telegram.test.js +0 -94
- package/dist/chat/config.d.ts +0 -98
- package/dist/chat/config.d.ts.map +0 -1
- package/dist/chat/config.js +0 -185
- package/dist/chat/config.js.map +0 -1
- package/dist/chat/daemon/active-project.d.ts +0 -17
- package/dist/chat/daemon/active-project.d.ts.map +0 -1
- package/dist/chat/daemon/active-project.js +0 -23
- package/dist/chat/daemon/active-project.js.map +0 -1
- package/dist/chat/daemon/autospawn.d.ts +0 -40
- package/dist/chat/daemon/autospawn.d.ts.map +0 -1
- package/dist/chat/daemon/autospawn.js +0 -129
- package/dist/chat/daemon/autospawn.js.map +0 -1
- package/dist/chat/daemon/autospawn.test.js +0 -112
- package/dist/chat/daemon/cli.d.ts +0 -18
- package/dist/chat/daemon/cli.d.ts.map +0 -1
- package/dist/chat/daemon/cli.js +0 -71
- package/dist/chat/daemon/cli.js.map +0 -1
- package/dist/chat/daemon/collisions.js +0 -384
- package/dist/chat/daemon/health-check.d.ts +0 -69
- package/dist/chat/daemon/health-check.d.ts.map +0 -1
- package/dist/chat/daemon/health-check.js +0 -112
- package/dist/chat/daemon/health-check.js.map +0 -1
- package/dist/chat/daemon/inbox-read.d.ts +0 -35
- package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
- package/dist/chat/daemon/inbox-read.js +0 -75
- package/dist/chat/daemon/inbox-read.js.map +0 -1
- package/dist/chat/daemon/inbox-read.test.js +0 -97
- package/dist/chat/daemon/inbox.d.ts +0 -63
- package/dist/chat/daemon/inbox.d.ts.map +0 -1
- package/dist/chat/daemon/inbox.js +0 -56
- package/dist/chat/daemon/inbox.js.map +0 -1
- package/dist/chat/daemon/inbox.test.js +0 -110
- package/dist/chat/daemon/lifecycle.d.ts +0 -71
- package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
- package/dist/chat/daemon/lifecycle.js +0 -221
- package/dist/chat/daemon/lifecycle.js.map +0 -1
- package/dist/chat/daemon/lifecycle.test.js +0 -163
- package/dist/chat/daemon/protocol.d.ts +0 -107
- package/dist/chat/daemon/protocol.d.ts.map +0 -1
- package/dist/chat/daemon/protocol.js +0 -54
- package/dist/chat/daemon/protocol.js.map +0 -1
- package/dist/chat/daemon/routing.d.ts +0 -140
- package/dist/chat/daemon/routing.d.ts.map +0 -1
- package/dist/chat/daemon/routing.js +0 -198
- package/dist/chat/daemon/routing.js.map +0 -1
- package/dist/chat/daemon/routing.test.js +0 -259
- package/dist/chat/daemon/rpc-client.d.ts +0 -45
- package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-client.js +0 -133
- package/dist/chat/daemon/rpc-client.js.map +0 -1
- package/dist/chat/daemon/rpc-server.d.ts +0 -39
- package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-server.js +0 -385
- package/dist/chat/daemon/rpc-server.js.map +0 -1
- package/dist/chat/daemon/rpc.test.js +0 -177
- package/dist/chat/daemon/subscribers.js +0 -257
- package/dist/chat/daemon/worker.d.ts +0 -27
- package/dist/chat/daemon/worker.d.ts.map +0 -1
- package/dist/chat/daemon/worker.js +0 -313
- package/dist/chat/daemon/worker.js.map +0 -1
- package/dist/chat/daemon/workspace-topic.js +0 -324
- package/dist/chat/env-token.d.ts +0 -60
- package/dist/chat/env-token.d.ts.map +0 -1
- package/dist/chat/env-token.js +0 -137
- package/dist/chat/env-token.js.map +0 -1
- package/dist/chat/env-token.test.js +0 -160
- package/dist/chat/factory.d.ts +0 -30
- package/dist/chat/factory.d.ts.map +0 -1
- package/dist/chat/factory.js +0 -50
- package/dist/chat/factory.js.map +0 -1
- package/dist/chat/factory.test.js +0 -55
- package/dist/chat/gateway.d.ts +0 -176
- package/dist/chat/gateway.d.ts.map +0 -1
- package/dist/chat/gateway.js +0 -146
- package/dist/chat/gateway.js.map +0 -1
- package/dist/chat/gateway.test.js +0 -192
- package/dist/claude-md.d.ts +0 -39
- package/dist/claude-md.d.ts.map +0 -1
- package/dist/claude-md.js +0 -113
- package/dist/claude-md.js.map +0 -1
- package/dist/claude-md.test.js +0 -91
- package/dist/codex/activate.d.ts +0 -66
- package/dist/codex/activate.d.ts.map +0 -1
- package/dist/codex/activate.js +0 -329
- package/dist/codex/activate.js.map +0 -1
- package/dist/codex/activate.test.js +0 -229
- package/dist/codex/bundled-default/bundled-default.test.js +0 -161
- package/dist/codex/cli-publish.test.js +0 -133
- package/dist/codex/cli.d.ts +0 -35
- package/dist/codex/cli.d.ts.map +0 -1
- package/dist/codex/cli.js +0 -554
- package/dist/codex/cli.js.map +0 -1
- package/dist/codex/cli.test.js +0 -277
- package/dist/codex/import-skill-md.d.ts +0 -53
- package/dist/codex/import-skill-md.d.ts.map +0 -1
- package/dist/codex/import-skill-md.js +0 -236
- package/dist/codex/import-skill-md.js.map +0 -1
- package/dist/codex/import-skill-md.test.js +0 -225
- package/dist/codex/loader.d.ts +0 -27
- package/dist/codex/loader.d.ts.map +0 -1
- package/dist/codex/loader.js +0 -86
- package/dist/codex/loader.js.map +0 -1
- package/dist/codex/loader.test.js +0 -75
- package/dist/codex/parse.d.ts +0 -28
- package/dist/codex/parse.d.ts.map +0 -1
- package/dist/codex/parse.js +0 -309
- package/dist/codex/parse.js.map +0 -1
- package/dist/codex/parse.test.js +0 -241
- package/dist/codex/store.d.ts +0 -87
- package/dist/codex/store.d.ts.map +0 -1
- package/dist/codex/store.js +0 -205
- package/dist/codex/store.js.map +0 -1
- package/dist/codex/store.test.js +0 -242
- package/dist/codex/types.d.ts +0 -398
- package/dist/codex/types.d.ts.map +0 -1
- package/dist/codex/types.js +0 -21
- package/dist/codex/types.js.map +0 -1
- package/dist/config.d.ts +0 -53
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -202
- package/dist/config.js.map +0 -1
- package/dist/config.test.js +0 -117
- package/dist/engine/cli.d.ts +0 -14
- package/dist/engine/cli.d.ts.map +0 -1
- package/dist/engine/cli.js +0 -171
- package/dist/engine/cli.js.map +0 -1
- package/dist/engine/client.d.ts +0 -219
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js +0 -312
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/config.d.ts +0 -62
- package/dist/engine/config.d.ts.map +0 -1
- package/dist/engine/config.js +0 -223
- package/dist/engine/config.js.map +0 -1
- package/dist/engine/index.d.ts +0 -17
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js +0 -16
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/resolver.d.ts +0 -62
- package/dist/engine/resolver.d.ts.map +0 -1
- package/dist/engine/resolver.js +0 -103
- package/dist/engine/resolver.js.map +0 -1
- package/dist/engine/singleton.d.ts +0 -95
- package/dist/engine/singleton.d.ts.map +0 -1
- package/dist/engine/singleton.js +0 -325
- package/dist/engine/singleton.js.map +0 -1
- package/dist/engine/types.d.ts +0 -402
- package/dist/engine/types.d.ts.map +0 -1
- package/dist/engine/types.js +0 -22
- package/dist/engine/types.js.map +0 -1
- package/dist/engine-binary-resolver.js +0 -110
- package/dist/engine-binary-resolver.test.js +0 -61
- package/dist/engine-cli.js +0 -60
- package/dist/engine-client.js +0 -301
- package/dist/engine-client.test.js +0 -118
- package/dist/functions/chain_state.d.ts +0 -51
- package/dist/functions/chain_state.d.ts.map +0 -1
- package/dist/functions/chain_state.js +0 -59
- package/dist/functions/chain_state.js.map +0 -1
- package/dist/hooks/drift-catalog.d.ts +0 -68
- package/dist/hooks/drift-catalog.d.ts.map +0 -1
- package/dist/hooks/drift-catalog.js +0 -184
- package/dist/hooks/drift-catalog.js.map +0 -1
- package/dist/hooks/drift-catalog.test.js +0 -154
- package/dist/hooks/drift-patterns.d.ts +0 -110
- package/dist/hooks/drift-patterns.d.ts.map +0 -1
- package/dist/hooks/drift-patterns.js +0 -289
- package/dist/hooks/drift-patterns.js.map +0 -1
- package/dist/hooks/drift-patterns.test.js +0 -325
- package/dist/hooks/engine-vocab-gate.d.ts +0 -108
- package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
- package/dist/hooks/engine-vocab-gate.js +0 -225
- package/dist/hooks/engine-vocab-gate.js.map +0 -1
- package/dist/hooks/engine-vocab-gate.test.js +0 -170
- package/dist/hooks/heartbeat.d.ts +0 -107
- package/dist/hooks/heartbeat.d.ts.map +0 -1
- package/dist/hooks/heartbeat.js +0 -316
- package/dist/hooks/heartbeat.js.map +0 -1
- package/dist/hooks/heartbeat.test.js +0 -393
- package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
- package/dist/hooks/honesty-ledger.d.ts +0 -123
- package/dist/hooks/honesty-ledger.d.ts.map +0 -1
- package/dist/hooks/honesty-ledger.js +0 -226
- package/dist/hooks/honesty-ledger.js.map +0 -1
- package/dist/hooks/honesty-ledger.test.js +0 -466
- package/dist/hooks/inline-report-check.d.ts +0 -63
- package/dist/hooks/inline-report-check.d.ts.map +0 -1
- package/dist/hooks/inline-report-check.js +0 -88
- package/dist/hooks/inline-report-check.js.map +0 -1
- package/dist/hooks/inline-report-check.test.js +0 -96
- package/dist/hooks/pre-tool-use.d.ts +0 -62
- package/dist/hooks/pre-tool-use.d.ts.map +0 -1
- package/dist/hooks/pre-tool-use.js +0 -342
- package/dist/hooks/pre-tool-use.js.map +0 -1
- package/dist/hooks/pre-tool-use.test.js +0 -134
- package/dist/hooks/session-end.d.ts +0 -15
- package/dist/hooks/session-end.d.ts.map +0 -1
- package/dist/hooks/session-end.js +0 -60
- package/dist/hooks/session-end.js.map +0 -1
- package/dist/hooks/session-end.test.js +0 -52
- package/dist/hooks/stop.d.ts +0 -35
- package/dist/hooks/stop.d.ts.map +0 -1
- package/dist/hooks/stop.js +0 -136
- package/dist/hooks/stop.js.map +0 -1
- package/dist/hooks/transcript-active-task.test.js +0 -342
- package/dist/hooks/transcript.d.ts +0 -26
- package/dist/hooks/transcript.d.ts.map +0 -1
- package/dist/hooks/transcript.js +0 -266
- package/dist/hooks/transcript.js.map +0 -1
- package/dist/hooks/transcript.test.js +0 -103
- package/dist/hooks/user-prompt-submit.d.ts +0 -74
- package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
- package/dist/hooks/user-prompt-submit.js +0 -256
- package/dist/hooks/user-prompt-submit.js.map +0 -1
- package/dist/hooks/user-prompt-submit.test.js +0 -118
- package/dist/hooks/versioning-gate.d.ts +0 -101
- package/dist/hooks/versioning-gate.d.ts.map +0 -1
- package/dist/hooks/versioning-gate.js +0 -245
- package/dist/hooks/versioning-gate.js.map +0 -1
- package/dist/hooks/versioning-gate.test.js +0 -368
- package/dist/hooks/workflow-gate.d.ts +0 -64
- package/dist/hooks/workflow-gate.d.ts.map +0 -1
- package/dist/hooks/workflow-gate.js +0 -152
- package/dist/hooks/workflow-gate.js.map +0 -1
- package/dist/hooks/workflow-gate.test.js +0 -197
- package/dist/hooks-cli.d.ts +0 -25
- package/dist/hooks-cli.d.ts.map +0 -1
- package/dist/hooks-cli.js +0 -286
- package/dist/hooks-cli.js.map +0 -1
- package/dist/hooks-cli.test.js +0 -148
- package/dist/origin.d.ts +0 -16
- package/dist/origin.d.ts.map +0 -1
- package/dist/origin.js +0 -92
- package/dist/origin.js.map +0 -1
- package/dist/packs/seed_lessons_ingest.d.ts +0 -30
- package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
- package/dist/packs/seed_lessons_ingest.js +0 -107
- package/dist/packs/seed_lessons_ingest.js.map +0 -1
- package/dist/project-cli.d.ts +0 -7
- package/dist/project-cli.d.ts.map +0 -1
- package/dist/project-cli.js +0 -145
- package/dist/project-cli.js.map +0 -1
- package/dist/project.d.ts +0 -127
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js +0 -281
- package/dist/project.js.map +0 -1
- package/dist/project.test.js +0 -287
- package/dist/rag/backends/loop_engine.d.ts +0 -61
- package/dist/rag/backends/loop_engine.d.ts.map +0 -1
- package/dist/rag/backends/loop_engine.js +0 -160
- package/dist/rag/backends/loop_engine.js.map +0 -1
- package/dist/recall.d.ts +0 -82
- package/dist/recall.d.ts.map +0 -1
- package/dist/recall.js +0 -81
- package/dist/recall.js.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
- package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.js +0 -251
- package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
- package/dist/runtime/chain_state.d.ts +0 -124
- package/dist/runtime/chain_state.d.ts.map +0 -1
- package/dist/runtime/chain_state.js +0 -189
- package/dist/runtime/chain_state.js.map +0 -1
- package/dist/runtime/hooks/permission_decision.d.ts +0 -34
- package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
- package/dist/runtime/hooks/permission_decision.js +0 -39
- package/dist/runtime/hooks/permission_decision.js.map +0 -1
- package/dist/runtime/workflow_fsm.d.ts +0 -21
- package/dist/runtime/workflow_fsm.d.ts.map +0 -1
- package/dist/runtime/workflow_fsm.js +0 -25
- package/dist/runtime/workflow_fsm.js.map +0 -1
- package/dist/runtime/workflow_map.d.ts +0 -26
- package/dist/runtime/workflow_map.d.ts.map +0 -1
- package/dist/runtime/workflow_map.js +0 -38
- package/dist/runtime/workflow_map.js.map +0 -1
- package/dist/scope.d.ts +0 -48
- package/dist/scope.d.ts.map +0 -1
- package/dist/scope.js +0 -111
- package/dist/scope.js.map +0 -1
- package/dist/setup/cli/topic_create_step.d.ts +0 -84
- package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
- package/dist/setup/cli/topic_create_step.js +0 -213
- package/dist/setup/cli/topic_create_step.js.map +0 -1
- package/dist/system-export.d.ts +0 -65
- package/dist/system-export.d.ts.map +0 -1
- package/dist/system-export.js +0 -194
- package/dist/system-export.js.map +0 -1
- package/dist/utterance/classifier.d.ts +0 -53
- package/dist/utterance/classifier.d.ts.map +0 -1
- package/dist/utterance/classifier.js +0 -184
- package/dist/utterance/classifier.js.map +0 -1
- package/dist/utterance/classifier.test.js +0 -147
package/dist/anti-drift/rules.js
DELETED
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Anti-drift declarative rule list (0.7.33 — unified-evaluator track).
|
|
3
|
-
*
|
|
4
|
-
* Per loop/docs/opensquid-anti-drift-unified-evaluator-design.md: one
|
|
5
|
-
* declarative `Rule[]` list replaces the per-file hook handlers in
|
|
6
|
-
* src/hooks/*. Each rule:
|
|
7
|
-
*
|
|
8
|
-
* - `id` stable identifier
|
|
9
|
-
* - `catches` which D-entry (or "preexisting") this rule covers
|
|
10
|
-
* - `hook` which lifecycle event this rule binds to
|
|
11
|
-
* - `when` (cheap, synchronous) gate — short-circuits before
|
|
12
|
-
* expensive `check` work
|
|
13
|
-
* - `check` (async) returns a Verdict
|
|
14
|
-
* - `bypass` optional env var that emergency-disables the rule
|
|
15
|
-
* - `rationale` one-line agent-facing reason (used in error messages)
|
|
16
|
-
*
|
|
17
|
-
* Today the check functions DELEGATE to existing src/hooks/* helpers
|
|
18
|
-
* (engine-vocab-gate, versioning-gate, workflow-gate, drift-patterns,
|
|
19
|
-
* inline-report-check, etc.) so this patch ships the declarative
|
|
20
|
-
* SURFACE without re-implementing every gate. The 0.7.35 cutover
|
|
21
|
-
* moves the helper bodies into src/anti-drift/* and deletes the old
|
|
22
|
-
* per-hook files.
|
|
23
|
-
*
|
|
24
|
-
* The evaluator (0.7.34) walks this list at each hook event, runs
|
|
25
|
-
* applicable rules, aggregates verdicts (most-restrictive wins for
|
|
26
|
-
* PreToolUse permissions; all surfaces accumulate at Stop/UPS).
|
|
27
|
-
*/
|
|
28
|
-
import { findDrifts, decide as decideDriftPatterns } from "../hooks/drift-patterns.js";
|
|
29
|
-
import { evaluateEngineVocabGate } from "../hooks/engine-vocab-gate.js";
|
|
30
|
-
import { isRecallRequired, clearRecallRequired } from "../hooks/heartbeat.js";
|
|
31
|
-
import { checkInlineReportFormat } from "../hooks/inline-report-check.js";
|
|
32
|
-
import { checkChatSendReportFormat } from "../hooks/pre-tool-use.js";
|
|
33
|
-
import { evaluateVersioningGate } from "../hooks/versioning-gate.js";
|
|
34
|
-
import { evaluateWorkflowGate } from "../hooks/workflow-gate.js";
|
|
35
|
-
// =====================================================================
|
|
36
|
-
// Helpers shared across rules
|
|
37
|
-
// =====================================================================
|
|
38
|
-
function getCommand(ctx) {
|
|
39
|
-
const v = ctx.toolInput?.command;
|
|
40
|
-
return typeof v === "string" ? v : null;
|
|
41
|
-
}
|
|
42
|
-
function isBypassed(rule) {
|
|
43
|
-
if (!rule.bypass)
|
|
44
|
-
return false;
|
|
45
|
-
return process.env[rule.bypass] === "1";
|
|
46
|
-
}
|
|
47
|
-
/** Pass-through verdict factory keeps the rule list legible. */
|
|
48
|
-
const PASS = { kind: "pass" };
|
|
49
|
-
// =====================================================================
|
|
50
|
-
// The rule list (18 entries — matches the design doc)
|
|
51
|
-
// =====================================================================
|
|
52
|
-
export const RULES = [
|
|
53
|
-
// ---------- Hard blocks (PreToolUse) ----------
|
|
54
|
-
{
|
|
55
|
-
id: "active-task-required",
|
|
56
|
-
catches: "D1",
|
|
57
|
-
hook: "PreToolUse",
|
|
58
|
-
bypass: "OPENSQUID_SKIP_ACTIVE_TASK_GATE",
|
|
59
|
-
rationale: "log_phase / chat_send without an in_progress task means the workflow-gate has nothing to validate against — exactly the D1 headline drift.",
|
|
60
|
-
when: (ctx) => ctx.toolName === "mcp__opensquid__log_phase" || ctx.toolName === "mcp__opensquid__chat_send",
|
|
61
|
-
check: async (ctx) => {
|
|
62
|
-
// Delegated to the existing readActiveTaskId for now (transcript
|
|
63
|
-
// parsing). 0.7.35 cutover swaps this to state.readActiveTask.
|
|
64
|
-
if (!ctx.transcriptPath)
|
|
65
|
-
return PASS;
|
|
66
|
-
const { readActiveTaskId } = await import("../hooks/transcript.js");
|
|
67
|
-
try {
|
|
68
|
-
const id = await readActiveTaskId(ctx.transcriptPath);
|
|
69
|
-
if (id)
|
|
70
|
-
return PASS;
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
return PASS;
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
kind: "block",
|
|
77
|
-
message: `🦑 [opensquid] ${ctx.toolName} called without an in_progress task. Call TaskCreate first.`,
|
|
78
|
-
};
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
id: "never-amend",
|
|
83
|
-
catches: "preexisting",
|
|
84
|
-
hook: "PreToolUse",
|
|
85
|
-
rationale: "git commit --amend hides iteration history per feedback_auto_commit; use a follow-up commit instead.",
|
|
86
|
-
when: (ctx) => ctx.toolName === "Bash",
|
|
87
|
-
check: async (ctx) => deriveDriftPatternVerdict(ctx, "never-amend"),
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
id: "no-implicit-push",
|
|
91
|
-
catches: "preexisting",
|
|
92
|
-
hook: "PreToolUse",
|
|
93
|
-
bypass: "OPENSQUID_SKIP_DRIFT",
|
|
94
|
-
rationale: "git push requires explicit user authorization; commits stay local by default.",
|
|
95
|
-
when: (ctx) => ctx.toolName === "Bash",
|
|
96
|
-
check: async (ctx) => deriveDriftPatternVerdict(ctx, "no-implicit-push"),
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
id: "no-force-push-main",
|
|
100
|
-
catches: "preexisting",
|
|
101
|
-
hook: "PreToolUse",
|
|
102
|
-
rationale: "force-push to main/master is destructive and bypasses normal authorization.",
|
|
103
|
-
when: (ctx) => ctx.toolName === "Bash",
|
|
104
|
-
check: async (ctx) => deriveDriftPatternVerdict(ctx, "no-force-push-main"),
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
id: "engine-vocab-leak",
|
|
108
|
-
catches: "D6",
|
|
109
|
-
hook: "PreToolUse",
|
|
110
|
-
bypass: "OPENSQUID_SKIP_ENGINE_VOCAB_GATE",
|
|
111
|
-
rationale: "engine artifacts must not name consumer products per feedback_engine_vocabulary_discipline.",
|
|
112
|
-
when: (ctx) => ctx.toolName === "Bash" && /\bgit\s+commit\b/.test(getCommand(ctx) ?? ""),
|
|
113
|
-
check: async (ctx) => {
|
|
114
|
-
const r = await evaluateEngineVocabGate({
|
|
115
|
-
cwd: ctx.cwd,
|
|
116
|
-
bashCommand: getCommand(ctx) ?? undefined,
|
|
117
|
-
});
|
|
118
|
-
if (r.block)
|
|
119
|
-
return { kind: "block", message: r.stderr };
|
|
120
|
-
if (r.stderr)
|
|
121
|
-
return { kind: "warn", message: r.stderr };
|
|
122
|
-
return PASS;
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
id: "versioning-bump-required",
|
|
127
|
-
catches: "D5+preexisting",
|
|
128
|
-
hook: "PreToolUse",
|
|
129
|
-
bypass: "OPENSQUID_SKIP_VERSION_GATE",
|
|
130
|
-
rationale: "every src commit ships a matching patch bump per feedback_pre1_versioning v4 PATCH-ONLY rule.",
|
|
131
|
-
when: (ctx) => ctx.toolName === "Bash" && /\bgit\s+commit\b/.test(getCommand(ctx) ?? ""),
|
|
132
|
-
check: async (ctx) => {
|
|
133
|
-
const r = await evaluateVersioningGate({ cwd: ctx.cwd });
|
|
134
|
-
if (r.block)
|
|
135
|
-
return { kind: "block", message: r.stderr };
|
|
136
|
-
if (r.stderr)
|
|
137
|
-
return { kind: "warn", message: r.stderr };
|
|
138
|
-
return PASS;
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
id: "workflow-phases-required",
|
|
143
|
-
catches: "preexisting",
|
|
144
|
-
hook: "PreToolUse",
|
|
145
|
-
bypass: "OPENSQUID_SKIP_WORKFLOW_GATE",
|
|
146
|
-
rationale: "git commit requires required phases logged for the active task per feedback_workflow_cycle.",
|
|
147
|
-
when: (ctx) => ctx.toolName === "Bash" && /\bgit\s+commit\b/.test(getCommand(ctx) ?? ""),
|
|
148
|
-
check: async (ctx) => {
|
|
149
|
-
const r = await evaluateWorkflowGate({ transcriptPath: ctx.transcriptPath });
|
|
150
|
-
if (r.block)
|
|
151
|
-
return { kind: "block", message: r.stderr };
|
|
152
|
-
if (r.stderr)
|
|
153
|
-
return { kind: "warn", message: r.stderr };
|
|
154
|
-
return PASS;
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
id: "seven-phase-report-format",
|
|
159
|
-
catches: "D3",
|
|
160
|
-
hook: "PreToolUse",
|
|
161
|
-
rationale: "chat_send body matching the task-report marker must include the PHASES heading per feedback_telegram_reports.",
|
|
162
|
-
when: (ctx) => ctx.toolName === "mcp__opensquid__chat_send",
|
|
163
|
-
check: async (ctx) => {
|
|
164
|
-
const msg = checkChatSendReportFormat({
|
|
165
|
-
tool: ctx.toolName ?? "",
|
|
166
|
-
input: ctx.toolInput ?? {},
|
|
167
|
-
});
|
|
168
|
-
return msg ? { kind: "warn", message: msg } : PASS;
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
id: "heartbeat-recall-required",
|
|
173
|
-
catches: "D7",
|
|
174
|
-
hook: "PreToolUse",
|
|
175
|
-
bypass: "OPENSQUID_SKIP_RECALL_GATE",
|
|
176
|
-
rationale: "after a heartbeat surfaces, the next mcp__opensquid__* call must be recall to re-anchor.",
|
|
177
|
-
when: (ctx) => typeof ctx.toolName === "string" && ctx.toolName.startsWith("mcp__opensquid__"),
|
|
178
|
-
check: async (ctx) => {
|
|
179
|
-
if (!ctx.sessionId)
|
|
180
|
-
return PASS;
|
|
181
|
-
const required = await isRecallRequired(ctx.sessionId);
|
|
182
|
-
if (!required)
|
|
183
|
-
return PASS;
|
|
184
|
-
if (ctx.toolName === "mcp__opensquid__recall") {
|
|
185
|
-
await clearRecallRequired(ctx.sessionId);
|
|
186
|
-
return PASS;
|
|
187
|
-
}
|
|
188
|
-
return {
|
|
189
|
-
kind: "block",
|
|
190
|
-
message: `🦑 [opensquid recall-gate] ${ctx.toolName} blocked — heartbeat surfaced this turn; call mcp__opensquid__recall first.`,
|
|
191
|
-
};
|
|
192
|
-
},
|
|
193
|
-
},
|
|
194
|
-
// ---------- Soft warns (PreToolUse) ----------
|
|
195
|
-
{
|
|
196
|
-
id: "telegram-redirect-report",
|
|
197
|
-
catches: "D2",
|
|
198
|
-
hook: "PreToolUse",
|
|
199
|
-
rationale: "task-completion reports go via mcp__opensquid__chat_send to the project report_channel, not plugin:telegram reply (DM).",
|
|
200
|
-
when: (ctx) => ctx.toolName === "mcp__plugin_telegram_telegram__reply",
|
|
201
|
-
check: async (ctx) => deriveDriftPatternVerdict(ctx, "telegram-redirect-report"),
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
id: "bundled-commit",
|
|
205
|
-
catches: "D4",
|
|
206
|
-
hook: "PreToolUse",
|
|
207
|
-
rationale: "prefer multiple small logical commits over one large catchall per feedback_auto_commit.",
|
|
208
|
-
when: (ctx) => ctx.toolName === "Bash",
|
|
209
|
-
check: async (ctx) => deriveDriftPatternVerdict(ctx, "bundled-commit"),
|
|
210
|
-
},
|
|
211
|
-
// ---------- Stop hook ----------
|
|
212
|
-
{
|
|
213
|
-
id: "inline-report-missing-phases",
|
|
214
|
-
catches: "D3-inline",
|
|
215
|
-
hook: "Stop",
|
|
216
|
-
rationale: "in-session status reports must include the PHASES heading too, not just chat_send — feedback_telegram_reports applies to both surfaces.",
|
|
217
|
-
when: (ctx) => Boolean(ctx.assistantText),
|
|
218
|
-
check: async (ctx) => {
|
|
219
|
-
const v = checkInlineReportFormat(ctx.assistantText ?? "");
|
|
220
|
-
if (!v)
|
|
221
|
-
return PASS;
|
|
222
|
-
return {
|
|
223
|
-
kind: "surface",
|
|
224
|
-
message: `inline-report-missing-phases: message shape suggests a completion report (version_refs=${v.signals.version_refs}, commit_hashes=${v.signals.hash_refs}) but PHASES heading missing.`,
|
|
225
|
-
};
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
id: "false-stop-haiku",
|
|
230
|
-
catches: "D9",
|
|
231
|
-
hook: "Stop",
|
|
232
|
-
rationale: "trailing pause-prompts violate feedback_full_automation_mode; Claude Code's native prompt-type Stop hook (installed via hooks-cli) handles the Haiku classification + re-prompt loop.",
|
|
233
|
-
when: () => false, // implemented as a `type: "prompt"` settings.json entry, not via this Rule.check path
|
|
234
|
-
check: async () => PASS,
|
|
235
|
-
},
|
|
236
|
-
// ---------- UserPromptSubmit ----------
|
|
237
|
-
{
|
|
238
|
-
id: "multi-task-plan-injection",
|
|
239
|
-
catches: "D8",
|
|
240
|
-
hook: "UserPromptSubmit",
|
|
241
|
-
rationale: "when the user lists multiple task identifiers, mirror back the parsed plan before executing to catch a misread per feedback_user_words_have_top_weight.",
|
|
242
|
-
when: (ctx) => typeof ctx.userPrompt === "string",
|
|
243
|
-
check: async (ctx) => {
|
|
244
|
-
const { detectMultiTaskDirective } = await import("../hooks/user-prompt-submit.js");
|
|
245
|
-
const msg = detectMultiTaskDirective(ctx.userPrompt ?? "");
|
|
246
|
-
return msg ? { kind: "surface", message: msg } : PASS;
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
// ---------- SessionEnd ----------
|
|
250
|
-
{
|
|
251
|
-
id: "drift-catalog-auto",
|
|
252
|
-
catches: "D10",
|
|
253
|
-
hook: "SessionEnd",
|
|
254
|
-
rationale: "auto-catalog drift markers (user corrections, rule citations, mea culpas) so the dogfood proof writes itself.",
|
|
255
|
-
when: () => true,
|
|
256
|
-
check: async (ctx) => {
|
|
257
|
-
if (!ctx.sessionId)
|
|
258
|
-
return PASS;
|
|
259
|
-
const { runDriftCatalogScan } = await import("../hooks/drift-catalog.js");
|
|
260
|
-
try {
|
|
261
|
-
const count = await runDriftCatalogScan({
|
|
262
|
-
sessionId: ctx.sessionId,
|
|
263
|
-
transcriptPath: ctx.transcriptPath,
|
|
264
|
-
cwd: ctx.cwd,
|
|
265
|
-
});
|
|
266
|
-
if (count > 0) {
|
|
267
|
-
return {
|
|
268
|
-
kind: "warn",
|
|
269
|
-
message: `🦑 [opensquid drift-catalog] recorded ${count} drift marker(s)`,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
catch {
|
|
274
|
-
/* fail-open per Stop/SessionEnd precedent */
|
|
275
|
-
}
|
|
276
|
-
return PASS;
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
id: "session-state-cleanup",
|
|
281
|
-
catches: "preexisting",
|
|
282
|
-
hook: "SessionEnd",
|
|
283
|
-
rationale: "remove per-session ledger + state files to bound disk usage.",
|
|
284
|
-
when: () => true,
|
|
285
|
-
check: async (ctx) => {
|
|
286
|
-
if (!ctx.sessionId)
|
|
287
|
-
return PASS;
|
|
288
|
-
const { clearSession } = await import("../hooks/honesty-ledger.js");
|
|
289
|
-
try {
|
|
290
|
-
await clearSession(ctx.sessionId);
|
|
291
|
-
}
|
|
292
|
-
catch {
|
|
293
|
-
/* non-fatal */
|
|
294
|
-
}
|
|
295
|
-
return PASS;
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
// ---------- Honesty + heartbeat (Stop hook auto-actions) ----------
|
|
299
|
-
{
|
|
300
|
-
id: "honesty-reconcile",
|
|
301
|
-
catches: "preexisting",
|
|
302
|
-
hook: "Stop",
|
|
303
|
-
rationale: "reconcile claims-vs-actions for the just-completed turn; broken promises surface next UPS.",
|
|
304
|
-
when: (ctx) => Boolean(ctx.sessionId && ctx.transcriptPath),
|
|
305
|
-
check: async () => PASS, // orchestrated by the evaluator (multi-step IO); kept here for catalog completeness
|
|
306
|
-
},
|
|
307
|
-
{
|
|
308
|
-
id: "heartbeat-arm",
|
|
309
|
-
catches: "preexisting",
|
|
310
|
-
hook: "Stop",
|
|
311
|
-
rationale: "arm the heartbeat nudge when the transcript crosses the configured token threshold.",
|
|
312
|
-
when: (ctx) => Boolean(ctx.sessionId && ctx.transcriptPath),
|
|
313
|
-
check: async () => PASS, // orchestrated by the evaluator; same kept-for-catalog reason
|
|
314
|
-
},
|
|
315
|
-
];
|
|
316
|
-
// =====================================================================
|
|
317
|
-
// Evaluation helpers — used by the evaluator (0.7.34) to walk RULES
|
|
318
|
-
// =====================================================================
|
|
319
|
-
/**
|
|
320
|
-
* Pick the rules that apply to a given hook event AND that aren't
|
|
321
|
-
* bypassed. Exported so the evaluator and tests share the same filter.
|
|
322
|
-
*/
|
|
323
|
-
export function rulesForEvent(event) {
|
|
324
|
-
return RULES.filter((r) => r.hook === event && !isBypassed(r));
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Walk applicable rules and collect verdicts. Stops at the first
|
|
328
|
-
* `block` for PreToolUse events; aggregates all surfaces/warns for
|
|
329
|
-
* Stop/UPS/SessionEnd events.
|
|
330
|
-
*/
|
|
331
|
-
export async function evaluateRules(ctx) {
|
|
332
|
-
const verdicts = [];
|
|
333
|
-
for (const rule of rulesForEvent(ctx.hookEvent)) {
|
|
334
|
-
if (!rule.when(ctx))
|
|
335
|
-
continue;
|
|
336
|
-
const v = await rule.check(ctx);
|
|
337
|
-
verdicts.push(v);
|
|
338
|
-
// Short-circuit on PreToolUse block — match the existing
|
|
339
|
-
// most-restrictive-wins semantic.
|
|
340
|
-
if (ctx.hookEvent === "PreToolUse" && v.kind === "block")
|
|
341
|
-
break;
|
|
342
|
-
}
|
|
343
|
-
return verdicts;
|
|
344
|
-
}
|
|
345
|
-
// =====================================================================
|
|
346
|
-
// Internal: drift-patterns delegation (shared by 4 rules)
|
|
347
|
-
// =====================================================================
|
|
348
|
-
/**
|
|
349
|
-
* Run the existing drift-patterns catalog against the tool call and
|
|
350
|
-
* extract the verdict for a specific pattern id. Returns PASS when
|
|
351
|
-
* the pattern doesn't fire OR when the verdict is bypass-only.
|
|
352
|
-
*
|
|
353
|
-
* Lets RULES delegate to the legacy catalog while presenting the
|
|
354
|
-
* declarative shape externally. The 0.7.35 cutover inlines these.
|
|
355
|
-
*/
|
|
356
|
-
async function deriveDriftPatternVerdict(ctx, patternId) {
|
|
357
|
-
if (!ctx.toolName)
|
|
358
|
-
return PASS;
|
|
359
|
-
const call = { tool: ctx.toolName, input: ctx.toolInput ?? {} };
|
|
360
|
-
const hits = findDrifts(call).filter((h) => h.pattern.id === patternId);
|
|
361
|
-
if (hits.length === 0)
|
|
362
|
-
return PASS;
|
|
363
|
-
const { exit, stderr } = decideDriftPatterns(hits, call);
|
|
364
|
-
if (exit === 2)
|
|
365
|
-
return { kind: "block", message: stderr };
|
|
366
|
-
return { kind: "warn", message: stderr };
|
|
367
|
-
}
|
|
368
|
-
//# sourceMappingURL=rules.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src.legacy/anti-drift/rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for anti-drift/rules.ts (0.7.33 unified-evaluator track).
|
|
3
|
-
*
|
|
4
|
-
* Validates:
|
|
5
|
-
* 1. Catalog shape — every rule has the required fields
|
|
6
|
-
* 2. rulesForEvent filtering — by event + bypass env var
|
|
7
|
-
* 3. evaluateRules behavior — PreToolUse short-circuits on block
|
|
8
|
-
* 4. Specific rule when() predicates fire correctly
|
|
9
|
-
*
|
|
10
|
-
* Deep behavioral coverage of each rule's check() lives in the
|
|
11
|
-
* existing src/hooks/*.test.ts suites (since the rules delegate
|
|
12
|
-
* there). The 0.7.35 cutover migrates those tests alongside.
|
|
13
|
-
*/
|
|
14
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
15
|
-
import { RULES, evaluateRules, rulesForEvent, } from "./rules.js";
|
|
16
|
-
describe("RULES catalog shape", () => {
|
|
17
|
-
it("has at least 16 rules (matches the design doc's 18, allowing 1-2 implementation merges)", () => {
|
|
18
|
-
expect(RULES.length).toBeGreaterThanOrEqual(16);
|
|
19
|
-
});
|
|
20
|
-
it("every rule has all required fields populated", () => {
|
|
21
|
-
for (const r of RULES) {
|
|
22
|
-
expect(r.id).toBeTruthy();
|
|
23
|
-
expect(r.catches).toBeTruthy();
|
|
24
|
-
expect(["PreToolUse", "Stop", "UserPromptSubmit", "SessionEnd"]).toContain(r.hook);
|
|
25
|
-
expect(typeof r.when).toBe("function");
|
|
26
|
-
expect(typeof r.check).toBe("function");
|
|
27
|
-
expect(r.rationale).toBeTruthy();
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
it("rule ids are unique", () => {
|
|
31
|
-
const ids = RULES.map((r) => r.id);
|
|
32
|
-
expect(new Set(ids).size).toBe(ids.length);
|
|
33
|
-
});
|
|
34
|
-
it("covers all 10 drift D-entries (by `catches` field)", () => {
|
|
35
|
-
const covered = new Set(RULES.map((r) => r.catches).flatMap((c) => c.split("+").map((s) => s.trim())));
|
|
36
|
-
for (const drift of ["D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "D10"]) {
|
|
37
|
-
expect([...covered].some((c) => c === drift || c.startsWith(drift)), `expected catches to include ${drift}`).toBe(true);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
describe("rulesForEvent", () => {
|
|
42
|
-
it("filters by hook event", () => {
|
|
43
|
-
const preRules = rulesForEvent("PreToolUse");
|
|
44
|
-
expect(preRules.every((r) => r.hook === "PreToolUse")).toBe(true);
|
|
45
|
-
expect(preRules.length).toBeGreaterThan(0);
|
|
46
|
-
});
|
|
47
|
-
it("excludes bypassed rules", () => {
|
|
48
|
-
const target = RULES.find((r) => r.id === "active-task-required");
|
|
49
|
-
expect(target).toBeDefined();
|
|
50
|
-
expect(target.bypass).toBe("OPENSQUID_SKIP_ACTIVE_TASK_GATE");
|
|
51
|
-
process.env.OPENSQUID_SKIP_ACTIVE_TASK_GATE = "1";
|
|
52
|
-
try {
|
|
53
|
-
const ids = rulesForEvent("PreToolUse").map((r) => r.id);
|
|
54
|
-
expect(ids).not.toContain("active-task-required");
|
|
55
|
-
}
|
|
56
|
-
finally {
|
|
57
|
-
delete process.env.OPENSQUID_SKIP_ACTIVE_TASK_GATE;
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
it("bypass env var must be exactly '1' (other values do NOT bypass)", () => {
|
|
61
|
-
process.env.OPENSQUID_SKIP_ACTIVE_TASK_GATE = "true";
|
|
62
|
-
try {
|
|
63
|
-
const ids = rulesForEvent("PreToolUse").map((r) => r.id);
|
|
64
|
-
expect(ids).toContain("active-task-required");
|
|
65
|
-
}
|
|
66
|
-
finally {
|
|
67
|
-
delete process.env.OPENSQUID_SKIP_ACTIVE_TASK_GATE;
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
describe("rule when() predicates", () => {
|
|
72
|
-
const preCtx = (toolName, command) => ({
|
|
73
|
-
hookEvent: "PreToolUse",
|
|
74
|
-
toolName,
|
|
75
|
-
toolInput: command !== undefined ? { command } : {},
|
|
76
|
-
});
|
|
77
|
-
it("active-task-required fires only on log_phase / chat_send", () => {
|
|
78
|
-
const rule = RULES.find((r) => r.id === "active-task-required");
|
|
79
|
-
expect(rule.when(preCtx("mcp__opensquid__log_phase"))).toBe(true);
|
|
80
|
-
expect(rule.when(preCtx("mcp__opensquid__chat_send"))).toBe(true);
|
|
81
|
-
expect(rule.when(preCtx("Bash", "ls"))).toBe(false);
|
|
82
|
-
expect(rule.when(preCtx("Read"))).toBe(false);
|
|
83
|
-
});
|
|
84
|
-
it("never-amend.when fires on Bash tool", () => {
|
|
85
|
-
const rule = RULES.find((r) => r.id === "never-amend");
|
|
86
|
-
expect(rule.when(preCtx("Bash", "git commit --amend"))).toBe(true);
|
|
87
|
-
// Filter happens inside check via drift-patterns; when() just gates by tool name.
|
|
88
|
-
expect(rule.when(preCtx("Edit"))).toBe(false);
|
|
89
|
-
});
|
|
90
|
-
it("engine-vocab-leak fires only on git commit Bash commands", () => {
|
|
91
|
-
const rule = RULES.find((r) => r.id === "engine-vocab-leak");
|
|
92
|
-
expect(rule.when(preCtx("Bash", "git commit -m 'foo'"))).toBe(true);
|
|
93
|
-
expect(rule.when(preCtx("Bash", "git status"))).toBe(false);
|
|
94
|
-
expect(rule.when(preCtx("Bash"))).toBe(false);
|
|
95
|
-
});
|
|
96
|
-
it("heartbeat-recall-required fires on any mcp__opensquid__* tool", () => {
|
|
97
|
-
const rule = RULES.find((r) => r.id === "heartbeat-recall-required");
|
|
98
|
-
expect(rule.when(preCtx("mcp__opensquid__recall"))).toBe(true);
|
|
99
|
-
expect(rule.when(preCtx("mcp__opensquid__log_phase"))).toBe(true);
|
|
100
|
-
expect(rule.when(preCtx("mcp__opensquid__chat_send"))).toBe(true);
|
|
101
|
-
expect(rule.when(preCtx("mcp__plugin_telegram_telegram__reply"))).toBe(false);
|
|
102
|
-
expect(rule.when(preCtx("Bash"))).toBe(false);
|
|
103
|
-
});
|
|
104
|
-
it("telegram-redirect-report fires only on plugin:telegram reply", () => {
|
|
105
|
-
const rule = RULES.find((r) => r.id === "telegram-redirect-report");
|
|
106
|
-
expect(rule.when(preCtx("mcp__plugin_telegram_telegram__reply"))).toBe(true);
|
|
107
|
-
expect(rule.when(preCtx("mcp__opensquid__chat_send"))).toBe(false);
|
|
108
|
-
});
|
|
109
|
-
it("inline-report-missing-phases fires on Stop with assistantText", () => {
|
|
110
|
-
const rule = RULES.find((r) => r.id === "inline-report-missing-phases");
|
|
111
|
-
expect(rule.when({ hookEvent: "Stop", assistantText: "hi" })).toBe(true);
|
|
112
|
-
expect(rule.when({ hookEvent: "Stop" })).toBe(false);
|
|
113
|
-
});
|
|
114
|
-
it("multi-task-plan-injection fires on UPS with userPrompt", () => {
|
|
115
|
-
const rule = RULES.find((r) => r.id === "multi-task-plan-injection");
|
|
116
|
-
expect(rule.when({ hookEvent: "UserPromptSubmit", userPrompt: "166 then 168" })).toBe(true);
|
|
117
|
-
expect(rule.when({ hookEvent: "UserPromptSubmit" })).toBe(false);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
describe("evaluateRules — short-circuit on PreToolUse block", () => {
|
|
121
|
-
let originalRules;
|
|
122
|
-
beforeEach(() => {
|
|
123
|
-
originalRules = [...RULES];
|
|
124
|
-
});
|
|
125
|
-
afterEach(() => {
|
|
126
|
-
RULES.length = 0;
|
|
127
|
-
RULES.push(...originalRules);
|
|
128
|
-
});
|
|
129
|
-
it("PreToolUse: stops at the first block verdict (most-restrictive-wins)", async () => {
|
|
130
|
-
// Replace catalog with two fakes that both apply; the first blocks.
|
|
131
|
-
RULES.length = 0;
|
|
132
|
-
let secondRan = false;
|
|
133
|
-
RULES.push({
|
|
134
|
-
id: "fake-block",
|
|
135
|
-
catches: "test",
|
|
136
|
-
hook: "PreToolUse",
|
|
137
|
-
when: () => true,
|
|
138
|
-
check: async () => ({ kind: "block", message: "first" }),
|
|
139
|
-
rationale: "test",
|
|
140
|
-
}, {
|
|
141
|
-
id: "fake-second",
|
|
142
|
-
catches: "test",
|
|
143
|
-
hook: "PreToolUse",
|
|
144
|
-
when: () => true,
|
|
145
|
-
check: async () => {
|
|
146
|
-
secondRan = true;
|
|
147
|
-
return { kind: "pass" };
|
|
148
|
-
},
|
|
149
|
-
rationale: "test",
|
|
150
|
-
});
|
|
151
|
-
const verdicts = await evaluateRules({ hookEvent: "PreToolUse", toolName: "Bash" });
|
|
152
|
-
expect(verdicts.length).toBe(1);
|
|
153
|
-
expect(verdicts[0]).toEqual({ kind: "block", message: "first" });
|
|
154
|
-
expect(secondRan).toBe(false);
|
|
155
|
-
});
|
|
156
|
-
it("Stop: accumulates all verdicts (no short-circuit on first surface)", async () => {
|
|
157
|
-
RULES.length = 0;
|
|
158
|
-
RULES.push({
|
|
159
|
-
id: "fake-surface-a",
|
|
160
|
-
catches: "test",
|
|
161
|
-
hook: "Stop",
|
|
162
|
-
when: () => true,
|
|
163
|
-
check: async () => ({ kind: "surface", message: "a" }),
|
|
164
|
-
rationale: "test",
|
|
165
|
-
}, {
|
|
166
|
-
id: "fake-surface-b",
|
|
167
|
-
catches: "test",
|
|
168
|
-
hook: "Stop",
|
|
169
|
-
when: () => true,
|
|
170
|
-
check: async () => ({ kind: "surface", message: "b" }),
|
|
171
|
-
rationale: "test",
|
|
172
|
-
});
|
|
173
|
-
const verdicts = await evaluateRules({ hookEvent: "Stop", assistantText: "test" });
|
|
174
|
-
expect(verdicts.length).toBe(2);
|
|
175
|
-
expect(verdicts.map((v) => v.kind === "surface" && v.message)).toEqual(["a", "b"]);
|
|
176
|
-
});
|
|
177
|
-
it("skips rules whose when() returns false", async () => {
|
|
178
|
-
RULES.length = 0;
|
|
179
|
-
RULES.push({
|
|
180
|
-
id: "fake-not-applicable",
|
|
181
|
-
catches: "test",
|
|
182
|
-
hook: "PreToolUse",
|
|
183
|
-
when: () => false,
|
|
184
|
-
check: async () => ({ kind: "block", message: "should not fire" }),
|
|
185
|
-
rationale: "test",
|
|
186
|
-
});
|
|
187
|
-
const verdicts = await evaluateRules({ hookEvent: "PreToolUse", toolName: "Bash" });
|
|
188
|
-
expect(verdicts).toEqual([]);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
describe("Verdict shape — pass / block / warn / surface", () => {
|
|
192
|
-
it("PASS verdicts have no message field", () => {
|
|
193
|
-
const v = { kind: "pass" };
|
|
194
|
-
expect(v.kind).toBe("pass");
|
|
195
|
-
expect("message" in v).toBe(false);
|
|
196
|
-
});
|
|
197
|
-
it("non-pass verdicts carry a message", () => {
|
|
198
|
-
const block = { kind: "block", message: "x" };
|
|
199
|
-
const warn = { kind: "warn", message: "y" };
|
|
200
|
-
const surface = { kind: "surface", message: "z" };
|
|
201
|
-
expect(block.message).toBe("x");
|
|
202
|
-
expect(warn.message).toBe("y");
|
|
203
|
-
expect(surface.message).toBe("z");
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
describe("hook-event coverage", () => {
|
|
207
|
-
it("each of the 4 hook events has at least one rule", () => {
|
|
208
|
-
for (const event of ["PreToolUse", "Stop", "UserPromptSubmit", "SessionEnd"]) {
|
|
209
|
-
const rules = RULES.filter((r) => r.hook === event);
|
|
210
|
-
expect(rules.length, `expected at least one rule for ${event}`).toBeGreaterThan(0);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
});
|