diff --git a/.backup/ban_stdlib_logger.rego b/.backup/ban_stdlib_logger.rego
deleted file mode 100644
index d6f372f..0000000
--- a/.backup/ban_stdlib_logger.rego
+++ /dev/null
@@ -1,163 +0,0 @@
-# METADATA
-# scope: package
-# title: Ban Stdlib Logger
-# description: Blocks use of stdlib logging in Python code
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.ban_stdlib_logger
-
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-is_python_patch(patch_text) if {
- contains(patch_text, ".py")
-}
-
-is_python_patch(patch_text) if {
- contains(patch_text, ".pyi")
-}
-
-stdlib_logger_pattern := `import logging|from logging import|logging\.getLogger`
-file_path_pattern := `\.py$`
-
-# Block Write/Edit operations that introduce stdlib logging
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
-
- content := new_content
- content != null
- regex.match(stdlib_logger_pattern, content)
-
- decision := {
- "rule_id": "PY-LOG-001",
- "reason": "Stdlib logging usage is prohibited. Use the project logging utilities instead.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
-
- content := edit_new_content(edit)
- content != null
- regex.match(stdlib_logger_pattern, content)
-
- decision := {
- "rule_id": "PY-LOG-001",
- "reason": "Stdlib logging usage is prohibited. Use the project logging utilities instead.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
- lower_patch := lower(patch)
- is_python_patch(lower_patch)
- regex.match(stdlib_logger_pattern, patch)
-
- decision := {
- "rule_id": "PY-LOG-001",
- "reason": "Stdlib logging usage is prohibited. Use the project logging utilities instead.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_assertion_roulette.rego b/.backup/block_assertion_roulette.rego
deleted file mode 100644
index 8c2671d..0000000
--- a/.backup/block_assertion_roulette.rego
+++ /dev/null
@@ -1,175 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Assertion Roulette
-# description: Blocks multiple bare asserts in a single test without messages
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_assertion_roulette
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "+++ b/")
- path := replace(line, "+++ b/", "")
- regex.match(pattern, path)
-}
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "--- a/")
- path := replace(line, "--- a/", "")
- regex.match(pattern, path)
-}
-
-file_path_pattern := `tests?/.*\.py$`
-assertion_pattern := `^\s*assert\s+[^,\n]+\n\s*assert\s+[^,\n]+$`
-
-# Block Write/Edit operations that introduce assertion roulette
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
-
- content := new_content
- content != null
- regex.match(assertion_pattern, content)
-
- decision := {
- "rule_id": "TEST-ASSERT-001",
- "reason": "Multiple bare asserts detected. Use one assert per test or add assertion messages.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
-
- content := edit_new_content(edit)
- content != null
- regex.match(assertion_pattern, content)
-
- decision := {
- "rule_id": "TEST-ASSERT-001",
- "reason": "Multiple bare asserts detected. Use one assert per test or add assertion messages.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
- patch_targets_path(file_path_pattern)
-
- regex.match(assertion_pattern, patch)
-
- decision := {
- "rule_id": "TEST-ASSERT-001",
- "reason": "Multiple bare asserts detected. Use one assert per test or add assertion messages.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_biome_ignore.rego b/.backup/block_biome_ignore.rego
deleted file mode 100644
index 35516ad..0000000
--- a/.backup/block_biome_ignore.rego
+++ /dev/null
@@ -1,175 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Biome Ignore
-# description: Blocks ignore directives in JS/TS files
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_biome_ignore
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "+++ b/")
- path := replace(line, "+++ b/", "")
- regex.match(pattern, path)
-}
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "--- a/")
- path := replace(line, "--- a/", "")
- regex.match(pattern, path)
-}
-
-file_path_pattern := `\.(js|jsx|ts|tsx|mjs|cjs)$`
-ignore_pattern := `//\s*biome-ignore|//\s*@ts-ignore|//\s*@ts-expect-error|//\s*@ts-nocheck|//\s*eslint-disable|/\*\s*eslint-disable`
-
-# Block Write/Edit operations that introduce ignore directives
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
-
- content := new_content
- content != null
- regex.match(ignore_pattern, content)
-
- decision := {
- "rule_id": "TS-LINT-002",
- "reason": "Ignore directives for Biome/TypeScript/ESLint are prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
-
- content := edit_new_content(edit)
- content != null
- regex.match(ignore_pattern, content)
-
- decision := {
- "rule_id": "TS-LINT-002",
- "reason": "Ignore directives for Biome/TypeScript/ESLint are prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
- patch_targets_path(file_path_pattern)
-
- regex.match(ignore_pattern, patch)
-
- decision := {
- "rule_id": "TS-LINT-002",
- "reason": "Ignore directives for Biome/TypeScript/ESLint are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_biome_ignore_bash.rego b/.backup/block_biome_ignore_bash.rego
deleted file mode 100644
index e88d33d..0000000
--- a/.backup/block_biome_ignore_bash.rego
+++ /dev/null
@@ -1,26 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Biome Ignore (Bash)
-# description: Blocks Bash commands that add ignore directives to JS/TS files
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["Bash"]
-package cupcake.policies.opencode.block_biome_ignore_bash
-import rego.v1
-
-ignore_pattern := `(biome-ignore|@ts-ignore|@ts-expect-error|@ts-nocheck|eslint-disable).*\.(js|jsx|ts|tsx|mjs|cjs)`
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- input.tool_name == "Bash"
-
- command := input.tool_input.command
- regex.match(ignore_pattern, command)
-
- decision := {
- "rule_id": "TS-LINT-001",
- "reason": "Ignore directives for Biome/TypeScript/ESLint are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_broad_exception_handler.rego b/.backup/block_broad_exception_handler.rego
deleted file mode 100644
index c706d2f..0000000
--- a/.backup/block_broad_exception_handler.rego
+++ /dev/null
@@ -1,146 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Broad Exception Handler
-# description: Blocks bare Exception handlers that only log
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_broad_exception_handler
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-handler_pattern := `except\s+Exception\s*(?:as\s+\w+)?:\s*\n\s+(?:logger\.|logging\.)`
-
-# Block Write/Edit operations that introduce broad exception handlers
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- content := new_content
- content != null
- regex.match(handler_pattern, content)
-
- decision := {
- "rule_id": "PY-EXC-001",
- "reason": "Broad Exception handlers that only log are prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- content := edit_new_content(edit)
- content != null
- regex.match(handler_pattern, content)
-
- decision := {
- "rule_id": "PY-EXC-001",
- "reason": "Broad Exception handlers that only log are prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
-
- regex.match(handler_pattern, patch)
-
- decision := {
- "rule_id": "PY-EXC-001",
- "reason": "Broad Exception handlers that only log are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_code_quality_test_bash.rego b/.backup/block_code_quality_test_bash.rego
deleted file mode 100644
index 692c70b..0000000
--- a/.backup/block_code_quality_test_bash.rego
+++ /dev/null
@@ -1,26 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Code Quality Test (Bash)
-# description: Blocks Bash edits to src/test/code-quality.test.ts
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["Bash"]
-package cupcake.policies.opencode.block_code_quality_test_bash
-import rego.v1
-
-pattern := `(sed|awk|cat\s*>|echo\s*>|tee|cp\s+.*code-quality\.test\.ts|mv\s+.*code-quality\.test\.ts|rm\s+.*code-quality\.test\.ts|>|>>).*code-quality\.test\.ts|code-quality\.test\.ts.*(>|>>|\|.*tee)`
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- input.tool_name == "Bash"
-
- command := input.tool_input.command
- regex.match(pattern, command)
-
- decision := {
- "rule_id": "TS-QUALITY-001",
- "reason": "Direct edits to src/test/code-quality.test.ts are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_code_quality_test_edits.rego b/.backup/block_code_quality_test_edits.rego
deleted file mode 100644
index 7f5ced7..0000000
--- a/.backup/block_code_quality_test_edits.rego
+++ /dev/null
@@ -1,127 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Code Quality Test (Edits)
-# description: Blocks file edits to src/test/code-quality.test.ts
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_code_quality_test_edits
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-file_path_pattern := `src/test/code-quality\.test\.ts$`
-
-# Block Write/Edit operations targeting code-quality test file
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
-
- decision := {
- "rule_id": "TS-QUALITY-002",
- "reason": "Direct edits to src/test/code-quality.test.ts are prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
-
- decision := {
- "rule_id": "TS-QUALITY-002",
- "reason": "Direct edits to src/test/code-quality.test.ts are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_code_quality_test_serena.rego b/.backup/block_code_quality_test_serena.rego
deleted file mode 100644
index cff4ce8..0000000
--- a/.backup/block_code_quality_test_serena.rego
+++ /dev/null
@@ -1,126 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Code Quality Test (Serena)
-# description: Blocks Serena edits to src/test/code-quality.test.ts
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools:
-# - McpSerenaReplaceContent
-# - McpSerenaReplaceSymbolBody
-# - McpSerenaCreateTextFile
-# - McpSerenaInsertBeforeSymbol
-# - McpSerenaInsertAfterSymbol
-# - McpSerenaRenameSymbol
-package cupcake.policies.opencode.block_code_quality_test_serena
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-file_path_pattern := `(^|/)src/test/code-quality\.test\.ts$`
-
-get_relative_path := path if {
- path := tool_input.relative_path
-} else := path if {
- path := tool_input.path
-} else := ""
-
-# Block Serena operations targeting code-quality test file
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
-
- tool_names := {"McpSerenaReplaceContent", "McpSerenaReplaceSymbolBody", "McpSerenaCreateTextFile", "McpSerenaInsertBeforeSymbol", "McpSerenaInsertAfterSymbol", "McpSerenaRenameSymbol"}
- tool_name in tool_names
-
- file_path := get_relative_path
- regex.match(file_path_pattern, file_path)
-
- decision := {
- "rule_id": "TS-QUALITY-003",
- "reason": "Direct edits to src/test/code-quality.test.ts are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_code_quality_test_serena_plugin.rego b/.backup/block_code_quality_test_serena_plugin.rego
deleted file mode 100644
index fb87a2b..0000000
--- a/.backup/block_code_quality_test_serena_plugin.rego
+++ /dev/null
@@ -1,126 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Code Quality Test (Serena Plugin)
-# description: Blocks Serena plugin edits to src/test/code-quality.test.ts
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools:
-# - McpPluginSerenaSerenaReplaceContent
-# - McpPluginSerenaSerenaReplaceSymbolBody
-# - McpPluginSerenaSerenaCreateTextFile
-# - McpPluginSerenaSerenaInsertBeforeSymbol
-# - McpPluginSerenaSerenaInsertAfterSymbol
-# - McpPluginSerenaSerenaRenameSymbol
-package cupcake.policies.opencode.block_code_quality_test_serena_plugin
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-file_path_pattern := `(^|/)src/test/code-quality\.test\.ts$`
-
-get_relative_path := path if {
- path := tool_input.relative_path
-} else := path if {
- path := tool_input.path
-} else := ""
-
-# Block Serena plugin operations targeting code-quality test file
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
-
- tool_names := {"McpPluginSerenaSerenaReplaceContent", "McpPluginSerenaSerenaReplaceSymbolBody", "McpPluginSerenaSerenaCreateTextFile", "McpPluginSerenaSerenaInsertBeforeSymbol", "McpPluginSerenaSerenaInsertAfterSymbol", "McpPluginSerenaSerenaRenameSymbol"}
- tool_name in tool_names
-
- file_path := get_relative_path
- regex.match(file_path_pattern, file_path)
-
- decision := {
- "rule_id": "TS-QUALITY-004",
- "reason": "Direct edits to src/test/code-quality.test.ts are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_datetime_now_fallback.rego b/.backup/block_datetime_now_fallback.rego
deleted file mode 100644
index f77719c..0000000
--- a/.backup/block_datetime_now_fallback.rego
+++ /dev/null
@@ -1,146 +0,0 @@
-# METADATA
-# scope: package
-# title: Block datetime.now Fallback
-# description: Blocks returning datetime.now() as a fallback
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_datetime_now_fallback
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-pattern := `return\s+datetime\.now\s*\(`
-
-# Block Write/Edit operations that introduce datetime.now fallback
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- content := new_content
- content != null
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-DT-001",
- "reason": "Returning datetime.now() as a fallback is prohibited. Use a caller-provided timestamp.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- content := edit_new_content(edit)
- content != null
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-DT-001",
- "reason": "Returning datetime.now() as a fallback is prohibited. Use a caller-provided timestamp.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
-
- regex.match(pattern, patch)
-
- decision := {
- "rule_id": "PY-DT-001",
- "reason": "Returning datetime.now() as a fallback is prohibited. Use a caller-provided timestamp.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_default_value_swallow.rego b/.backup/block_default_value_swallow.rego
deleted file mode 100644
index b051cd3..0000000
--- a/.backup/block_default_value_swallow.rego
+++ /dev/null
@@ -1,146 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Default Value Swallow
-# description: Blocks exception handlers that warn and return defaults
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_default_value_swallow
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-pattern := `except\s+\w*(?:Error|Exception).*?:\s*\n\s+.*?(?:logger\.|logging\.).*?(?:warning|warn).*?\n\s+return\s+(?:\w+Settings|Defaults?\(|default_|\{[^}]*\}|[A-Z_]+_DEFAULT)`
-
-# Block Write/Edit operations that swallow exceptions with defaults
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- content := new_content
- content != null
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-EXC-002",
- "reason": "Swallowing exceptions and returning defaults is prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- content := edit_new_content(edit)
- content != null
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-EXC-002",
- "reason": "Swallowing exceptions and returning defaults is prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
-
- regex.match(pattern, patch)
-
- decision := {
- "rule_id": "PY-EXC-002",
- "reason": "Swallowing exceptions and returning defaults is prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_duplicate_fixtures.rego b/.backup/block_duplicate_fixtures.rego
deleted file mode 100644
index bf974af..0000000
--- a/.backup/block_duplicate_fixtures.rego
+++ /dev/null
@@ -1,179 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Duplicate Fixtures
-# description: Blocks redefining global pytest fixtures outside conftest.py
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_duplicate_fixtures
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "+++ b/")
- path := replace(line, "+++ b/", "")
- regex.match(pattern, path)
-}
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "--- a/")
- path := replace(line, "--- a/", "")
- regex.match(pattern, path)
-}
-
-file_path_pattern := `tests?/.*\.py$`
-conftest_pattern := `tests?/conftest\.py$`
-fixture_pattern := `@pytest\.fixture[^@]*\ndef\s+(mock_uow|crypto|meetings_dir|webhook_config|webhook_config_all_events|sample_datetime|calendar_settings|meeting_id|sample_meeting|recording_meeting|mock_grpc_context|mock_asr_engine|mock_optional_extras)\s*\(`
-
-# Block Write/Edit operations that introduce duplicate fixtures
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
- not regex.match(conftest_pattern, file_path)
-
- content := new_content
- content != null
- regex.match(fixture_pattern, content)
-
- decision := {
- "rule_id": "TEST-FIX-001",
- "reason": "Duplicate global fixtures are prohibited. Use tests/conftest.py fixtures instead.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
- not regex.match(conftest_pattern, file_path)
-
- content := edit_new_content(edit)
- content != null
- regex.match(fixture_pattern, content)
-
- decision := {
- "rule_id": "TEST-FIX-001",
- "reason": "Duplicate global fixtures are prohibited. Use tests/conftest.py fixtures instead.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
- patch_targets_path(file_path_pattern)
- not regex.match(conftest_pattern, patch)
-
- regex.match(fixture_pattern, patch)
-
- decision := {
- "rule_id": "TEST-FIX-001",
- "reason": "Duplicate global fixtures are prohibited. Use tests/conftest.py fixtures instead.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_linter_config_frontend.rego b/.backup/block_linter_config_frontend.rego
deleted file mode 100644
index 3bff2fe..0000000
--- a/.backup/block_linter_config_frontend.rego
+++ /dev/null
@@ -1,129 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Frontend Linter Config
-# description: Blocks edits to frontend linter config files
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_linter_config_frontend
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-file_path_pattern := `(^|/)client/.*(?:\.?eslint(?:rc|\.config).*|\.?prettier(?:rc|\.config).*|biome\.json|tsconfig\.json|\.?rustfmt\.toml|\.?clippy\.toml)$`
-
-# Block Write/Edit operations targeting frontend linter configs
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
- not contains(lower(file_path), "node_modules/")
-
- decision := {
- "rule_id": "TS-CONFIG-002",
- "reason": "Frontend linter/config file edits are prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
- not contains(lower(file_path), "node_modules/")
-
- decision := {
- "rule_id": "TS-CONFIG-002",
- "reason": "Frontend linter/config file edits are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_linter_config_frontend_bash.rego b/.backup/block_linter_config_frontend_bash.rego
deleted file mode 100644
index a356217..0000000
--- a/.backup/block_linter_config_frontend_bash.rego
+++ /dev/null
@@ -1,26 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Frontend Linter Config (Bash)
-# description: Blocks Bash edits to frontend linter config files
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["Bash"]
-package cupcake.policies.opencode.block_linter_config_frontend_bash
-import rego.v1
-
-pattern := `(rm|mv|cp|sed|awk|chmod|chown|touch|truncate|tee|>|>>)\s.*client/.*(?:biome\.json|tsconfig\.json|\.?eslint(?:rc|\.config)|\.?prettier(?:rc|\.config)|\.?rustfmt\.toml|\.?clippy\.toml)`
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- input.tool_name == "Bash"
-
- command := input.tool_input.command
- regex.match(pattern, command)
-
- decision := {
- "rule_id": "TS-CONFIG-001",
- "reason": "Frontend linter/config file edits are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_linter_config_python.rego b/.backup/block_linter_config_python.rego
deleted file mode 100644
index f2f6479..0000000
--- a/.backup/block_linter_config_python.rego
+++ /dev/null
@@ -1,129 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Python Linter Config
-# description: Blocks edits to Python linter config files
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_linter_config_python
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-file_path_pattern := `(?:pyproject\.toml|\.?ruff\.toml|\.?pyrightconfig\.json|\.?mypy\.ini|setup\.cfg|\.flake8|tox\.ini|\.?pylintrc)$`
-
-# Block Write/Edit operations targeting Python linter configs
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
- not contains(lower(file_path), "/.venv/")
-
- decision := {
- "rule_id": "PY-CONFIG-002",
- "reason": "Python linter/config file edits are prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
- not contains(lower(file_path), "/.venv/")
-
- decision := {
- "rule_id": "PY-CONFIG-002",
- "reason": "Python linter/config file edits are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_linter_config_python_bash.rego b/.backup/block_linter_config_python_bash.rego
deleted file mode 100644
index 65229c7..0000000
--- a/.backup/block_linter_config_python_bash.rego
+++ /dev/null
@@ -1,26 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Python Linter Config (Bash)
-# description: Blocks Bash edits to Python linter config files
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["Bash"]
-package cupcake.policies.opencode.block_linter_config_python_bash
-import rego.v1
-
-pattern := `(rm|mv|cp|sed|awk|chmod|chown|touch|truncate|tee|>|>>)\s.*(?:pyproject\.toml|\.?ruff\.toml|\.?pyrightconfig\.json|\.?mypy\.ini|setup\.cfg|\.flake8|tox\.ini|\.?pylintrc)`
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- input.tool_name == "Bash"
-
- command := input.tool_input.command
- regex.match(pattern, command)
-
- decision := {
- "rule_id": "PY-CONFIG-001",
- "reason": "Python linter/config file edits are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_magic_numbers.rego b/.backup/block_magic_numbers.rego
deleted file mode 100644
index 54bac75..0000000
--- a/.backup/block_magic_numbers.rego
+++ /dev/null
@@ -1,178 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Magic Numbers
-# description: Blocks introduction of magic numbers outside constants modules
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_magic_numbers
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "+++ b/")
- path := replace(line, "+++ b/", "")
- regex.match(pattern, path)
-}
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "--- a/")
- path := replace(line, "--- a/", "")
- regex.match(pattern, path)
-}
-
-file_path_pattern := `\.(py|ts|tsx|js|jsx)$`
-number_pattern := `(?:timeout|delay|interval|duration|limit|max|min|size|count|threshold|retry|retries|attempts|port|width|height|margin|padding|offset|index|length|capacity|buffer|batch|chunk|page|rate|fps|dpi|quality|level|priority|weight|score|factor|multiplier|divisor|percentage|ratio|scale)\s*[=:]\s*([2-9]|[1-9]\d+)|(?:if|while|for|elif|range|slice|sleep|wait|setTimeout|setInterval)\s*\([^)]*([2-9]|[1-9]\d+)`
-
-# Block Write/Edit operations that introduce magic numbers
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
- not contains(lower(file_path), "constants")
-
- content := new_content
- content != null
- regex.match(number_pattern, content)
-
- decision := {
- "rule_id": "STYLE-001",
- "reason": "Magic numbers are prohibited. Use named constants.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
- not contains(lower(file_path), "constants")
-
- content := edit_new_content(edit)
- content != null
- regex.match(number_pattern, content)
-
- decision := {
- "rule_id": "STYLE-001",
- "reason": "Magic numbers are prohibited. Use named constants.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
- patch_targets_path(file_path_pattern)
- not contains(lower(patch), "constants")
-
- regex.match(number_pattern, patch)
-
- decision := {
- "rule_id": "STYLE-001",
- "reason": "Magic numbers are prohibited. Use named constants.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_makefile_bash.rego b/.backup/block_makefile_bash.rego
deleted file mode 100644
index 15e2b9a..0000000
--- a/.backup/block_makefile_bash.rego
+++ /dev/null
@@ -1,26 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Makefile Edit (Bash)
-# description: Blocks Bash edits to Makefile
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["Bash"]
-package cupcake.policies.opencode.block_makefile_bash
-import rego.v1
-
-pattern := `(>>?\s*Makefile|sed\s+.*-i.*Makefile|sed\s+-i.*Makefile|perl\s+-[pi].*Makefile|tee\s+.*Makefile|(mv|cp)\s+\S+\s+Makefile\b|>\s*Makefile)`
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- input.tool_name == "Bash"
-
- command := input.tool_input.command
- regex.match(pattern, command)
-
- decision := {
- "rule_id": "BUILD-001",
- "reason": "Makefile edits are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_makefile_edit.rego b/.backup/block_makefile_edit.rego
deleted file mode 100644
index 9f7da15..0000000
--- a/.backup/block_makefile_edit.rego
+++ /dev/null
@@ -1,127 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Makefile Edit
-# description: Blocks file edits to Makefile
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_makefile_edit
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-file_path_pattern := `(?:^|/)Makefile$`
-
-# Block Write/Edit operations targeting Makefile
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
-
- decision := {
- "rule_id": "BUILD-002",
- "reason": "Makefile edits are prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
-
- decision := {
- "rule_id": "BUILD-002",
- "reason": "Makefile edits are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_no_verify.rego b/.backup/block_no_verify.rego
deleted file mode 100644
index d399d1f..0000000
--- a/.backup/block_no_verify.rego
+++ /dev/null
@@ -1,26 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Git --no-verify
-# description: Blocks git commit --no-verify
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["Bash"]
-package cupcake.policies.opencode.block_no_verify
-import rego.v1
-
-pattern := `git\s+commit\s+.*--no-verify|git\s+commit\s+--no-verify`
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- input.tool_name == "Bash"
-
- command := input.tool_input.command
- regex.match(pattern, command)
-
- decision := {
- "rule_id": "GIT-001",
- "reason": "Git commit --no-verify is prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_silent_none_return.rego b/.backup/block_silent_none_return.rego
deleted file mode 100644
index bdb56b4..0000000
--- a/.backup/block_silent_none_return.rego
+++ /dev/null
@@ -1,146 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Silent None Return
-# description: Blocks exception handlers that log and return empty values
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_silent_none_return
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-pattern := `except\s+\w*Error.*?:\s*\n\s+.*?(?:logger\.|logging\.).*?\n\s+return\s+(?:None|\[\]|False|\{\}|0)`
-
-# Block Write/Edit operations that swallow exceptions with empty returns
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- content := new_content
- content != null
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-EXC-003",
- "reason": "Silent exception handlers returning empty values are prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- content := edit_new_content(edit)
- content != null
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-EXC-003",
- "reason": "Silent exception handlers returning empty values are prohibited.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
-
- regex.match(pattern, patch)
-
- decision := {
- "rule_id": "PY-EXC-003",
- "reason": "Silent exception handlers returning empty values are prohibited.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_test_loops_conditionals.rego b/.backup/block_test_loops_conditionals.rego
deleted file mode 100644
index b706398..0000000
--- a/.backup/block_test_loops_conditionals.rego
+++ /dev/null
@@ -1,175 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Test Loops/Conditionals
-# description: Blocks loops or conditionals inside tests with asserts
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_test_loops_conditionals
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "+++ b/")
- path := replace(line, "+++ b/", "")
- regex.match(pattern, path)
-}
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "--- a/")
- path := replace(line, "--- a/", "")
- regex.match(pattern, path)
-}
-
-file_path_pattern := `tests?/.*\.py$`
-pattern := `def test_[^(]+\([^)]*\)[^:]*:[\s\S]*?\b(for|while|if)\s+[^:]+:[\s\S]*?assert`
-
-# Block Write/Edit operations that introduce loops/conditionals in tests
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
-
- content := new_content
- content != null
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "TEST-STRUCT-001",
- "reason": "Loops or conditionals inside tests are prohibited. Use parametrization.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
-
- content := edit_new_content(edit)
- content != null
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "TEST-STRUCT-001",
- "reason": "Loops or conditionals inside tests are prohibited. Use parametrization.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
- patch_targets_path(file_path_pattern)
-
- regex.match(pattern, patch)
-
- decision := {
- "rule_id": "TEST-STRUCT-001",
- "reason": "Loops or conditionals inside tests are prohibited. Use parametrization.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_tests_quality.rego b/.backup/block_tests_quality.rego
deleted file mode 100644
index 45233fd..0000000
--- a/.backup/block_tests_quality.rego
+++ /dev/null
@@ -1,130 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Tests Quality
-# description: Blocks edits to tests/quality (except baselines.json)
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.block_tests_quality
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-file_path_pattern := `tests/quality/`
-exclude_pattern := `baselines\.json$`
-
-# Block Write/Edit operations targeting tests/quality
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
- not regex.match(exclude_pattern, file_path)
-
- decision := {
- "rule_id": "TEST-QUALITY-002",
- "reason": "Direct edits to tests/quality are prohibited (except baselines.json).",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
- not regex.match(exclude_pattern, file_path)
-
- decision := {
- "rule_id": "TEST-QUALITY-002",
- "reason": "Direct edits to tests/quality are prohibited (except baselines.json).",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/block_tests_quality_bash.rego b/.backup/block_tests_quality_bash.rego
deleted file mode 100644
index e65e9b0..0000000
--- a/.backup/block_tests_quality_bash.rego
+++ /dev/null
@@ -1,27 +0,0 @@
-# METADATA
-# scope: package
-# title: Block Tests Quality (Bash)
-# description: Blocks Bash edits to tests/quality (except baselines.json)
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["Bash"]
-package cupcake.policies.opencode.block_tests_quality_bash
-import rego.v1
-
-pattern := `(rm|mv|cp|sed|awk|chmod|chown|touch|mkdir|rmdir|truncate|tee|>|>>)\s.*tests/quality/`
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- input.tool_name == "Bash"
-
- command := input.tool_input.command
- regex.match(pattern, command)
- not contains(lower(command), "tests/quality/baselines.json")
-
- decision := {
- "rule_id": "TEST-QUALITY-001",
- "reason": "Direct edits to tests/quality are prohibited (except baselines.json).",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/example.rego b/.backup/example.rego
deleted file mode 100644
index 0fb2487..0000000
--- a/.backup/example.rego
+++ /dev/null
@@ -1,33 +0,0 @@
-# METADATA
-# scope: package
-# title: Example Policy
-# description: A minimal example policy that never fires
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["Bash"]
-package cupcake.policies.example
-
-import rego.v1
-
-# This rule will never fire - it's just here to prevent OPA compilation issues
-# It checks for a command that nobody would ever type
-deny contains decision if {
- input.tool_input.command == "CUPCAKE_EXAMPLE_RULE_THAT_NEVER_FIRES_12345"
- decision := {
- "reason": "This will never happen",
- "severity": "LOW",
- "rule_id": "EXAMPLE-001"
- }
-}
-
-# Replace the above with your actual policies
-# Example of a real policy:
-# deny contains decision if {
-# contains(input.tool_input.command, "rm -rf /")
-# decision := {
-# "reason": "Dangerous command blocked",
-# "severity": "HIGH",
-# "rule_id": "SAFETY-001"
-# }
-# }
diff --git a/.backup/prevent_any_type.rego b/.backup/prevent_any_type.rego
deleted file mode 100644
index 89e4022..0000000
--- a/.backup/prevent_any_type.rego
+++ /dev/null
@@ -1,181 +0,0 @@
-# METADATA
-# scope: package
-# title: Ban Python Any Type
-# description: Blocks introduction of typing.Any in Python code
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.prevent_any_type
-
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-is_python_file(path) if {
- endswith(path, ".py")
-}
-
-is_python_file(path) if {
- endswith(path, ".pyi")
-}
-
-# Regex patterns indicating use of Any in type annotations/imports
-any_type_patterns := [
- `(?m)^\s*from\s+typing\s+import\s+[^#\n]*\bAny\b`,
- `\btyping\.Any\b`,
- `:\s*Any\b`,
- `:\s*"Any"`,
- `:\s*'Any'`,
- `->\s*Any\b`,
- `->\s*"Any"`,
- `->\s*'Any'`,
- `\[\s*Any\s*\]`,
- `\[\s*Any\s*,`,
- `,\s*Any\s*\]`,
- `,\s*Any\s*,`,
- `Union\[[^\]]*\bAny\b[^\]]*\]`,
- `Optional\[Any\]`,
-]
-
-# Block Write/Edit operations that introduce Any in Python files
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- # Only enforce for Python files
- file_path := lower(resolved_file_path)
- is_python_file(file_path)
-
- content := new_content
- content != null
-
- some pattern in any_type_patterns
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-TYPE-001",
- "reason": "Use of Any is prohibited in Python type annotations/imports. Replace with Protocol, TypeVar, TypedDict, or a concrete type.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- content := patch_content
- content != null
-
- some pattern in any_type_patterns
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-TYPE-001",
- "reason": "Use of Any is prohibited in Python type annotations/imports. Replace with Protocol, TypeVar, TypedDict, or a concrete type.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := lower(edit_path(edit))
- is_python_file(file_path)
-
- content := edit_new_content(edit)
- content != null
-
- some pattern in any_type_patterns
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-TYPE-001",
- "reason": "Use of Any is prohibited in Python type annotations/imports. Replace with Protocol, TypeVar, TypedDict, or a concrete type.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/prevent_type_suppression.rego b/.backup/prevent_type_suppression.rego
deleted file mode 100644
index e1ec86c..0000000
--- a/.backup/prevent_type_suppression.rego
+++ /dev/null
@@ -1,176 +0,0 @@
-# METADATA
-# scope: package
-# title: Ban Python Type Suppression
-# description: Blocks type suppression directives in Python code
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.prevent_type_suppression
-
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-is_python_file(path) if {
- endswith(path, ".py")
-}
-
-is_python_file(path) if {
- endswith(path, ".pyi")
-}
-
-# Regex patterns indicating type suppression directives
-type_suppression_patterns := [
- `#\s*type:\s*ignore(\[[^\]]+\])?\b`,
- `#\s*pyright:\s*ignore(\[[^\]]+\])?\b`,
- `#\s*mypy:\s*ignore(\[[^\]]+\])?\b`,
- `#\s*pyre-ignore\b`,
- `#\s*pyre-fixme\b`,
- `#\s*pyrefly:\s*ignore(\[[^\]]+\])?\b`,
- `#\s*basedpyright:\s*ignore(\[[^\]]+\])?\b`,
- `#\s*noqa\b`,
- `#\s*noqa:\s*\w+`,
-]
-
-# Block Write/Edit operations that introduce type suppression in Python files
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- # Only enforce for Python files
- file_path := lower(resolved_file_path)
- is_python_file(file_path)
-
- content := new_content
- content != null
-
- some pattern in type_suppression_patterns
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-TYPE-002",
- "reason": "Type suppression directives are prohibited in Python code. Fix the underlying type/lint issues instead.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- content := patch_content
- content != null
-
- some pattern in type_suppression_patterns
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-TYPE-002",
- "reason": "Type suppression directives are prohibited in Python code. Fix the underlying type/lint issues instead.",
- "severity": "HIGH"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := lower(edit_path(edit))
- is_python_file(file_path)
-
- content := edit_new_content(edit)
- content != null
-
- some pattern in type_suppression_patterns
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "PY-TYPE-002",
- "reason": "Type suppression directives are prohibited in Python code. Fix the underlying type/lint issues instead.",
- "severity": "HIGH"
- }
-}
diff --git a/.backup/warn_baselines_edit.rego b/.backup/warn_baselines_edit.rego
deleted file mode 100644
index 4d3410f..0000000
--- a/.backup/warn_baselines_edit.rego
+++ /dev/null
@@ -1,127 +0,0 @@
-# METADATA
-# scope: package
-# title: Warn on Baselines Edit
-# description: Warns on edits to tests/quality/baselines.json
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.warn_baselines_edit
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-file_path_pattern := `tests/quality/baselines\.json$`
-
-# Warn on Write/Edit operations targeting baselines.json
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
-
- decision := {
- "rule_id": "TEST-QUALITY-004",
- "reason": "Warning: editing tests/quality/baselines.json should be avoided unless explicitly required.",
- "severity": "LOW"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
-
- decision := {
- "rule_id": "TEST-QUALITY-004",
- "reason": "Warning: editing tests/quality/baselines.json should be avoided unless explicitly required.",
- "severity": "LOW"
- }
-}
diff --git a/.backup/warn_baselines_edit_bash.rego b/.backup/warn_baselines_edit_bash.rego
deleted file mode 100644
index 0b67e3d..0000000
--- a/.backup/warn_baselines_edit_bash.rego
+++ /dev/null
@@ -1,26 +0,0 @@
-# METADATA
-# scope: package
-# title: Warn on Baselines Edit (Bash)
-# description: Warns on Bash edits to tests/quality/baselines.json
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["Bash"]
-package cupcake.policies.opencode.warn_baselines_edit_bash
-import rego.v1
-
-pattern := `(sed|awk|echo|cat|tee|>|>>|cp|mv).*tests/quality/baselines\.json`
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- input.tool_name == "Bash"
-
- command := input.tool_input.command
- regex.match(pattern, command)
-
- decision := {
- "rule_id": "TEST-QUALITY-003",
- "reason": "Warning: editing tests/quality/baselines.json should be avoided unless explicitly required.",
- "severity": "LOW"
- }
-}
diff --git a/.backup/warn_large_file.rego b/.backup/warn_large_file.rego
deleted file mode 100644
index db96c11..0000000
--- a/.backup/warn_large_file.rego
+++ /dev/null
@@ -1,175 +0,0 @@
-# METADATA
-# scope: package
-# title: Warn on Large File
-# description: Warns when writing large files (>= 500 lines)
-# custom:
-# routing:
-# required_events: ["PreToolUse"]
-# required_tools: ["ApplyPatch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
-package cupcake.policies.opencode.warn_large_file
-import rego.v1
-
-tool_name := input.tool_name
-tool_input := input.tool_input
-
-resolved_file_path := input.resolved_file_path if {
- input.resolved_file_path != null
-} else := tool_input.file_path if {
- tool_input.file_path != null
-} else := tool_input.filePath if {
- tool_input.filePath != null
-} else := tool_input.path if {
- tool_input.path != null
-} else := tool_input.notebook_path if {
- tool_input.notebook_path != null
-} else := tool_input.notebookPath if {
- tool_input.notebookPath != null
-} else := ""
-
-new_content := tool_input.new_string if {
- tool_input.new_string != null
-} else := tool_input.newText if {
- tool_input.newText != null
-} else := tool_input.new_text if {
- tool_input.new_text != null
-} else := tool_input.content if {
- tool_input.content != null
-} else := ""
-
-old_content := tool_input.old_string if {
- tool_input.old_string != null
-} else := tool_input.oldText if {
- tool_input.oldText != null
-} else := tool_input.old_text if {
- tool_input.old_text != null
-} else := tool_input.previousContent if {
- tool_input.previousContent != null
-} else := ""
-
-patch_content := tool_input.patch if {
- tool_input.patch != null
-} else := tool_input.patchText if {
- tool_input.patchText != null
-} else := tool_input.patch_text if {
- tool_input.patch_text != null
-} else := ""
-
-edit_path(edit) := path if {
- edit.resolved_file_path != null
- path := edit.resolved_file_path
-} else := path if {
- edit.file_path != null
- path := edit.file_path
-} else := path if {
- edit.filePath != null
- path := edit.filePath
-} else := path if {
- edit.path != null
- path := edit.path
-} else := ""
-
-edit_new_content(edit) := content if {
- edit.new_string != null
- content := edit.new_string
-} else := content if {
- edit.newText != null
- content := edit.newText
-} else := content if {
- edit.new_text != null
- content := edit.new_text
-} else := content if {
- edit.content != null
- content := edit.content
-} else := ""
-
-edit_old_content(edit) := content if {
- edit.old_string != null
- content := edit.old_string
-} else := content if {
- edit.oldText != null
- content := edit.oldText
-} else := content if {
- edit.old_text != null
- content := edit.old_text
-} else := ""
-
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "+++ b/")
- path := replace(line, "+++ b/", "")
- regex.match(pattern, path)
-}
-
-patch_targets_path(pattern) if {
- patch := patch_content
- patch != null
- lines := split(patch, "\n")
- some line in lines
- startswith(line, "--- a/")
- path := replace(line, "--- a/", "")
- regex.match(pattern, path)
-}
-
-file_path_pattern := `\.(py|ts|tsx|js|jsx)$`
-pattern := `(?:.*\n){500,}`
-
-# Warn on Write/Edit operations that introduce large file content
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Write", "Edit", "NotebookEdit"}
-
- file_path := resolved_file_path
- regex.match(file_path_pattern, file_path)
-
- content := new_content
- content != null
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "STYLE-002",
- "reason": "Warning: file content exceeds 500 lines. Consider refactoring.",
- "severity": "LOW"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name == "MultiEdit"
-
- some edit in tool_input.edits
- file_path := edit_path(edit)
- regex.match(file_path_pattern, file_path)
-
- content := edit_new_content(edit)
- content != null
- regex.match(pattern, content)
-
- decision := {
- "rule_id": "STYLE-002",
- "reason": "Warning: file content exceeds 500 lines. Consider refactoring.",
- "severity": "LOW"
- }
-}
-
-deny contains decision if {
- input.hook_event_name == "PreToolUse"
- tool_name in {"Patch", "ApplyPatch"}
-
- patch := patch_content
- patch != null
-
- patch_targets_path(file_path_pattern)
-
- regex.match(pattern, patch)
-
- decision := {
- "rule_id": "STYLE-002",
- "reason": "Warning: file content exceeds 500 lines. Consider refactoring.",
- "severity": "LOW"
- }
-}
diff --git a/.benchmarks/Linux-CPython-3.12-64bit/0001_baseline.json b/.benchmarks/Linux-CPython-3.12-64bit/0001_baseline.json
deleted file mode 100644
index cbf6f55..0000000
--- a/.benchmarks/Linux-CPython-3.12-64bit/0001_baseline.json
+++ /dev/null
@@ -1,972 +0,0 @@
-{
- "machine_info": {
- "node": "little",
- "processor": "x86_64",
- "machine": "x86_64",
- "python_compiler": "GCC 13.3.0",
- "python_implementation": "CPython",
- "python_implementation_version": "3.12.3",
- "python_version": "3.12.3",
- "python_build": [
- "main",
- "Nov 6 2025 13:44:16"
- ],
- "release": "6.14.0-1018-oem",
- "system": "Linux",
- "cpu": {
- "python_version": "3.12.3.final.0 (64 bit)",
- "cpuinfo_version": [
- 9,
- 0,
- 0
- ],
- "cpuinfo_version_string": "9.0.0",
- "arch": "X86_64",
- "bits": 64,
- "count": 14,
- "arch_string_raw": "x86_64",
- "vendor_id_raw": "AuthenticAMD",
- "brand_raw": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S",
- "hz_advertised_friendly": "3.0000 GHz",
- "hz_actual_friendly": "3.0000 GHz",
- "hz_advertised": [
- 2999956000,
- 0
- ],
- "hz_actual": [
- 2999956000,
- 0
- ],
- "model": 112,
- "family": 26,
- "flags": [
- "3dnowprefetch",
- "abm",
- "adx",
- "aes",
- "apic",
- "arat",
- "arch_capabilities",
- "avx",
- "avx2",
- "avx512_bf16",
- "avx512_bitalg",
- "avx512_vbmi2",
- "avx512_vnni",
- "avx512_vp2intersect",
- "avx512_vpopcntdq",
- "avx512bitalg",
- "avx512bw",
- "avx512cd",
- "avx512dq",
- "avx512f",
- "avx512ifma",
- "avx512vbmi",
- "avx512vbmi2",
- "avx512vl",
- "avx512vnni",
- "avx512vpopcntdq",
- "avx_vnni",
- "bmi1",
- "bmi2",
- "clflush",
- "clflushopt",
- "clwb",
- "clzero",
- "cmov",
- "cmp_legacy",
- "cpuid",
- "cr8_legacy",
- "cx16",
- "cx8",
- "de",
- "erms",
- "extd_apicid",
- "f16c",
- "flush_l1d",
- "flushbyasid",
- "fma",
- "fpu",
- "fsgsbase",
- "fsrm",
- "fxsr",
- "fxsr_opt",
- "gfni",
- "ht",
- "hypervisor",
- "ibpb",
- "ibrs",
- "ibrs_enhanced",
- "invpcid",
- "lahf_lm",
- "lbrv",
- "lm",
- "mca",
- "mce",
- "misalignsse",
- "mmx",
- "mmxext",
- "movbe",
- "movdir64b",
- "movdiri",
- "msr",
- "mtrr",
- "nopl",
- "npt",
- "nrip_save",
- "nx",
- "ospke",
- "osvw",
- "osxsave",
- "overflow_recov",
- "pae",
- "pat",
- "pausefilter",
- "pclmulqdq",
- "pdpe1gb",
- "perfctr_core",
- "perfmon_v2",
- "pfthreshold",
- "pge",
- "pku",
- "pni",
- "popcnt",
- "pse",
- "pse36",
- "rdpid",
- "rdrand",
- "rdrnd",
- "rdseed",
- "rdtscp",
- "rep_good",
- "sep",
- "sha",
- "sha_ni",
- "smap",
- "smep",
- "ssbd",
- "sse",
- "sse2",
- "sse4_1",
- "sse4_2",
- "sse4a",
- "ssse3",
- "stibp",
- "succor",
- "svm",
- "syscall",
- "tsc",
- "tsc_adjust",
- "tsc_deadline_timer",
- "tsc_known_freq",
- "tsc_scale",
- "tscdeadline",
- "umip",
- "v_vmsave_vmload",
- "vaes",
- "vgif",
- "vmcb_clean",
- "vme",
- "vmmcall",
- "vnmi",
- "vpclmulqdq",
- "wbnoinvd",
- "x2apic",
- "xgetbv1",
- "xsave",
- "xsavec",
- "xsaveerptr",
- "xsaveopt",
- "xsaves"
- ],
- "l3_cache_size": 524288,
- "l2_cache_size": 7340032,
- "l1_data_cache_size": 917504,
- "l1_instruction_cache_size": 917504,
- "l2_cache_line_size": 512,
- "l2_cache_associativity": 8
- }
- },
- "commit_info": {
- "id": "7292f0fc29a9a16b9f34fb78b33363f459c37523",
- "time": "2025-12-29T23:28:35+00:00",
- "author_time": "2025-12-29T23:28:35+00:00",
- "dirty": true,
- "project": "noteflow",
- "branch": "master"
- },
- "benchmarks": [
- {
- "group": null,
- "name": "test_compute_rms_typical_chunk",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestComputeRmsBenchmark::test_compute_rms_typical_chunk",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.449000359978527e-06,
- "max": 4.6748999011470005e-05,
- "mean": 5.254236109048915e-06,
- "stddev": 5.406737467323485e-06,
- "rounds": 5993,
- "median": 3.550001565599814e-06,
- "iqr": 7.00001692166552e-08,
- "q1": 3.529999958118424e-06,
- "q3": 3.600000127335079e-06,
- "iqr_outliers": 1178,
- "stddev_outliers": 472,
- "outliers": "472;1178",
- "ld15iqr": 3.449000359978527e-06,
- "hd15iqr": 3.7089994293637574e-06,
- "ops": 190322.62335485584,
- "total": 0.03148863700153015,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_compute_rms_silence",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestComputeRmsBenchmark::test_compute_rms_silence",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.429999196669087e-06,
- "max": 5.3219000619719736e-05,
- "mean": 4.240495678882168e-06,
- "stddev": 3.363396020443664e-06,
- "rounds": 28075,
- "median": 3.5199991543777287e-06,
- "iqr": 4.000139597337693e-08,
- "q1": 3.499999365885742e-06,
- "q3": 3.540000761859119e-06,
- "iqr_outliers": 3382,
- "stddev_outliers": 1091,
- "outliers": "1091;3382",
- "ld15iqr": 3.440000000409782e-06,
- "hd15iqr": 3.6010005715070292e-06,
- "ops": 235821.48779918315,
- "total": 0.11905191618461686,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_compute_rms_speech",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestComputeRmsBenchmark::test_compute_rms_speech",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.359000402269885e-06,
- "max": 0.0003828669996437384,
- "mean": 4.002095375594478e-06,
- "stddev": 3.627403301293051e-06,
- "rounds": 34664,
- "median": 3.529999958118424e-06,
- "iqr": 8.999995770864189e-08,
- "q1": 3.4900003811344504e-06,
- "q3": 3.5800003388430923e-06,
- "iqr_outliers": 3474,
- "stddev_outliers": 717,
- "outliers": "717;3474",
- "ld15iqr": 3.359000402269885e-06,
- "hd15iqr": 3.718998414115049e-06,
- "ops": 249869.1075925341,
- "total": 0.138728634099607,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_energy_vad_process",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestVadBenchmark::test_energy_vad_process",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.510000169626437e-06,
- "max": 8.923800123739056e-05,
- "mean": 4.2858499286555344e-06,
- "stddev": 3.3828911298869402e-06,
- "rounds": 34603,
- "median": 3.610000931075774e-06,
- "iqr": 5.00003807246685e-08,
- "q1": 3.5900011425837874e-06,
- "q3": 3.640001523308456e-06,
- "iqr_outliers": 3951,
- "stddev_outliers": 1194,
- "outliers": "1194;3951",
- "ld15iqr": 3.5189987102057785e-06,
- "hd15iqr": 3.718998414115049e-06,
- "ops": 233325.94856248237,
- "total": 0.14830326508126745,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_streaming_vad_process_chunk",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestVadBenchmark::test_streaming_vad_process_chunk",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.5800003388430923e-06,
- "max": 0.00010890600060520228,
- "mean": 4.529488871753616e-06,
- "stddev": 3.7911652114629043e-06,
- "rounds": 43013,
- "median": 3.690000085043721e-06,
- "iqr": 5.000219971407205e-08,
- "q1": 3.6699984775623307e-06,
- "q3": 3.7200006772764027e-06,
- "iqr_outliers": 6322,
- "stddev_outliers": 1571,
- "outliers": "1571;6322",
- "ld15iqr": 3.598999683163129e-06,
- "hd15iqr": 3.7989993870723993e-06,
- "ops": 220775.4623785718,
- "total": 0.19482690484073828,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_energy_vad_speech_detection",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestVadBenchmark::test_energy_vad_speech_detection",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.5700013540918007e-06,
- "max": 6.072900032449979e-05,
- "mean": 4.189390656398419e-06,
- "stddev": 3.0348496487482607e-06,
- "rounds": 42001,
- "median": 3.6800011002924293e-06,
- "iqr": 3.9999576983973384e-08,
- "q1": 3.668999852379784e-06,
- "q3": 3.7089994293637574e-06,
- "iqr_outliers": 3425,
- "stddev_outliers": 1102,
- "outliers": "1102;3425",
- "ld15iqr": 3.609000486903824e-06,
- "hd15iqr": 3.769000613829121e-06,
- "ops": 238698.19790443964,
- "total": 0.17595859695938998,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_energy_vad_silence_detection",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestVadBenchmark::test_energy_vad_silence_detection",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.609000486903824e-06,
- "max": 5.7467999795335345e-05,
- "mean": 4.000768088102557e-06,
- "stddev": 2.261540943065658e-06,
- "rounds": 37260,
- "median": 3.690000085043721e-06,
- "iqr": 4.799949238076806e-08,
- "q1": 3.6710007407236844e-06,
- "q3": 3.7190002331044525e-06,
- "iqr_outliers": 2187,
- "stddev_outliers": 909,
- "outliers": "909;2187",
- "ld15iqr": 3.609000486903824e-06,
- "hd15iqr": 3.791001290665008e-06,
- "ops": 249952.003709935,
- "total": 0.1490686189627013,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_segmenter_idle_silence",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestSegmenterBenchmark::test_segmenter_idle_silence",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 5.79000698053278e-07,
- "max": 2.313799996045418e-05,
- "mean": 6.564644165580224e-07,
- "stddev": 5.256608812213979e-07,
- "rounds": 88818,
- "median": 6.099999154685065e-07,
- "iqr": 1.0000803740695119e-08,
- "q1": 6.099999154685065e-07,
- "q3": 6.200007192092016e-07,
- "iqr_outliers": 9309,
- "stddev_outliers": 1236,
- "outliers": "1236;9309",
- "ld15iqr": 5.989986675558612e-07,
- "hd15iqr": 6.389982445398346e-07,
- "ops": 1523311.812151533,
- "total": 0.05830585654985043,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_segmenter_speech_accumulation",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestSegmenterBenchmark::test_segmenter_speech_accumulation",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.92333337610277e-07,
- "max": 1.8954433350396964e-05,
- "mean": 6.502446459889802e-07,
- "stddev": 6.140182926016494e-07,
- "rounds": 69595,
- "median": 4.1999998453926913e-07,
- "iqr": 2.369997673667964e-08,
- "q1": 4.1296668011151877e-07,
- "q3": 4.366666568481984e-07,
- "iqr_outliers": 12877,
- "stddev_outliers": 7206,
- "outliers": "7206;12877",
- "ld15iqr": 3.92333337610277e-07,
- "hd15iqr": 4.7229999230088043e-07,
- "ops": 1537882.7125582318,
- "total": 0.04525377613760308,
- "iterations": 30
- }
- },
- {
- "group": null,
- "name": "test_segmenter_transition_idle_to_speech",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestSegmenterBenchmark::test_segmenter_transition_idle_to_speech",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 9.800005500437692e-07,
- "max": 0.001683107000644668,
- "mean": 1.1487242486780888e-06,
- "stddev": 6.228963542121108e-06,
- "rounds": 74963,
- "median": 1.0500007192604244e-06,
- "iqr": 2.0001607481390238e-08,
- "q1": 1.0399999155197293e-06,
- "q3": 1.0600015230011195e-06,
- "iqr_outliers": 4632,
- "stddev_outliers": 209,
- "outliers": "209;4632",
- "ld15iqr": 1.0099993232870474e-06,
- "hd15iqr": 1.0909989214269444e-06,
- "ops": 870530.9399977972,
- "total": 0.08611181585365557,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_get_rms",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestRmsLevelProviderBenchmark::test_get_rms",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.6690016713691875e-06,
- "max": 8.10370002000127e-05,
- "mean": 4.130748244247385e-06,
- "stddev": 2.485301198628286e-06,
- "rounds": 12794,
- "median": 3.760000254260376e-06,
- "iqr": 3.9999576983973384e-08,
- "q1": 3.7400004657683894e-06,
- "q3": 3.7800000427523628e-06,
- "iqr_outliers": 1053,
- "stddev_outliers": 342,
- "outliers": "342;1053",
- "ld15iqr": 3.6800011002924293e-06,
- "hd15iqr": 3.840001227217726e-06,
- "ops": 242086.891011243,
- "total": 0.05284879303690104,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_get_db",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestRmsLevelProviderBenchmark::test_get_db",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.790000846493058e-06,
- "max": 8.7697000708431e-05,
- "mean": 4.408465139383066e-06,
- "stddev": 2.99667632703755e-06,
- "rounds": 23625,
- "median": 3.920000381185673e-06,
- "iqr": 4.100184014532715e-08,
- "q1": 3.899998773704283e-06,
- "q3": 3.94100061384961e-06,
- "iqr_outliers": 2281,
- "stddev_outliers": 606,
- "outliers": "606;2281",
- "ld15iqr": 3.838998964056373e-06,
- "hd15iqr": 4.008999894722365e-06,
- "ops": 226836.31794351517,
- "total": 0.10414998891792493,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_rms_to_db_conversion",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestRmsLevelProviderBenchmark::test_rms_to_db_conversion",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 1.6821429328826654e-07,
- "max": 2.489571475702438e-06,
- "mean": 1.8857932409648374e-07,
- "stddev": 6.388702698443576e-08,
- "rounds": 196851,
- "median": 1.7607141801688287e-07,
- "iqr": 1.821458032022101e-09,
- "q1": 1.7532140970745657e-07,
- "q3": 1.7714286773947867e-07,
- "iqr_outliers": 25379,
- "stddev_outliers": 10574,
- "outliers": "10574;25379",
- "ld15iqr": 1.728214036640046e-07,
- "hd15iqr": 1.7996425023219282e-07,
- "ops": 5302808.273341595,
- "total": 0.03712202852771692,
- "iterations": 28
- }
- },
- {
- "group": null,
- "name": "test_array_copy",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestNumpyOperationsBenchmark::test_array_copy",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 1.868000254034996e-07,
- "max": 4.698279954027385e-06,
- "mean": 2.0344677179673465e-07,
- "stddev": 6.667938193365902e-08,
- "rounds": 193799,
- "median": 1.9279999833088368e-07,
- "iqr": 2.400047378614549e-09,
- "q1": 1.915999746415764e-07,
- "q3": 1.9400002202019095e-07,
- "iqr_outliers": 11953,
- "stddev_outliers": 7096,
- "outliers": "7096;11953",
- "ld15iqr": 1.8799997633323074e-07,
- "hd15iqr": 1.976399653358385e-07,
- "ops": 4915290.575360459,
- "total": 0.03942778092743538,
- "iterations": 25
- }
- },
- {
- "group": null,
- "name": "test_array_concatenate_small",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestNumpyOperationsBenchmark::test_array_concatenate_small",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 1.019998308038339e-06,
- "max": 4.340899977250956e-05,
- "mean": 1.1834804079043573e-06,
- "stddev": 8.861083095740762e-07,
- "rounds": 96071,
- "median": 1.059999704011716e-06,
- "iqr": 2.9998773243278265e-08,
- "q1": 1.0500007192604244e-06,
- "q3": 1.0799994925037026e-06,
- "iqr_outliers": 8523,
- "stddev_outliers": 1612,
- "outliers": "1612;8523",
- "ld15iqr": 1.019998308038339e-06,
- "hd15iqr": 1.128999429056421e-06,
- "ops": 844965.4031626476,
- "total": 0.11369814626777952,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_array_concatenate_large",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestNumpyOperationsBenchmark::test_array_concatenate_large",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 2.999999196617864e-06,
- "max": 3.1288000172935426e-05,
- "mean": 3.3309941825889935e-06,
- "stddev": 1.1706137650278387e-06,
- "rounds": 26640,
- "median": 3.2099997042678297e-06,
- "iqr": 1.4000215742271394e-07,
- "q1": 3.1299987313104793e-06,
- "q3": 3.2700008887331933e-06,
- "iqr_outliers": 727,
- "stddev_outliers": 429,
- "outliers": "429;727",
- "ld15iqr": 2.999999196617864e-06,
- "hd15iqr": 3.4889999369625002e-06,
- "ops": 300210.67140464246,
- "total": 0.08873768502417079,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_array_square",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestNumpyOperationsBenchmark::test_array_square",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.3599999369471336e-07,
- "max": 3.1568999474984595e-06,
- "mean": 3.5837566215609753e-07,
- "stddev": 9.067377964642227e-08,
- "rounds": 141443,
- "median": 3.46000069839647e-07,
- "iqr": 4.950015863869318e-09,
- "q1": 3.440000000409782e-07,
- "q3": 3.489500159048475e-07,
- "iqr_outliers": 4615,
- "stddev_outliers": 3055,
- "outliers": "3055;4615",
- "ld15iqr": 3.3745000109774993e-07,
- "hd15iqr": 3.5644998206407765e-07,
- "ops": 2790368.056758359,
- "total": 0.0506897287823449,
- "iterations": 20
- }
- },
- {
- "group": null,
- "name": "test_array_mean",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestNumpyOperationsBenchmark::test_array_mean",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 2.67999894276727e-06,
- "max": 5.860900091647636e-05,
- "mean": 2.954065997680174e-06,
- "stddev": 1.737545708579586e-06,
- "rounds": 14363,
- "median": 2.749999111983925e-06,
- "iqr": 3.0999217415228486e-08,
- "q1": 2.730001142481342e-06,
- "q3": 2.7610003598965704e-06,
- "iqr_outliers": 897,
- "stddev_outliers": 283,
- "outliers": "283;897",
- "ld15iqr": 2.688999302336015e-06,
- "hd15iqr": 2.8089998522773385e-06,
- "ops": 338516.47213884164,
- "total": 0.042429249924680335,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_list_append",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestBufferOperationsBenchmark::test_list_append",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 2.5500003175693564e-07,
- "max": 0.0006309430000328575,
- "mean": 3.0804353053803723e-06,
- "stddev": 1.165474475076179e-05,
- "rounds": 50356,
- "median": 1.2219500604260248e-06,
- "iqr": 5.370500730350612e-07,
- "q1": 1.1269499736954459e-06,
- "q3": 1.664000046730507e-06,
- "iqr_outliers": 6448,
- "stddev_outliers": 1234,
- "outliers": "1234;6448",
- "ld15iqr": 3.254500370530877e-07,
- "hd15iqr": 2.470399977028137e-06,
- "ops": 324629.44384950167,
- "total": 0.15511840023773402,
- "iterations": 20
- }
- },
- {
- "group": null,
- "name": "test_list_clear",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestBufferOperationsBenchmark::test_list_clear",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.850000211969018e-06,
- "max": 4.9208001655642875e-05,
- "mean": 4.4985737419180015e-06,
- "stddev": 1.841199779668339e-06,
- "rounds": 132644,
- "median": 4.05999890062958e-06,
- "iqr": 7.100061338860542e-08,
- "q1": 4.0289996832143515e-06,
- "q3": 4.100000296602957e-06,
- "iqr_outliers": 16629,
- "stddev_outliers": 10011,
- "outliers": "10011;16629",
- "ld15iqr": 3.9289989217650145e-06,
- "hd15iqr": 4.208999598631635e-06,
- "ops": 222292.67705049165,
- "total": 0.5967088154229714,
- "iterations": 1
- }
- },
- {
- "group": null,
- "name": "test_sum_lengths_naive",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestBufferOperationsBenchmark::test_sum_lengths_naive",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 5.070000042906031e-07,
- "max": 6.428855003832723e-05,
- "mean": 5.806604613237722e-07,
- "stddev": 2.6627529629995264e-07,
- "rounds": 94340,
- "median": 5.419999979494606e-07,
- "iqr": 7.050039130263115e-09,
- "q1": 5.389999387261923e-07,
- "q3": 5.460499778564554e-07,
- "iqr_outliers": 15957,
- "stddev_outliers": 6262,
- "outliers": "6262;15957",
- "ld15iqr": 5.284499820845667e-07,
- "hd15iqr": 5.569499990087934e-07,
- "ops": 1722176.8427632046,
- "total": 0.05477950792128468,
- "iterations": 20
- }
- },
- {
- "group": null,
- "name": "test_cached_length",
- "fullname": "tests/benchmarks/test_hot_paths.py::TestBufferOperationsBenchmark::test_cached_length",
- "params": null,
- "param": null,
- "extra_info": {},
- "options": {
- "disable_gc": false,
- "timer": "perf_counter",
- "min_rounds": 5,
- "max_time": 1.0,
- "min_time": 5e-06,
- "warmup": false
- },
- "stats": {
- "min": 3.2465758933590036e-08,
- "max": 5.197123331751368e-07,
- "mean": 3.6134694196367755e-08,
- "stddev": 9.28983695660366e-09,
- "rounds": 198060,
- "median": 3.452054418269417e-08,
- "iqr": 4.1095455805451644e-10,
- "q1": 3.431507313308268e-08,
- "q3": 3.4726027691137196e-08,
- "iqr_outliers": 19406,
- "stddev_outliers": 10291,
- "outliers": "10291;19406",
- "ld15iqr": 3.3705474965095723e-08,
- "hd15iqr": 3.5342465757634747e-08,
- "ops": 27674234.478522852,
- "total": 0.007156837532532597,
- "iterations": 146
- }
- }
- ],
- "datetime": "2025-12-29T23:37:15.755048+00:00",
- "version": "5.2.3"
-}
\ No newline at end of file
diff --git a/.claudectx/codefixes.md b/.claudectx/codefixes.md
deleted file mode 100644
index 1ff1dce..0000000
--- a/.claudectx/codefixes.md
+++ /dev/null
@@ -1,1034 +0,0 @@
-> git -c user.useConfigOnly=true commit --quiet --allow-empty-message --file -
-Running pre-commit quality checks...
-=== TypeScript Type Check ===
-cd client && npm run type-check
-
-> noteflow-client@0.1.0 type-check
-> tsc --noEmit
-
-=== Biome Lint ===
-cd client && HYGIENE_DIR=/home/trav/repos/noteflow/.hygeine npm run lint
-
-> noteflow-client@0.1.0 lint
-> mkdir -p ${HYGIENE_DIR:-../.hygeine} && biome lint . --reporter=json > ${HYGIENE_DIR:-../.hygeine}/biome.json && eslint . --format json --output-file ${HYGIENE_DIR:-../.hygeine}/eslint.json
-
-The --json option is unstable/experimental and its output might change between patches/minor releases.
-=== TypeScript Quality Tests ===
-cd client && npm run test:quality
-
-> noteflow-client@0.1.0 test:quality
-> vitest run src/test/code-quality.test.ts
-
-
- RUN v4.0.17 /home/trav/repos/noteflow/client
-
- ✓ src/test/code-quality.test.ts (28 tests) 244ms
-
- Test Files 1 passed (1)
- Tests 28 passed (28)
- Start at 09:52:07
- Duration 1.20s (transform 370ms, setup 551ms, import 60ms, tests 244ms, environment 257ms)
-
-=== TypeScript Coverage (Vitest) ===
-cd client && npm run test -- --coverage
-
-> noteflow-client@0.1.0 test
-> vitest run --coverage
-
-
- RUN v4.0.17 /home/trav/repos/noteflow/client
- Coverage enabled with v8
-
- ❯ src/api/adapters/tauri/sections/meetings.test.ts (10 tests | 1 failed) 59ms
- ✓ creates meetings and caches them 14ms
- ✓ lists meetings and caches results 10ms
- ✓ gets meetings and caches them 3ms
- ✓ stops meetings and logs 3ms
- ✓ deletes meetings and removes cached items 3ms
- × starts transcription and returns a stream 6ms
- ✓ records failures when transcription fails 4ms
- ✓ resets stream state and logs metadata 2ms
- ✓ generates summaries using preference templates 9ms
- ✓ logs summary failures 3ms
- ✓ src/hooks/auth/use-cloud-consent.test.ts (14 tests) 765ms
- ✓ src/hooks/auth/use-huggingface-token.test.ts (17 tests) 933ms
-stderr | src/pages/Tasks.test.tsx > TasksPage > renders kanban view and forwards updates
-Warning: An update to TasksPage inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TasksPage (/home/trav/repos/noteflow/client/src/pages/Tasks.tsx:72:77)
- at Router (/home/trav/repos/noteflow/client/node_modules/react-router/dist/umd/react-router.development.js:1207:17)
- at MemoryRouter (/home/trav/repos/noteflow/client/node_modules/react-router/dist/umd/react-router.development.js:1101:7)
-Warning: An update to TasksPage inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TasksPage (/home/trav/repos/noteflow/client/src/pages/Tasks.tsx:72:77)
- at Router (/home/trav/repos/noteflow/client/node_modules/react-router/dist/umd/react-router.development.js:1207:17)
- at MemoryRouter (/home/trav/repos/noteflow/client/node_modules/react-router/dist/umd/react-router.development.js:1101:7)
-
- ✓ src/pages/Recording.logic.test.tsx (7 tests | 2 skipped) 1185ms
- ✓ shows desktop-only message when not running in tauri without simulation 806ms
- ✓ src/pages/Tasks.test.tsx (12 tests) 789ms
- ✓ filters tasks by priority and search query 311ms
- ✓ src/components/features/entities/entity-management-panel.test.tsx (4 tests) 836ms
- ✓ adds, edits, and deletes entities when persisted 481ms
- ✓ src/components/features/analytics/logs-tab.test.tsx (16 tests) 1161ms
- ✓ src/hooks/sync/use-integration-sync.test.ts (43 tests) 805ms
- ✓ polls until sync completes when initial status is running 503ms
- ✓ src/hooks/processing/use-post-processing.test.ts (30 tests) 1129ms
- ✓ src/pages/MeetingDetail.test.tsx (7 tests) 927ms
- ✓ src/pages/Meetings.test.tsx (11 tests) 1033ms
- ✓ src/pages/Recording.test.tsx (5 tests) 1028ms
- ✓ src/components/features/settings/export-ai-section.test.tsx (22 tests) 671ms
-stderr | src/hooks/recording/use-recording-session.test.tsx > useRecordingSession > closes streams when unmounted before transcription resolves
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
- ✓ src/components/features/meetings/processing-status.test.tsx (22 tests) 616ms
-stderr | src/hooks/recording/use-recording-session.test.tsx > useRecordingSession > skips start error handling when unmounted
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
- ✓ src/components/common/dialogs/confirmation-dialog.test.tsx (2 tests) 574ms
- ✓ renders content and handles confirm 403ms
-stderr | src/hooks/recording/use-recording-session.test.tsx > useRecordingSession > skips stop updates when unmounted before stop resolves
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
-stderr | src/hooks/recording/use-recording-session.test.tsx > useRecordingSession > skips stop error handling when unmounted
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
- ✓ src/components/features/analytics/performance-tab.test.tsx (16 tests) 905ms
- ✓ src/hooks/recording/use-recording-session.test.tsx (26 tests) 676ms
- ✓ src/pages/meeting-detail/header.test.tsx (8 tests) 553ms
- ✓ src/pages/Home.test.tsx (8 tests) 889ms
- ✓ has view all link for meetings 460ms
- ✓ src/components/features/notes/timestamped-notes-editor.test.tsx (4 tests) 804ms
- ✓ saves notes manually and shows history 341ms
- ✓ src/components/features/projects/ProjectScopeFilter.test.tsx (5 tests) 563ms
- ✓ src/components/features/recording/audio-device-selector.test.tsx (3 tests) 577ms
- ✓ renders empty device list state 315ms
- ✓ src/components/features/connectivity/server-switch-confirmation-dialog.test.tsx (2 tests) 395ms
- ✓ renders server details and confirms 314ms
-Error: useProjects must be used within ProjectProvider
- at Module.useProjects (/home/trav/repos/noteflow/client/src/contexts/project-state.ts:25:11)
- at ProjectConsumer (/home/trav/repos/noteflow/client/src/contexts/project-context.test.tsx:124:3)
- at renderWithHooks (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:15486:18)
- at mountIndeterminateComponent (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:20103:13)
- at beginWork (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:21626:16)
- at HTMLUnknownElement.callCallback (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:4164:14)
- at HTMLUnknownElement.callTheUserObjectsOperation (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
- at innerInvokeEventListeners (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:360:16)
- at invokeEventListeners (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:296:3)
- at HTMLUnknownElementImpl._dispatch (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:243:9)
-Error: useProjects must be used within ProjectProvider
- at Module.useProjects (/home/trav/repos/noteflow/client/src/contexts/project-state.ts:25:11)
- at ProjectConsumer (/home/trav/repos/noteflow/client/src/contexts/project-context.test.tsx:124:3)
- at renderWithHooks (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:15486:18)
- at mountIndeterminateComponent (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:20103:13)
- at beginWork (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:21626:16)
- at HTMLUnknownElement.callCallback (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:4164:14)
- at HTMLUnknownElement.callTheUserObjectsOperation (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
- at innerInvokeEventListeners (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:360:16)
- at invokeEventListeners (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:296:3)
- at HTMLUnknownElementImpl._dispatch (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:243:9)
-stderr | src/contexts/project-context.test.tsx > ProjectProvider > throws when useProjects is used outside provider
-The above error occurred in the component:
-
- at ProjectConsumer (/home/trav/repos/noteflow/client/src/contexts/project-context.test.tsx:194:21)
- at ErrorBoundary (/home/trav/repos/noteflow/client/src/contexts/project-context.test.tsx:188:9)
-
-React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.
-
- ✓ src/api/adapters/tauri/stream.test.ts (19 tests) 516ms
- ✓ src/contexts/project-context.test.tsx (9 tests) 615ms
- ✓ src/lib/preferences/storage.test.ts (14 tests) 552ms
- ✓ src/components/features/calendar/upcoming-meetings.test.tsx (7 tests) 417ms
-stderr | src/hooks/audio/use-asr-config.test.ts > useAsrConfig > updateConfig > refreshes config when job completes without new configuration
-Warning: You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one.
-
-stderr | src/hooks/audio/use-asr-config.test.ts > useAsrConfig > updateConfig > refreshes config when job completes without new configuration
-Warning: You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one.
-
- ✓ src/components/ui/dropdown-menu.test.tsx (1 test) 472ms
- ✓ renders menu content and items when open 468ms
- ✓ src/pages/meeting-detail/index.test.tsx (12 tests) 379ms
-stderr | src/hooks/audio/use-audio-devices.test.ts > useAudioDevices > rethrows input selection errors in tauri mode
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
-stderr | src/hooks/audio/use-audio-devices.test.ts > useAudioDevices > rethrows output selection errors in tauri mode
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
-stderr | src/hooks/audio/use-audio-devices.test.ts > useAudioDevices > rethrows system device selection errors in tauri mode
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
- ✓ src/pages/Analytics.test.tsx (5 tests) 683ms
- ✓ requests speech analysis data when switching tabs 418ms
-stderr | src/hooks/audio/use-audio-devices.test.ts > useAudioDevices > rethrows dual capture toggle errors
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
-stderr | src/hooks/audio/use-audio-devices.test.ts > useAudioDevices > rethrows audio mix errors
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
- ✓ src/pages/meeting-detail/ask-panel.test.tsx (5 tests) 716ms
- ✓ renders empty state and disables submit for blank input 524ms
-stderr | src/hooks/audio/use-audio-devices.test.ts > useAudioDevices > ignores tauri audio test events when not testing input
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
- ✓ src/components/features/analytics/log-timeline.test.tsx (3 tests) 430ms
- ✓ renders groups with badges, gaps, and hidden count 324ms
-stderr | src/hooks/data/use-async-data.test.tsx > useAsyncData > resets mutation state
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
- ✓ src/components/features/workspace/workspace-switcher.test.tsx (2 tests) 418ms
- ✓ lists workspaces and switches on selection 373ms
- ✓ src/components/features/sync/preferences-sync-status.test.tsx (2 tests) 244ms
- ✓ src/hooks/audio/use-audio-devices.test.ts (33 tests) 294ms
- ✓ src/hooks/data/use-async-data.test.tsx (6 tests) 249ms
-stderr | src/pages/meeting-detail/use-meeting-detail.test.ts > useMeetingDetail > refreshes meeting after post-processing completes
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
-stderr | src/pages/Recording.behavior.test.tsx > RecordingPage behavior > toggles pinned entities from transcript cards
-Warning: An update to RecordingPage inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at RecordingPage (/home/trav/repos/noteflow/client/src/pages/Recording.tsx:62:40)
- at Router (/home/trav/repos/noteflow/client/node_modules/react-router/dist/umd/react-router.development.js:1207:17)
- at MemoryRouter (/home/trav/repos/noteflow/client/node_modules/react-router/dist/umd/react-router.development.js:1101:7)
-
- ✓ src/pages/meeting-detail/use-meeting-detail.test.ts (7 tests) 447ms
-stderr | src/pages/Recording.behavior.test.tsx > RecordingPage behavior > toggles pinned entities from transcript cards
-Warning: An update to RecordingPage inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at RecordingPage (/home/trav/repos/noteflow/client/src/pages/Recording.tsx:62:40)
- at Router (/home/trav/repos/noteflow/client/node_modules/react-router/dist/umd/react-router.development.js:1207:17)
- at MemoryRouter (/home/trav/repos/noteflow/client/node_modules/react-router/dist/umd/react-router.development.js:1101:7)
-
-stderr | src/pages/Recording.behavior.test.tsx > RecordingPage behavior > jumps to live when indicator is clicked
-Warning: An update to RecordingPage inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at RecordingPage (/home/trav/repos/noteflow/client/src/pages/Recording.tsx:62:40)
- at Router (/home/trav/repos/noteflow/client/node_modules/react-router/dist/umd/react-router.development.js:1207:17)
- at MemoryRouter (/home/trav/repos/noteflow/client/node_modules/react-router/dist/umd/react-router.development.js:1101:7)
-
- ✓ src/pages/Recording.behavior.test.tsx (11 tests) 527ms
- ✓ src/components/features/calendar/calendar-connection-panel.test.tsx (3 tests) 427ms
- ✓ src/pages/NotFound.test.tsx (3 tests) 257ms
- ✓ src/api/adapters/mock/index.test.ts (12 tests) 486ms
- ✓ src/components/ui/sheet.test.tsx (1 test) 221ms
- ✓ src/test/code-quality.test.ts (28 tests) 335ms
-Error: Kaboom
- at Thrower (/home/trav/repos/noteflow/client/src/components/common/error-boundary.test.tsx:7:9)
- at renderWithHooks (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:15486:18)
- at mountIndeterminateComponent (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:20103:13)
- at beginWork (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:21626:16)
- at HTMLUnknownElement.callCallback (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:4164:14)
- at HTMLUnknownElement.callTheUserObjectsOperation (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
- at innerInvokeEventListeners (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:360:16)
- at invokeEventListeners (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:296:3)
- at HTMLUnknownElementImpl._dispatch (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:243:9)
- at HTMLUnknownElementImpl.dispatchEvent (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:114:17)
- ✓ src/components/ui/dialog.test.tsx (1 test) 276ms
-Error: Kaboom
- at Thrower (/home/trav/repos/noteflow/client/src/components/common/error-boundary.test.tsx:7:9)
- at renderWithHooks (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:15486:18)
- at mountIndeterminateComponent (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:20103:13)
- at beginWork (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:21626:16)
- at HTMLUnknownElement.callCallback (/home/trav/repos/noteflow/client/node_modules/react-dom/cjs/react-dom.development.js:4164:14)
- at HTMLUnknownElement.callTheUserObjectsOperation (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
- at innerInvokeEventListeners (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:360:16)
- at invokeEventListeners (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:296:3)
- at HTMLUnknownElementImpl._dispatch (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:243:9)
- at HTMLUnknownElementImpl.dispatchEvent (/home/trav/repos/noteflow/client/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:114:17)
- ✓ src/lib/ai-providers/strategies/strategies.test.ts (99 tests) 231ms
- ✓ src/hooks/processing/use-diarization.test.ts (26 tests) 152ms
- ✓ src/pages/meeting-detail/summary-panel.test.tsx (5 tests) 231ms
- ✓ src/pages/Home.behavior.test.tsx (4 tests) 782ms
- ✓ renders active recording and uses project meeting path 533ms
- ✓ src/components/common/error-boundary.test.tsx (2 tests) 657ms
- ✓ renders fallback UI on error and reloads 601ms
- ✓ src/components/features/analytics/logs-tab-list.test.tsx (4 tests) 126ms
- ✓ src/hooks/recording/use-recording-app-policy.test.tsx (5 tests) 221ms
- ✓ src/components/ui/resizable.test.tsx (1 test) 131ms
- ✓ src/components/features/recording/recording-header.test.tsx (3 tests) 262ms
- ✓ src/api/adapters/tauri/__tests__/transcription-mapping.test.ts (8 tests) 205ms
- ✓ src/contexts/workspace-context.test.tsx (2 tests) 178ms
- ✓ src/pages/meeting-detail/entities-panel.test.tsx (2 tests) 419ms
- ✓ renders empty state with extract button 369ms
- ✓ src/components/common/nav-link.test.tsx (2 tests) 224ms
- ✓ src/components/features/connectivity/api-mode-indicator.test.tsx (3 tests) 89ms
- ✓ src/lib/state/entities.test.ts (47 tests) 116ms
- ✓ src/components/features/entities/entity-highlight.test.tsx (3 tests) 364ms
- ✓ src/hooks/data/use-project-members.test.tsx (3 tests) 144ms
- ✓ src/components/features/recording/notes-quick-actions.test.tsx (2 tests) 55ms
- ✓ src/hooks/data/use-guarded-mutation.test.tsx (2 tests) 172ms
- ✓ src/components/features/settings/integrations-section/use-integration-handlers.test.tsx (28 tests) 227ms
- ✓ src/components/features/recording/jump-to-live-indicator.test.tsx (1 test) 251ms
- ✓ src/lib/preferences/api.test.ts (8 tests) 224ms
- ✓ src/lib/utils/format.test.ts (40 tests) 110ms
- ✓ src/hooks/auth/use-auth-flow.test.tsx (6 tests) 119ms
- ✓ src/hooks/audio/use-streaming-config.test.tsx (2 tests) 133ms
- ✓ src/components/features/recording/audio-level-meter.test.tsx (10 tests) 147ms
- ✓ src/components/features/calendar/calendar-events-panel.test.tsx (4 tests) 257ms
-stderr | src/components/ui/markdown-editor.test.tsx > MarkdownEditor > renders toolbar and executes formatting commands
-Warning: Received `false` for a non-boolean attribute `pressed`.
-
-If you want to write it to the DOM, pass a string instead: pressed="false" or pressed={value.toString()}.
-
-If you used to conditionally omit it with pressed={condition && value}, pass pressed={condition ? value : undefined} instead.
- at button
- at Toggle (/home/trav/repos/noteflow/client/src/components/ui/markdown-editor.test.tsx:48:20)
- at TooltipTrigger (/home/trav/repos/noteflow/client/src/components/ui/markdown-editor.test.tsx:23:28)
- at Tooltip (/home/trav/repos/noteflow/client/src/components/ui/markdown-editor.test.tsx:20:21)
- at ToolbarButton (/home/trav/repos/noteflow/client/src/components/ui/markdown-editor.tsx:25:32)
- at div
- at EditorToolbar (/home/trav/repos/noteflow/client/src/components/ui/markdown-editor.tsx:86:26)
- at div
- at MarkdownEditor (/home/trav/repos/noteflow/client/src/components/ui/markdown-editor.tsx:228:31)
-
- ✓ src/components/ui/markdown-editor.test.tsx (7 tests) 134ms
- ✓ src/api/adapters/cached/index.test.ts (8 tests) 102ms
- ✓ src/hooks/auth/use-oidc-providers.test.ts (23 tests) 88ms
- ✓ src/components/ui/ui-components.test.tsx (5 tests) 88ms
-stderr | src/hooks/ui/use-mobile.test.tsx > useIsMobile > updates when matchMedia change fires
-Warning: An update to TestComponent inside a test was not wrapped in act(...).
-
-When testing, code that causes React state updates should be wrapped into act(...):
-
-act(() => {
- /* fire events that update state */
-});
-/* assert on the output */
-
-This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
- at TestComponent (/home/trav/repos/noteflow/client/node_modules/@testing-library/react/dist/pure.js:328:5)
-
- ✓ src/hooks/auth/use-oauth-flow.test.ts (22 tests) 171ms
- ✓ src/components/common/stats-card.test.tsx (3 tests) 35ms
- ✓ src/lib/observability/messages.test.ts (60 tests) 123ms
- ✓ src/hooks/ui/use-mobile.test.tsx (2 tests) 89ms
- ✓ src/integration/recording-session.integration.test.tsx (5 tests) 110ms
- ✓ src/api/core/reconnection.test.ts (21 tests) 89ms
- ✓ src/components/features/recording/confidence-indicator.test.tsx (7 tests) 68ms
- ✓ src/lib/preferences/tauri.test.ts (8 tests) 70ms
- ✓ src/hooks/processing/use-assistant.test.ts (5 tests) 49ms
- ✓ src/lib/observability/events.test.ts (32 tests) 78ms
- ✓ src/components/features/recording/stat-card.test.tsx (5 tests) 82ms
- ✓ src/components/features/recording/idle-state.test.tsx (5 tests) 66ms
- ✓ src/components/ui/select.test.tsx (2 tests) 60ms
- ✓ src/lib/observability/events.integration.test.ts (24 tests) 67ms
- ✓ src/components/features/recording/transcript-segment-actions.test.tsx (3 tests) 84ms
- ✓ src/components/features/connectivity/offline-banner.test.tsx (5 tests) 87ms
- ✓ src/components/common/badges/speaker-badge.test.tsx (5 tests) 164ms
- ✓ src/components/common/badges/annotation-type-badge.test.tsx (2 tests) 49ms
- ✓ src/hooks/sync/use-preferences-sync.test.tsx (4 tests) 61ms
- ✓ src/api/types/errors.test.ts (37 tests) 59ms
- ✓ src/lib/integrations/oauth.test.ts (6 tests) 98ms
- ✓ src/components/features/recording/buffering-indicator.test.tsx (4 tests) 117ms
- ✓ src/components/features/connectivity/connection-status.test.tsx (4 tests) 189ms
- ✓ src/hooks/sync/use-integration-validation.test.tsx (3 tests) 36ms
- ✓ src/hooks/sync/use-calendar-sync.test.tsx (7 tests) 95ms
- ✓ src/hooks/processing/use-entity-extraction.test.tsx (5 tests) 51ms
- ✓ src/contexts/connection-context.test.tsx (4 tests) 169ms
- ✓ src/hooks/sync/use-webhooks.test.tsx (3 tests) 26ms
- ✓ src/components/features/entities/animated-transcription.test.tsx (3 tests) 34ms
- ✓ src/components/ui/slider.test.tsx (1 test) 64ms
- ✓ src/hooks/sync/use-meeting-reminders.test.tsx (5 tests) 159ms
- ✓ src/lib/preferences/sync.test.ts (13 tests) 89ms
- ✓ src/components/features/recording/unified-status-row.test.tsx (4 tests) 120ms
- ✓ src/hooks/ui/use-toast.test.ts (5 tests) 59ms
- ✓ src/api/index.test.ts (5 tests) 56ms
- ✓ src/lib/observability/group-summarizer.test.ts (27 tests) 70ms
- ✓ src/components/features/recording/vad-indicator.test.tsx (6 tests) 91ms
-stderr | src/pages/meeting-detail/transcript-row.test.tsx > MeetingTranscriptRow > applies selected styles and disables animation when requested
-Warning: Received `false` for a non-boolean attribute `initial`.
-
-If you want to write it to the DOM, pass a string instead: initial="false" or initial={value.toString()}.
-
-If you used to conditionally omit it with initial={condition && value}, pass initial={condition ? value : undefined} instead.
- at div
- at div (/home/trav/repos/noteflow/client/src/pages/meeting-detail/transcript-row.test.tsx:4:21)
- at MeetingTranscriptRow (/home/trav/repos/noteflow/client/src/pages/meeting-detail/transcript-row.tsx:14:107)
-
- ✓ src/lib/observability/groups.test.ts (18 tests) 37ms
- ✓ src/pages/meeting-detail/transcript-row.test.tsx (2 tests) 67ms
- ✓ src/lib/storage/crypto.test.ts (14 tests) 46ms
- ✓ src/hooks/auth/use-secure-integration-secrets.test.tsx (3 tests) 48ms
- ✓ src/components/features/recording/speaker-distribution.test.tsx (2 tests) 63ms
- ✓ src/components/features/recording/recording-components.test.tsx (4 tests) 144ms
- ✓ src/lib/audio/device-ids.test.ts (21 tests) 51ms
- ✓ src/lib/observability/summarizer.test.ts (27 tests) 60ms
- ✓ src/lib/cache/meeting-cache.test.ts (18 tests) 38ms
- ✓ src/hooks/processing/events.test.tsx (2 tests) 54ms
- ✓ src/components/features/recording/in-transcript-search.test.tsx (1 test) 42ms
- ✓ src/hooks/ui/use-recording-panels.test.tsx (3 tests) 24ms
- ✓ src/lib/observability/converters.test.ts (18 tests) 35ms
- ✓ src/pages/Projects.test.tsx (1 test) 61ms
- ✓ src/lib/ai-providers/strategies/google.test.ts (3 tests) 13ms
- ✓ src/hooks/ui/use-panel-preferences.test.ts (4 tests) 39ms
- ✓ src/lib/utils/polling.test.ts (8 tests) 154ms
- ✓ src/api/core/helpers.test.ts (13 tests) 43ms
- ✓ src/lib/utils/async.test.ts (7 tests) 44ms
- ✓ src/lib/system/events.test.tsx (6 tests) 44ms
- ✓ src/hooks/data/use-project.test.tsx (2 tests) 18ms
- ✓ src/api/adapters/tauri/__tests__/misc-mapping.test.ts (8 tests) 32ms
- ✓ src/api/adapters/cached/audio.test.ts (2 tests) 11ms
-Not implemented: navigation to another Document
- ✓ src/api/adapters/tauri/utils.test.ts (2 tests) 14ms
- ✓ src/lib/utils/download.test.ts (3 tests) 42ms
- ✓ src/api/adapters/tauri/__tests__/core-mapping.test.ts (8 tests) 35ms
- ✓ src/lib/observability/client.test.ts (2 tests) 8ms
- ✓ src/lib/audio/device-persistence.integration.test.ts (8 tests) 34ms
- ✓ src/api/adapters/tauri/__tests__/environment.test.ts (3 tests) 39ms
- ✓ src/api/adapters/mock/data.test.ts (6 tests) 21ms
- ✓ src/api/adapters/tauri/sections/sections.test.ts (7 tests) 29ms
- ✓ src/lib/storage/utils.test.ts (7 tests) 33ms
- ✓ src/lib/ai-providers/model-catalog-utils.test.ts (4 tests) 29ms
- ✓ src/hooks/ui/use-animated-words.test.ts (3 tests) 37ms
- ✓ src/lib/preferences/validation.test.ts (3 tests) 16ms
- ✓ src/api/adapters/cached/projects.test.ts (3 tests) 11ms
- ✓ src/lib/preferences/validation-events.test.ts (3 tests) 15ms
- ✓ src/lib/preferences/integrations.test.ts (2 tests) 12ms
- ✓ src/lib/utils/index.test.ts (7 tests) 27ms
- ✓ src/lib/utils/object.test.ts (5 tests) 24ms
- ✓ src/api/adapters/tauri/constants.test.ts (4 tests) 10ms
- ✓ src/lib/integrations/utils.test.ts (6 tests) 14ms
- ✓ src/lib/ai-providers/strategies/custom.test.ts (4 tests) 16ms
- ❯ src/hooks/audio/use-asr-config.test.ts (12 tests | 5 failed) 21415ms
- ✓ starts with isLoading true 15ms
- ✓ loads configuration successfully 61ms
- ✓ handles load error 58ms
- ✓ refreshes configuration 56ms
- ✓ starts reconfiguration job successfully 59ms
- ✓ handles rejected update 55ms
- ✓ handles API error during update 56ms
- × updates config when job completes with new configuration 5016ms
- × refreshes config when job completes without new configuration 5008ms
- × stops reconfiguring and reports failure status 5007ms
- × stops reconfiguring and reports cancelled status 5003ms
- × can be called without error 1018ms
- ✓ src/api/adapters/cached/apps.test.ts (2 tests) 10ms
- ✓ src/api/adapters/cached/templates.test.ts (2 tests) 10ms
- ✓ src/lib/config/app-config.test.ts (4 tests) 12ms
- ✓ src/api/adapters/cached/triggers.test.ts (2 tests) 12ms
- ✓ src/lib/config/config.test.ts (4 tests) 9ms
- ✓ src/lib/utils/event-emitter.test.ts (3 tests) 10ms
- ✓ src/lib/audio/speaker.test.ts (2 tests) 32ms
- ✓ src/contexts/storage.test.ts (4 tests) 19ms
- ✓ src/components/features/settings/integrations-section/helpers.test.ts (2 tests) 15ms
- ✓ src/api/adapters/tauri/sections/summarization.test.ts (2 tests) 9ms
- ✓ src/api/core/connection.test.ts (3 tests) 7ms
- ✓ src/lib/utils/id.test.ts (2 tests) 7ms
- ✓ src/components/features/analytics/analytics-utils.test.ts (3 tests) 11ms
- ✓ src/lib/preferences/local-only-keys.test.ts (2 tests) 8ms
- ✓ src/pages/meeting-detail/constants.test.ts (2 tests) 7ms
- ✓ src/lib/preferences/tags.test.ts (1 test) 6ms
- ✓ src/lib/ui/cva.test.ts (1 test) 4ms
- ✓ src/components/features/recording/index.test.ts (1 test) 4ms
- ❯ src/api/adapters/mock/stream.test.ts (5 tests | 5 failed) 50069ms
- × emits VAD and transcript updates 10043ms
- × stops emitting after close 10009ms
- × emits vad_end when activity stops 10005ms
- × does not emit when no callback registered 10005ms
- × accepts audio chunks without throwing 10005ms
-
-⎯⎯⎯⎯⎯⎯ Failed Tests 11 ⎯⎯⎯⎯⎯⎯⎯
-
- FAIL src/hooks/audio/use-asr-config.test.ts > useAsrConfig > updateConfig > updates config when job completes with new configuration
-Error: Test timed out in 5000ms.
-If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
- ❯ src/hooks/audio/use-asr-config.test.ts:177:5
- 175| });
- 176|
- 177| it('updates config when job completes with new configuration', asy…
- | ^
- 178| vi.useFakeTimers();
- 179| mockAPI.getAsrConfiguration.mockResolvedValue(mockConfig);
-
-⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/11]⎯
-
- FAIL src/hooks/audio/use-asr-config.test.ts > useAsrConfig > updateConfig > refreshes config when job completes without new configuration
-Error: Test timed out in 5000ms.
-If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
- ❯ src/hooks/audio/use-asr-config.test.ts:217:5
- 215| });
- 216|
- 217| it('refreshes config when job completes without new configuration'…
- | ^
- 218| vi.useFakeTimers();
- 219| const updatedConfig = { ...mockConfig, modelSize: 'small' as con…
-
-⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/11]⎯
-
- FAIL src/hooks/audio/use-asr-config.test.ts > useAsrConfig > updateConfig > stops reconfiguring and reports failure status
-Error: Test timed out in 5000ms.
-If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
- ❯ src/hooks/audio/use-asr-config.test.ts:260:5
- 258| });
- 259|
- 260| it('stops reconfiguring and reports failure status', async () => {
- | ^
- 261| vi.useFakeTimers();
- 262| mockAPI.getAsrConfiguration.mockResolvedValue(mockConfig);
-
-⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/11]⎯
-
- FAIL src/hooks/audio/use-asr-config.test.ts > useAsrConfig > updateConfig > stops reconfiguring and reports cancelled status
-Error: Test timed out in 5000ms.
-If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
- ❯ src/hooks/audio/use-asr-config.test.ts:299:5
- 297| });
- 298|
- 299| it('stops reconfiguring and reports cancelled status', async () =>…
- | ^
- 300| vi.useFakeTimers();
- 301| mockAPI.getAsrConfiguration.mockResolvedValue(mockConfig);
-
-⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/11]⎯
-
- FAIL src/hooks/audio/use-asr-config.test.ts > useAsrConfig > cancelPolling > can be called without error
-TypeError: Cannot read properties of null (reading 'isLoading')
-
-Ignored nodes: comments, script, style
-
-
-
-
-
-
- ❯ src/hooks/audio/use-asr-config.test.ts:346:31
- 344|
- 345| await waitFor(() => {
- 346| expect(result.current.isLoading).toBe(false);
- | ^
- 347| });
- 348|
- ❯ runWithExpensiveErrorDiagnosticsDisabled node_modules/@testing-library/dom/dist/config.js:47:12
- ❯ checkCallback node_modules/@testing-library/dom/dist/wait-for.js:124:77
- ❯ Timeout.checkRealTimersCallback node_modules/@testing-library/dom/dist/wait-for.js:118:16
-
-⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/11]⎯
-
- FAIL src/api/adapters/mock/stream.test.ts > MockTranscriptionStream > emits VAD and transcript updates
- FAIL src/api/adapters/mock/stream.test.ts > MockTranscriptionStream > stops emitting after close
- FAIL src/api/adapters/mock/stream.test.ts > MockTranscriptionStream > emits vad_end when activity stops
- FAIL src/api/adapters/mock/stream.test.ts > MockTranscriptionStream > does not emit when no callback registered
- FAIL src/api/adapters/mock/stream.test.ts > MockTranscriptionStream > accepts audio chunks without throwing
-Error: Hook timed out in 10000ms.
-If this is a long-running hook, pass a timeout value as the last argument or configure it globally with "hookTimeout".
- ❯ src/test/setup.ts:37:1
- 35|
- 36| // Clean up client logs after each test to prevent timer-related errors
- 37| afterEach(async () => {
- | ^
- 38| await act(async () => {
- 39| await Promise.resolve();
-
-⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/11]⎯
-
- FAIL src/api/adapters/tauri/sections/meetings.test.ts > createMeetingApi > starts transcription and returns a stream
-TypeError: __vite_ssr_import_5__.TauriTranscriptionStream is not a constructor
- ❯ Object.startTranscription src/api/adapters/tauri/sections/meetings.ts:120:16
- 118| transcription_api_key: transcriptionKey,
- 119| });
- 120| return new TauriTranscriptionStream(meetingId, invoke, listen);
- | ^
- 121| } catch (error) {
- 122| const details = extractErrorDetails(error, 'Failed to start re…
- ❯ src/api/adapters/tauri/sections/meetings.test.ts:204:20
-
-⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/11]⎯
-
-
- Test Files 3 failed | 172 passed (175)
- Tests 11 failed | 1528 passed | 2 skipped (1541)
- Start at 09:52:08
- Duration 53.14s (transform 14.08s, setup 85.42s, import 25.26s, tests 109.97s, environment 78.92s)
-
-make: *** [Makefile:106: coverage-ts] Error 1
-✗ Quality checks failed
-Fix the issues above or use 'git commit --no-verify' to bypass
diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index c3750db..0000000
--- a/.coveragerc
+++ /dev/null
@@ -1,18 +0,0 @@
-[run]
-source = src/noteflow
-omit =
- */grpc/proto/*
- */grpc/client.py
- */grpc/service.py
- */grpc/server.py
- */grpc/meeting_store.py
- */client/*
- */domain/ports/*
- */infrastructure/persistence/migrations/*
- */infrastructure/audio/playback.py
- */infrastructure/persistence/*
- */config/settings.py
-
-[report]
-precision = 2
-show_missing = True
diff --git a/.gitignore b/.gitignore
index 3a798c5..6ef6094 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ build/
.mypy_cache/
.ruff_cache/
.coverage
+.coveragerc
htmlcov/
*.egg
.hygeine/
diff --git a/context b/context
deleted file mode 120000
index 1e84cd6..0000000
--- a/context
+++ /dev/null
@@ -1 +0,0 @@
-/home/trav/context/07876be0-9645-4705-b8db-73bcb08eaa13
\ No newline at end of file
diff --git a/noteflow-api-spec.json b/noteflow-api-spec.json
deleted file mode 100644
index 8236da2..0000000
--- a/noteflow-api-spec.json
+++ /dev/null
@@ -1,537 +0,0 @@
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "info": {
- "title": "NoteFlow API",
- "version": "1.0.0",
- "description": "Intelligent meeting notetaker API - real-time audio transcription, meeting management, and AI-powered summarization",
- "protocol": "gRPC",
- "package": "noteflow"
- },
- "service": {
- "name": "NoteFlowService",
- "description": "Main service providing real-time ASR streaming and meeting management"
- },
- "endpoints": {
- "StreamTranscription": {
- "type": "bidirectional_streaming",
- "description": "Bidirectional streaming: client sends audio chunks, server returns transcripts in real-time",
- "request": {
- "type": "stream",
- "message": "AudioChunk"
- },
- "response": {
- "type": "stream",
- "message": "TranscriptUpdate"
- }
- },
- "CreateMeeting": {
- "type": "unary",
- "description": "Create a new meeting session",
- "request": "CreateMeetingRequest",
- "response": "Meeting"
- },
- "StopMeeting": {
- "type": "unary",
- "description": "Stop an active meeting recording",
- "request": "StopMeetingRequest",
- "response": "Meeting"
- },
- "ListMeetings": {
- "type": "unary",
- "description": "List meetings with optional filtering and pagination",
- "request": "ListMeetingsRequest",
- "response": "ListMeetingsResponse"
- },
- "GetMeeting": {
- "type": "unary",
- "description": "Get a specific meeting by ID with optional transcript and summary",
- "request": "GetMeetingRequest",
- "response": "Meeting"
- },
- "DeleteMeeting": {
- "type": "unary",
- "description": "Delete a meeting and all associated data",
- "request": "DeleteMeetingRequest",
- "response": "DeleteMeetingResponse"
- },
- "GenerateSummary": {
- "type": "unary",
- "description": "Generate an AI-powered summary for a meeting",
- "request": "GenerateSummaryRequest",
- "response": "Summary"
- },
- "AddAnnotation": {
- "type": "unary",
- "description": "Add a user annotation to a meeting (action item, decision, note, or risk)",
- "request": "AddAnnotationRequest",
- "response": "Annotation"
- },
- "GetAnnotation": {
- "type": "unary",
- "description": "Get a specific annotation by ID",
- "request": "GetAnnotationRequest",
- "response": "Annotation"
- },
- "ListAnnotations": {
- "type": "unary",
- "description": "List all annotations for a meeting with optional time range filter",
- "request": "ListAnnotationsRequest",
- "response": "ListAnnotationsResponse"
- },
- "UpdateAnnotation": {
- "type": "unary",
- "description": "Update an existing annotation",
- "request": "UpdateAnnotationRequest",
- "response": "Annotation"
- },
- "DeleteAnnotation": {
- "type": "unary",
- "description": "Delete an annotation",
- "request": "DeleteAnnotationRequest",
- "response": "DeleteAnnotationResponse"
- },
- "ExportTranscript": {
- "type": "unary",
- "description": "Export meeting transcript to Markdown or HTML format",
- "request": "ExportTranscriptRequest",
- "response": "ExportTranscriptResponse"
- },
- "RefineSpeakerDiarization": {
- "type": "unary",
- "description": "Run offline speaker diarization to improve speaker labels (background job)",
- "request": "RefineSpeakerDiarizationRequest",
- "response": "RefineSpeakerDiarizationResponse"
- },
- "RenameSpeaker": {
- "type": "unary",
- "description": "Rename a speaker ID to a human-readable name",
- "request": "RenameSpeakerRequest",
- "response": "RenameSpeakerResponse"
- },
- "GetDiarizationJobStatus": {
- "type": "unary",
- "description": "Check status of a background diarization job",
- "request": "GetDiarizationJobStatusRequest",
- "response": "DiarizationJobStatus"
- },
- "GetServerInfo": {
- "type": "unary",
- "description": "Get server health and capabilities information",
- "request": "ServerInfoRequest",
- "response": "ServerInfo"
- }
- },
- "enums": {
- "UpdateType": {
- "description": "Type of transcript update",
- "values": {
- "UPDATE_TYPE_UNSPECIFIED": { "value": 0, "description": "Default/unspecified" },
- "UPDATE_TYPE_PARTIAL": { "value": 1, "description": "Tentative transcript, may change" },
- "UPDATE_TYPE_FINAL": { "value": 2, "description": "Confirmed segment" },
- "UPDATE_TYPE_VAD_START": { "value": 3, "description": "Voice activity started" },
- "UPDATE_TYPE_VAD_END": { "value": 4, "description": "Voice activity ended" }
- }
- },
- "MeetingState": {
- "description": "Current state of a meeting",
- "values": {
- "MEETING_STATE_UNSPECIFIED": { "value": 0, "description": "Default/unspecified" },
- "MEETING_STATE_CREATED": { "value": 1, "description": "Created but not started" },
- "MEETING_STATE_RECORDING": { "value": 2, "description": "Actively recording audio" },
- "MEETING_STATE_STOPPED": { "value": 3, "description": "Recording stopped, processing may continue" },
- "MEETING_STATE_COMPLETED": { "value": 4, "description": "All processing complete" },
- "MEETING_STATE_ERROR": { "value": 5, "description": "Error occurred" }
- }
- },
- "SortOrder": {
- "description": "Sort order for listing meetings",
- "values": {
- "SORT_ORDER_UNSPECIFIED": { "value": 0, "description": "Default (newest first)" },
- "SORT_ORDER_CREATED_DESC": { "value": 1, "description": "Newest first" },
- "SORT_ORDER_CREATED_ASC": { "value": 2, "description": "Oldest first" }
- }
- },
- "Priority": {
- "description": "Priority level for action items",
- "values": {
- "PRIORITY_UNSPECIFIED": { "value": 0, "description": "Default/unspecified" },
- "PRIORITY_LOW": { "value": 1, "description": "Low priority" },
- "PRIORITY_MEDIUM": { "value": 2, "description": "Medium priority" },
- "PRIORITY_HIGH": { "value": 3, "description": "High priority" }
- }
- },
- "AnnotationType": {
- "description": "Type of user annotation",
- "values": {
- "ANNOTATION_TYPE_UNSPECIFIED": { "value": 0, "description": "Default/unspecified" },
- "ANNOTATION_TYPE_ACTION_ITEM": { "value": 1, "description": "Action item to be done" },
- "ANNOTATION_TYPE_DECISION": { "value": 2, "description": "Decision made" },
- "ANNOTATION_TYPE_NOTE": { "value": 3, "description": "General note" },
- "ANNOTATION_TYPE_RISK": { "value": 4, "description": "Risk or concern" }
- }
- },
- "ExportFormat": {
- "description": "Transcript export format",
- "values": {
- "EXPORT_FORMAT_UNSPECIFIED": { "value": 0, "description": "Default/unspecified" },
- "EXPORT_FORMAT_MARKDOWN": { "value": 1, "description": "Markdown format" },
- "EXPORT_FORMAT_HTML": { "value": 2, "description": "HTML format" }
- }
- },
- "JobStatus": {
- "description": "Background job status",
- "values": {
- "JOB_STATUS_UNSPECIFIED": { "value": 0, "description": "Default/unspecified" },
- "JOB_STATUS_QUEUED": { "value": 1, "description": "Job is queued" },
- "JOB_STATUS_RUNNING": { "value": 2, "description": "Job is running" },
- "JOB_STATUS_COMPLETED": { "value": 3, "description": "Job completed successfully" },
- "JOB_STATUS_FAILED": { "value": 4, "description": "Job failed" }
- }
- }
- },
- "messages": {
- "AudioChunk": {
- "description": "Audio data chunk for streaming transcription",
- "fields": {
- "meeting_id": { "type": "string", "required": true, "description": "Meeting ID this audio belongs to" },
- "audio_data": { "type": "bytes", "required": true, "description": "Raw audio data (float32, mono, 16kHz expected)" },
- "timestamp": { "type": "double", "required": false, "description": "Timestamp when audio was captured (monotonic, seconds)" },
- "sample_rate": { "type": "int32", "required": false, "default": 16000, "description": "Sample rate in Hz" },
- "channels": { "type": "int32", "required": false, "default": 1, "description": "Number of channels (1 for mono)" }
- }
- },
- "TranscriptUpdate": {
- "description": "Real-time transcript update from server",
- "fields": {
- "meeting_id": { "type": "string", "description": "Meeting ID this transcript belongs to" },
- "update_type": { "type": "UpdateType", "description": "Type of update (partial, final, VAD events)" },
- "partial_text": { "type": "string", "description": "For partial updates - tentative transcript text" },
- "segment": { "type": "FinalSegment", "description": "For final updates - confirmed transcript segment" },
- "server_timestamp": { "type": "double", "description": "Server-side processing timestamp" }
- }
- },
- "FinalSegment": {
- "description": "Confirmed transcript segment with word-level timing",
- "fields": {
- "segment_id": { "type": "int32", "description": "Segment ID (sequential within meeting)" },
- "text": { "type": "string", "description": "Transcript text" },
- "start_time": { "type": "double", "description": "Start time relative to meeting start (seconds)" },
- "end_time": { "type": "double", "description": "End time relative to meeting start (seconds)" },
- "words": { "type": "array", "items": "WordTiming", "description": "Word-level timestamps" },
- "language": { "type": "string", "description": "Detected language code" },
- "language_confidence": { "type": "float", "description": "Language detection confidence (0.0-1.0)" },
- "avg_logprob": { "type": "float", "description": "Average log probability (quality indicator)" },
- "no_speech_prob": { "type": "float", "description": "Probability that segment contains no speech" },
- "speaker_id": { "type": "string", "description": "Speaker identification (from diarization)" },
- "speaker_confidence": { "type": "float", "description": "Speaker assignment confidence (0.0-1.0)" }
- }
- },
- "WordTiming": {
- "description": "Word-level timing information",
- "fields": {
- "word": { "type": "string", "description": "The word text" },
- "start_time": { "type": "double", "description": "Start time in seconds" },
- "end_time": { "type": "double", "description": "End time in seconds" },
- "probability": { "type": "float", "description": "Recognition confidence (0.0-1.0)" }
- }
- },
- "Meeting": {
- "description": "Complete meeting record with transcript and summary",
- "fields": {
- "id": { "type": "string", "description": "Unique meeting identifier (UUID)" },
- "title": { "type": "string", "description": "User-provided or auto-generated title" },
- "state": { "type": "MeetingState", "description": "Current meeting state" },
- "created_at": { "type": "double", "description": "Creation timestamp (Unix epoch seconds)" },
- "started_at": { "type": "double", "description": "Recording start timestamp" },
- "ended_at": { "type": "double", "description": "Recording end timestamp" },
- "duration_seconds": { "type": "double", "description": "Total duration in seconds" },
- "segments": { "type": "array", "items": "FinalSegment", "description": "Full transcript segments" },
- "summary": { "type": "Summary", "description": "Generated summary (if available)" },
- "metadata": { "type": "map", "description": "Custom metadata key-value pairs" }
- }
- },
- "CreateMeetingRequest": {
- "description": "Request to create a new meeting",
- "fields": {
- "title": { "type": "string", "required": false, "description": "Optional title (auto-generated if not provided)" },
- "metadata": { "type": "map", "required": false, "description": "Optional custom metadata" }
- }
- },
- "StopMeetingRequest": {
- "description": "Request to stop a meeting recording",
- "fields": {
- "meeting_id": { "type": "string", "required": true, "description": "Meeting ID to stop" }
- }
- },
- "ListMeetingsRequest": {
- "description": "Request to list meetings with filters",
- "fields": {
- "states": { "type": "array", "items": "MeetingState", "required": false, "description": "Filter by meeting states" },
- "limit": { "type": "int32", "required": false, "default": 50, "description": "Max results to return" },
- "offset": { "type": "int32", "required": false, "default": 0, "description": "Pagination offset" },
- "sort_order": { "type": "SortOrder", "required": false, "default": "SORT_ORDER_CREATED_DESC", "description": "Sort order" }
- }
- },
- "ListMeetingsResponse": {
- "description": "Response containing list of meetings",
- "fields": {
- "meetings": { "type": "array", "items": "Meeting", "description": "List of meetings" },
- "total_count": { "type": "int32", "description": "Total number of meetings matching filter" }
- }
- },
- "GetMeetingRequest": {
- "description": "Request to get a specific meeting",
- "fields": {
- "meeting_id": { "type": "string", "required": true, "description": "Meeting ID to retrieve" },
- "include_segments": { "type": "bool", "required": false, "default": false, "description": "Include full transcript segments" },
- "include_summary": { "type": "bool", "required": false, "default": false, "description": "Include summary if available" }
- }
- },
- "DeleteMeetingRequest": {
- "description": "Request to delete a meeting",
- "fields": {
- "meeting_id": { "type": "string", "required": true, "description": "Meeting ID to delete" }
- }
- },
- "DeleteMeetingResponse": {
- "description": "Response confirming meeting deletion",
- "fields": {
- "success": { "type": "bool", "description": "Whether deletion was successful" }
- }
- },
- "Summary": {
- "description": "AI-generated meeting summary with evidence linking",
- "fields": {
- "meeting_id": { "type": "string", "description": "Meeting this summary belongs to" },
- "executive_summary": { "type": "string", "description": "Executive summary (2-3 sentences)" },
- "key_points": { "type": "array", "items": "KeyPoint", "description": "Key points/highlights extracted" },
- "action_items": { "type": "array", "items": "ActionItem", "description": "Action items extracted" },
- "generated_at": { "type": "double", "description": "Generation timestamp (Unix epoch)" },
- "model_version": { "type": "string", "description": "Model/version used for generation" }
- }
- },
- "KeyPoint": {
- "description": "Key point from meeting with evidence linking",
- "fields": {
- "text": { "type": "string", "description": "The key point text" },
- "segment_ids": { "type": "array", "items": "int32", "description": "Segment IDs that support this point" },
- "start_time": { "type": "double", "description": "Start of relevant time range" },
- "end_time": { "type": "double", "description": "End of relevant time range" }
- }
- },
- "ActionItem": {
- "description": "Action item extracted from meeting",
- "fields": {
- "text": { "type": "string", "description": "Action item description" },
- "assignee": { "type": "string", "description": "Person assigned (if mentioned)" },
- "due_date": { "type": "double", "description": "Due date (Unix epoch, if mentioned)" },
- "priority": { "type": "Priority", "description": "Priority level" },
- "segment_ids": { "type": "array", "items": "int32", "description": "Segment IDs mentioning this action" }
- }
- },
- "GenerateSummaryRequest": {
- "description": "Request to generate meeting summary",
- "fields": {
- "meeting_id": { "type": "string", "required": true, "description": "Meeting ID to summarize" },
- "force_regenerate": { "type": "bool", "required": false, "default": false, "description": "Force regeneration even if summary exists" }
- }
- },
- "Annotation": {
- "description": "User-created annotation on meeting timeline",
- "fields": {
- "id": { "type": "string", "description": "Unique annotation identifier (UUID)" },
- "meeting_id": { "type": "string", "description": "Meeting this annotation belongs to" },
- "annotation_type": { "type": "AnnotationType", "description": "Type of annotation" },
- "text": { "type": "string", "description": "Annotation text content" },
- "start_time": { "type": "double", "description": "Start time relative to meeting start (seconds)" },
- "end_time": { "type": "double", "description": "End time relative to meeting start (seconds)" },
- "segment_ids": { "type": "array", "items": "int32", "description": "Linked transcript segment IDs" },
- "created_at": { "type": "double", "description": "Creation timestamp (Unix epoch)" }
- }
- },
- "AddAnnotationRequest": {
- "description": "Request to add an annotation",
- "fields": {
- "meeting_id": { "type": "string", "required": true, "description": "Meeting ID to annotate" },
- "annotation_type": { "type": "AnnotationType", "required": true, "description": "Type of annotation" },
- "text": { "type": "string", "required": true, "description": "Annotation text" },
- "start_time": { "type": "double", "required": true, "description": "Start time in seconds" },
- "end_time": { "type": "double", "required": true, "description": "End time in seconds" },
- "segment_ids": { "type": "array", "items": "int32", "required": false, "description": "Optional linked segment IDs" }
- }
- },
- "GetAnnotationRequest": {
- "description": "Request to get an annotation",
- "fields": {
- "annotation_id": { "type": "string", "required": true, "description": "Annotation ID to retrieve" }
- }
- },
- "ListAnnotationsRequest": {
- "description": "Request to list annotations for a meeting",
- "fields": {
- "meeting_id": { "type": "string", "required": true, "description": "Meeting ID to list annotations for" },
- "start_time": { "type": "double", "required": false, "description": "Filter: start of time range" },
- "end_time": { "type": "double", "required": false, "description": "Filter: end of time range" }
- }
- },
- "ListAnnotationsResponse": {
- "description": "Response containing annotations",
- "fields": {
- "annotations": { "type": "array", "items": "Annotation", "description": "List of annotations" }
- }
- },
- "UpdateAnnotationRequest": {
- "description": "Request to update an annotation",
- "fields": {
- "annotation_id": { "type": "string", "required": true, "description": "Annotation ID to update" },
- "annotation_type": { "type": "AnnotationType", "required": false, "description": "New type (keeps existing if not set)" },
- "text": { "type": "string", "required": false, "description": "New text (keeps existing if empty)" },
- "start_time": { "type": "double", "required": false, "description": "New start time (keeps existing if 0)" },
- "end_time": { "type": "double", "required": false, "description": "New end time (keeps existing if 0)" },
- "segment_ids": { "type": "array", "items": "int32", "required": false, "description": "New segment IDs (replaces existing)" }
- }
- },
- "DeleteAnnotationRequest": {
- "description": "Request to delete an annotation",
- "fields": {
- "annotation_id": { "type": "string", "required": true, "description": "Annotation ID to delete" }
- }
- },
- "DeleteAnnotationResponse": {
- "description": "Response confirming annotation deletion",
- "fields": {
- "success": { "type": "bool", "description": "Whether deletion was successful" }
- }
- },
- "ExportTranscriptRequest": {
- "description": "Request to export meeting transcript",
- "fields": {
- "meeting_id": { "type": "string", "required": true, "description": "Meeting ID to export" },
- "format": { "type": "ExportFormat", "required": true, "description": "Export format (Markdown or HTML)" }
- }
- },
- "ExportTranscriptResponse": {
- "description": "Response containing exported transcript",
- "fields": {
- "content": { "type": "string", "description": "Exported content as string" },
- "format_name": { "type": "string", "description": "Human-readable format name" },
- "file_extension": { "type": "string", "description": "Suggested file extension (.md or .html)" }
- }
- },
- "RefineSpeakerDiarizationRequest": {
- "description": "Request to run offline speaker diarization",
- "fields": {
- "meeting_id": { "type": "string", "required": true, "description": "Meeting ID to process" },
- "num_speakers": { "type": "int32", "required": false, "description": "Known number of speakers (auto-detect if 0)" }
- }
- },
- "RefineSpeakerDiarizationResponse": {
- "description": "Response from diarization job start",
- "fields": {
- "segments_updated": { "type": "int32", "description": "Number of segments updated (0 if job is async)" },
- "speaker_ids": { "type": "array", "items": "string", "description": "Distinct speaker IDs found" },
- "error_message": { "type": "string", "description": "Error message if failed" },
- "job_id": { "type": "string", "description": "Background job ID for polling" },
- "status": { "type": "JobStatus", "description": "Current job status" }
- }
- },
- "RenameSpeakerRequest": {
- "description": "Request to rename a speaker",
- "fields": {
- "meeting_id": { "type": "string", "required": true, "description": "Meeting ID" },
- "old_speaker_id": { "type": "string", "required": true, "description": "Original speaker ID (e.g., 'SPEAKER_00')" },
- "new_speaker_name": { "type": "string", "required": true, "description": "New human-readable name (e.g., 'Alice')" }
- }
- },
- "RenameSpeakerResponse": {
- "description": "Response from speaker rename",
- "fields": {
- "segments_updated": { "type": "int32", "description": "Number of segments updated" },
- "success": { "type": "bool", "description": "Whether rename was successful" }
- }
- },
- "GetDiarizationJobStatusRequest": {
- "description": "Request to check diarization job status",
- "fields": {
- "job_id": { "type": "string", "required": true, "description": "Job ID from RefineSpeakerDiarization" }
- }
- },
- "DiarizationJobStatus": {
- "description": "Status of a diarization job",
- "fields": {
- "job_id": { "type": "string", "description": "Job ID" },
- "status": { "type": "JobStatus", "description": "Current status" },
- "segments_updated": { "type": "int32", "description": "Segments updated (when completed)" },
- "speaker_ids": { "type": "array", "items": "string", "description": "Speaker IDs found (when completed)" },
- "error_message": { "type": "string", "description": "Error message if failed" }
- }
- },
- "ServerInfoRequest": {
- "description": "Request for server info (empty message)",
- "fields": {}
- },
- "ServerInfo": {
- "description": "Server health and capabilities",
- "fields": {
- "version": { "type": "string", "description": "Server version string" },
- "asr_model": { "type": "string", "description": "Loaded ASR model name" },
- "asr_ready": { "type": "bool", "description": "Whether ASR is ready" },
- "supported_sample_rates": { "type": "array", "items": "int32", "description": "Supported audio sample rates" },
- "max_chunk_size": { "type": "int32", "description": "Maximum audio chunk size in bytes" },
- "uptime_seconds": { "type": "double", "description": "Server uptime in seconds" },
- "active_meetings": { "type": "int32", "description": "Number of active meetings" },
- "diarization_enabled": { "type": "bool", "description": "Whether diarization is enabled" },
- "diarization_ready": { "type": "bool", "description": "Whether diarization models are ready" }
- }
- }
- },
- "uiHints": {
- "primaryEntities": ["Meeting", "Summary", "Annotation"],
- "listViews": {
- "meetings": {
- "endpoint": "ListMeetings",
- "displayFields": ["title", "state", "created_at", "duration_seconds"],
- "actions": ["view", "delete", "export"]
- },
- "annotations": {
- "endpoint": "ListAnnotations",
- "displayFields": ["annotation_type", "text", "start_time"],
- "actions": ["edit", "delete"]
- }
- },
- "detailViews": {
- "meeting": {
- "endpoint": "GetMeeting",
- "sections": [
- { "name": "Overview", "fields": ["title", "state", "created_at", "duration_seconds"] },
- { "name": "Transcript", "field": "segments", "type": "timeline" },
- { "name": "Summary", "field": "summary", "type": "expandable" }
- ]
- }
- },
- "forms": {
- "createMeeting": {
- "endpoint": "CreateMeeting",
- "fields": [
- { "name": "title", "label": "Meeting Title", "type": "text", "placeholder": "Optional - will be auto-generated" }
- ]
- },
- "addAnnotation": {
- "endpoint": "AddAnnotation",
- "fields": [
- { "name": "annotation_type", "label": "Type", "type": "select", "options": "AnnotationType" },
- { "name": "text", "label": "Note", "type": "textarea" },
- { "name": "start_time", "label": "Start Time", "type": "number" },
- { "name": "end_time", "label": "End Time", "type": "number" }
- ]
- }
- },
- "realTimeFeatures": {
- "transcription": {
- "endpoint": "StreamTranscription",
- "description": "Real-time audio-to-text with live updates",
- "updateTypes": ["partial", "final", "vad_start", "vad_end"]
- }
- }
- }
-}
diff --git a/repomix-output.md b/repomix-output.md
deleted file mode 100644
index 022b763..0000000
--- a/repomix-output.md
+++ /dev/null
@@ -1,3677 +0,0 @@
-This file is a merged representation of a subset of the codebase, containing specifically included files and files not matching ignore patterns, combined into a single document by Repomix.
-The content has been processed where line numbers have been added, content has been formatted for parsing in markdown style.
-
-# File Summary
-
-## Purpose
-This file contains a packed representation of a subset of the repository's contents that is considered the most important context.
-It is designed to be easily consumable by AI systems for analysis, code review,
-or other automated processes.
-
-## File Format
-The content is organized as follows:
-1. This summary section
-2. Repository information
-3. Directory structure
-4. Repository files (if enabled)
-5. Multiple file entries, each consisting of:
- a. A header with the file path (## File: path/to/file)
- b. The full contents of the file in a code block
-
-## Usage Guidelines
-- This file should be treated as read-only. Any changes should be made to the
- original repository files, not this packed version.
-- When processing this file, use the file path to distinguish
- between different files in the repository.
-- Be aware that this file may contain sensitive information. Handle it with
- the same level of security as you would the original repository.
-
-## Notes
-- Some files may have been excluded based on .gitignore rules and Repomix's configuration
-- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
-- Only files matching these patterns are included: .cupcake/policies/opencode
-- Files matching these patterns are excluded: **/*_pb2.py, **/*_pb2_grpc.py, **/*.pb2.py, **/*.pb2_grpc.py, **/*.pyi, **/*.wav, **/*.nfaudio, **/*.m4a, **/*.mp3, **/*.mp4, **/*.mov, **/*.avi, **/*.mkv, **/*.flv, **/*.wmv, **/*.webm, **/*.m3u8, **/noteflow.rs, **/noteflow_pb2.py, src/noteflow_pb2.py, client/src-tauri/src/grpc/noteflow.rs, src/noteflow/grpc/proto/noteflow_pb2.py, src/noteflow/grpc/proto/noteflow_pb2_grpc.py, src/noteflow/grpc/proto/noteflow_pb2.pyi, **/persistence/migrations/**, **/node_modules/**, **/target/**, **/gen/**, **/__pycache__/**, **/*.pyc, **/.pytest_cache/**, **/.mypy_cache/**, **/.ruff_cache/**, **/dist/**, **/build/**, **/.vite/**, **/coverage/**, **/htmlcov/**, **/playwright-report/**, **/test-results/**, uv.lock, **/Cargo.lock, **/package-lock.json, **/bun.lockb, **/yarn.lock, **/*.lock, **/*.lockb, **/*.png, **/*.jpg, **/*.jpeg, **/*.gif, **/*.ico, **/*.svg, **/*.icns, **/*.webp, **/*.xml, **/icons/**, **/public/**, client/app-icon.png, **/*.md, .benchmarks/**, noteflow-api-spec.json, scratch.md, repomix-output.md, **/logs/**, **/status_line.json
-- Files matching patterns in .gitignore are excluded
-- Files matching default ignore patterns are excluded
-- Line numbers have been added to the beginning of each line
-- Content has been formatted for parsing in markdown style
-- Long base64 data strings (e.g., data:image/png;base64,...) have been truncated to reduce token count
-- Files are sorted by Git change count (files with more changes are at the bottom)
-
-# Directory Structure
-```
-.cupcake/
- policies/
- opencode/
- ban_stdlib_logger.rego
- block_assertion_roulette.rego
- block_biome_ignore_bash.rego
- block_biome_ignore.rego
- block_broad_exception_handler.rego
- block_code_quality_test_bash.rego
- block_code_quality_test_edits.rego
- block_code_quality_test_serena_plugin.rego
- block_code_quality_test_serena.rego
- block_datetime_now_fallback.rego
- block_default_value_swallow.rego
- block_duplicate_fixtures.rego
- block_linter_config_frontend_bash.rego
- block_linter_config_frontend.rego
- block_linter_config_python_bash.rego
- block_linter_config_python.rego
- block_magic_numbers.rego
- block_makefile_bash.rego
- block_makefile_edit.rego
- block_no_verify.rego
- block_silent_none_return.rego
- block_test_loops_conditionals.rego
- block_tests_quality_bash.rego
- block_tests_quality.rego
- prevent_any_type.rego
- prevent_type_suppression.rego
- warn_baselines_edit_bash.rego
- warn_baselines_edit.rego
- warn_large_file.rego
-```
-
-# Files
-
-## File: .cupcake/policies/opencode/block_biome_ignore_bash.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Biome Ignore (Bash)
- 4: # description: Blocks Bash commands that add ignore directives to JS/TS files
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Bash"]
- 9: package cupcake.policies.opencode.block_biome_ignore_bash
-10: import rego.v1
-11:
-12: ignore_pattern := `(biome-ignore|@ts-ignore|@ts-expect-error|@ts-nocheck|eslint-disable).*\.(js|jsx|ts|tsx|mjs|cjs)`
-13:
-14: deny contains decision if {
-15: input.hook_event_name == "PreToolUse"
-16: input.tool_name == "Bash"
-17:
-18: command := input.tool_input.command
-19: regex.match(ignore_pattern, command)
-20:
-21: decision := {
-22: "rule_id": "TS-LINT-001",
-23: "reason": "Ignore directives for Biome/TypeScript/ESLint are prohibited.",
-24: "severity": "HIGH"
-25: }
-26: }
-```
-
-## File: .cupcake/policies/opencode/block_code_quality_test_bash.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Code Quality Test (Bash)
- 4: # description: Blocks Bash edits to src/test/code-quality.test.ts
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Bash"]
- 9: package cupcake.policies.opencode.block_code_quality_test_bash
-10: import rego.v1
-11:
-12: pattern := `(sed|awk|cat\s*>|echo\s*>|tee|cp\s+.*code-quality\.test\.ts|mv\s+.*code-quality\.test\.ts|rm\s+.*code-quality\.test\.ts|>|>>).*code-quality\.test\.ts|code-quality\.test\.ts.*(>|>>|\|.*tee)`
-13:
-14: deny contains decision if {
-15: input.hook_event_name == "PreToolUse"
-16: input.tool_name == "Bash"
-17:
-18: command := input.tool_input.command
-19: regex.match(pattern, command)
-20:
-21: decision := {
-22: "rule_id": "TS-QUALITY-001",
-23: "reason": "Direct edits to src/test/code-quality.test.ts are prohibited.",
-24: "severity": "HIGH"
-25: }
-26: }
-```
-
-## File: .cupcake/policies/opencode/block_code_quality_test_serena_plugin.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Code Quality Test (Serena Plugin)
- 4: # description: Blocks Serena plugin edits to src/test/code-quality.test.ts
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: []
- 9: package cupcake.policies.opencode.block_code_quality_test_serena_plugin
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: file_path_pattern := `(^|/)src/test/code-quality\.test\.ts$`
-102:
-103: get_relative_path := path if {
-104: path := tool_input.relative_path
-105: } else := path if {
-106: path := tool_input.path
-107: } else := ""
-108:
-109: # Block Serena plugin operations targeting code-quality test file
-110:
-111: deny contains decision if {
-112: input.hook_event_name == "PreToolUse"
-113:
-114: tool_names := {"mcp__plugin_serena_serena__replace_content", "mcp__plugin_serena_serena__replace_symbol_body", "mcp__plugin_serena_serena__create_text_file", "mcp__plugin_serena_serena__insert_before_symbol", "mcp__plugin_serena_serena__insert_after_symbol", "mcp__plugin_serena_serena__rename_symbol"}
-115: tool_name in tool_names
-116:
-117: file_path := get_relative_path
-118: regex.match(file_path_pattern, file_path)
-119:
-120: decision := {
-121: "rule_id": "TS-QUALITY-004",
-122: "reason": "Direct edits to src/test/code-quality.test.ts are prohibited.",
-123: "severity": "HIGH"
-124: }
-125: }
-```
-
-## File: .cupcake/policies/opencode/block_code_quality_test_serena.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Code Quality Test (Serena)
- 4: # description: Blocks Serena edits to src/test/code-quality.test.ts
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: []
- 9: package cupcake.policies.opencode.block_code_quality_test_serena
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: file_path_pattern := `(^|/)src/test/code-quality\.test\.ts$`
-102:
-103: get_relative_path := path if {
-104: path := tool_input.relative_path
-105: } else := path if {
-106: path := tool_input.path
-107: } else := ""
-108:
-109: # Block Serena operations targeting code-quality test file
-110:
-111: deny contains decision if {
-112: input.hook_event_name == "PreToolUse"
-113:
-114: tool_names := {"mcp__serena__replace_content", "mcp__serena__replace_symbol_body", "mcp__serena__create_text_file", "mcp__serena__insert_before_symbol", "mcp__serena__insert_after_symbol", "mcp__serena__rename_symbol"}
-115: tool_name in tool_names
-116:
-117: file_path := get_relative_path
-118: regex.match(file_path_pattern, file_path)
-119:
-120: decision := {
-121: "rule_id": "TS-QUALITY-003",
-122: "reason": "Direct edits to src/test/code-quality.test.ts are prohibited.",
-123: "severity": "HIGH"
-124: }
-125: }
-```
-
-## File: .cupcake/policies/opencode/block_linter_config_frontend_bash.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Frontend Linter Config (Bash)
- 4: # description: Blocks Bash edits to frontend linter config files
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Bash"]
- 9: package cupcake.policies.opencode.block_linter_config_frontend_bash
-10: import rego.v1
-11:
-12: pattern := `(rm|mv|cp|sed|awk|chmod|chown|touch|truncate|tee|>|>>)\s.*client/.*(?:biome\.json|tsconfig\.json|\.?eslint(?:rc|\.config)|\.?prettier(?:rc|\.config)|\.?rustfmt\.toml|\.?clippy\.toml)`
-13:
-14: deny contains decision if {
-15: input.hook_event_name == "PreToolUse"
-16: input.tool_name == "Bash"
-17:
-18: command := input.tool_input.command
-19: regex.match(pattern, command)
-20:
-21: decision := {
-22: "rule_id": "TS-CONFIG-001",
-23: "reason": "Frontend linter/config file edits are prohibited.",
-24: "severity": "HIGH"
-25: }
-26: }
-```
-
-## File: .cupcake/policies/opencode/block_linter_config_python_bash.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Python Linter Config (Bash)
- 4: # description: Blocks Bash edits to Python linter config files
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Bash"]
- 9: package cupcake.policies.opencode.block_linter_config_python_bash
-10: import rego.v1
-11:
-12: pattern := `(rm|mv|cp|sed|awk|chmod|chown|touch|truncate|tee|>|>>)\s.*(?:pyproject\.toml|\.?ruff\.toml|\.?pyrightconfig\.json|\.?mypy\.ini|setup\.cfg|\.flake8|tox\.ini|\.?pylintrc)`
-13:
-14: deny contains decision if {
-15: input.hook_event_name == "PreToolUse"
-16: input.tool_name == "Bash"
-17:
-18: command := input.tool_input.command
-19: regex.match(pattern, command)
-20:
-21: decision := {
-22: "rule_id": "PY-CONFIG-001",
-23: "reason": "Python linter/config file edits are prohibited.",
-24: "severity": "HIGH"
-25: }
-26: }
-```
-
-## File: .cupcake/policies/opencode/block_makefile_bash.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Makefile Edit (Bash)
- 4: # description: Blocks Bash edits to Makefile
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Bash"]
- 9: package cupcake.policies.opencode.block_makefile_bash
-10: import rego.v1
-11:
-12: pattern := `(>>?\s*Makefile|sed\s+.*-i.*Makefile|sed\s+-i.*Makefile|perl\s+-[pi].*Makefile|tee\s+.*Makefile|(mv|cp)\s+\S+\s+Makefile\b|>\s*Makefile)`
-13:
-14: deny contains decision if {
-15: input.hook_event_name == "PreToolUse"
-16: input.tool_name == "Bash"
-17:
-18: command := input.tool_input.command
-19: regex.match(pattern, command)
-20:
-21: decision := {
-22: "rule_id": "BUILD-001",
-23: "reason": "Makefile edits are prohibited.",
-24: "severity": "HIGH"
-25: }
-26: }
-```
-
-## File: .cupcake/policies/opencode/block_no_verify.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Git --no-verify
- 4: # description: Blocks git commit --no-verify
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Bash"]
- 9: package cupcake.policies.opencode.block_no_verify
-10: import rego.v1
-11:
-12: pattern := `git\s+commit\s+.*--no-verify|git\s+commit\s+--no-verify`
-13:
-14: deny contains decision if {
-15: input.hook_event_name == "PreToolUse"
-16: input.tool_name == "Bash"
-17:
-18: command := input.tool_input.command
-19: regex.match(pattern, command)
-20:
-21: decision := {
-22: "rule_id": "GIT-001",
-23: "reason": "Git commit --no-verify is prohibited.",
-24: "severity": "HIGH"
-25: }
-26: }
-```
-
-## File: .cupcake/policies/opencode/block_tests_quality_bash.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Tests Quality (Bash)
- 4: # description: Blocks Bash edits to tests/quality (except baselines.json)
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Bash"]
- 9: package cupcake.policies.opencode.block_tests_quality_bash
-10: import rego.v1
-11:
-12: pattern := `(rm|mv|cp|sed|awk|chmod|chown|touch|mkdir|rmdir|truncate|tee|>|>>)\s.*tests/quality/`
-13:
-14: deny contains decision if {
-15: input.hook_event_name == "PreToolUse"
-16: input.tool_name == "Bash"
-17:
-18: command := input.tool_input.command
-19: regex.match(pattern, command)
-20: not contains(lower(command), "tests/quality/baselines.json")
-21:
-22: decision := {
-23: "rule_id": "TEST-QUALITY-001",
-24: "reason": "Direct edits to tests/quality are prohibited (except baselines.json).",
-25: "severity": "HIGH"
-26: }
-27: }
-```
-
-## File: .cupcake/policies/opencode/warn_baselines_edit_bash.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Warn on Baselines Edit (Bash)
- 4: # description: Warns on Bash edits to tests/quality/baselines.json
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Bash"]
- 9: package cupcake.policies.opencode.warn_baselines_edit_bash
-10: import rego.v1
-11:
-12: pattern := `(sed|awk|echo|cat|tee|>|>>|cp|mv).*tests/quality/baselines\.json`
-13:
-14: deny contains decision if {
-15: input.hook_event_name == "PreToolUse"
-16: input.tool_name == "Bash"
-17:
-18: command := input.tool_input.command
-19: regex.match(pattern, command)
-20:
-21: decision := {
-22: "rule_id": "TEST-QUALITY-003",
-23: "reason": "Warning: editing tests/quality/baselines.json should be avoided unless explicitly required.",
-24: "severity": "LOW"
-25: }
-26: }
-```
-
-## File: .cupcake/policies/opencode/ban_stdlib_logger.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Ban Stdlib Logger
- 4: # description: Blocks use of stdlib logging in Python code
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.ban_stdlib_logger
- 10:
- 11: import rego.v1
- 12:
- 13: tool_name := input.tool_name if {
- 14: input.tool_name != null
- 15: } else := input.tool
- 16:
- 17: tool_input := input.tool_input if {
- 18: input.tool_input != null
- 19: } else := input.args
- 20:
- 21: resolved_file_path := input.resolved_file_path if {
- 22: input.resolved_file_path != null
- 23: } else := tool_input.file_path if {
- 24: tool_input.file_path != null
- 25: } else := tool_input.filePath if {
- 26: tool_input.filePath != null
- 27: } else := tool_input.path if {
- 28: tool_input.path != null
- 29: } else := tool_input.notebook_path if {
- 30: tool_input.notebook_path != null
- 31: } else := tool_input.notebookPath if {
- 32: tool_input.notebookPath != null
- 33: } else := ""
- 34:
- 35: new_content := tool_input.new_string if {
- 36: tool_input.new_string != null
- 37: } else := tool_input.newText if {
- 38: tool_input.newText != null
- 39: } else := tool_input.new_text if {
- 40: tool_input.new_text != null
- 41: } else := tool_input.content if {
- 42: tool_input.content != null
- 43: } else := ""
- 44:
- 45: old_content := tool_input.old_string if {
- 46: tool_input.old_string != null
- 47: } else := tool_input.oldText if {
- 48: tool_input.oldText != null
- 49: } else := tool_input.old_text if {
- 50: tool_input.old_text != null
- 51: } else := tool_input.previousContent if {
- 52: tool_input.previousContent != null
- 53: } else := ""
- 54:
- 55: patch_content := tool_input.patch if {
- 56: tool_input.patch != null
- 57: } else := tool_input.patchText if {
- 58: tool_input.patchText != null
- 59: } else := tool_input.patch_text if {
- 60: tool_input.patch_text != null
- 61: } else := ""
- 62:
- 63: edit_path(edit) := path if {
- 64: edit.resolved_file_path != null
- 65: path := edit.resolved_file_path
- 66: } else := path if {
- 67: edit.file_path != null
- 68: path := edit.file_path
- 69: } else := path if {
- 70: edit.filePath != null
- 71: path := edit.filePath
- 72: } else := path if {
- 73: edit.path != null
- 74: path := edit.path
- 75: } else := ""
- 76:
- 77: edit_new_content(edit) := content if {
- 78: edit.new_string != null
- 79: content := edit.new_string
- 80: } else := content if {
- 81: edit.newText != null
- 82: content := edit.newText
- 83: } else := content if {
- 84: edit.new_text != null
- 85: content := edit.new_text
- 86: } else := content if {
- 87: edit.content != null
- 88: content := edit.content
- 89: } else := ""
- 90:
- 91: edit_old_content(edit) := content if {
- 92: edit.old_string != null
- 93: content := edit.old_string
- 94: } else := content if {
- 95: edit.oldText != null
- 96: content := edit.oldText
- 97: } else := content if {
- 98: edit.old_text != null
- 99: content := edit.old_text
-100: } else := ""
-101:
-102: is_python_patch(patch_text) if {
-103: contains(patch_text, ".py")
-104: }
-105:
-106: is_python_patch(patch_text) if {
-107: contains(patch_text, ".pyi")
-108: }
-109:
-110: stdlib_logger_pattern := `import logging|from logging import|logging\.getLogger`
-111: file_path_pattern := `\.py$`
-112:
-113: # Block Write/Edit operations that introduce stdlib logging
-114:
-115: deny contains decision if {
-116: input.hook_event_name == "PreToolUse"
-117: tool_name in {"Write", "Edit", "NotebookEdit"}
-118:
-119: file_path := resolved_file_path
-120: regex.match(file_path_pattern, file_path)
-121:
-122: content := new_content
-123: content != null
-124: regex.match(stdlib_logger_pattern, content)
-125:
-126: decision := {
-127: "rule_id": "PY-LOG-001",
-128: "reason": "Stdlib logging usage is prohibited. Use the project logging utilities instead.",
-129: "severity": "HIGH"
-130: }
-131: }
-132:
-133: deny contains decision if {
-134: input.hook_event_name == "PreToolUse"
-135: tool_name == "MultiEdit"
-136:
-137: some edit in tool_input.edits
-138: file_path := edit_path(edit)
-139: regex.match(file_path_pattern, file_path)
-140:
-141: content := edit_new_content(edit)
-142: content != null
-143: regex.match(stdlib_logger_pattern, content)
-144:
-145: decision := {
-146: "rule_id": "PY-LOG-001",
-147: "reason": "Stdlib logging usage is prohibited. Use the project logging utilities instead.",
-148: "severity": "HIGH"
-149: }
-150: }
-151:
-152: deny contains decision if {
-153: input.hook_event_name == "PreToolUse"
-154: tool_name in {"Patch", "Apply_Patch"}
-155:
-156: patch := patch_content
-157: patch != null
-158:
-159: lower_patch := lower(patch)
-160: is_python_patch(lower_patch)
-161: regex.match(stdlib_logger_pattern, patch)
-162:
-163: decision := {
-164: "rule_id": "PY-LOG-001",
-165: "reason": "Stdlib logging usage is prohibited. Use the project logging utilities instead.",
-166: "severity": "HIGH"
-167: }
-168: }
-```
-
-## File: .cupcake/policies/opencode/block_assertion_roulette.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Assertion Roulette
- 4: # description: Blocks multiple bare asserts in a single test without messages
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_assertion_roulette
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101:
-102: patch_targets_path(pattern) if {
-103: patch := patch_content
-104: patch != null
-105: lines := split(patch, "\n")
-106: some line in lines
-107: startswith(line, "+++ b/")
-108: path := replace(line, "+++ b/", "")
-109: regex.match(pattern, path)
-110: }
-111:
-112: patch_targets_path(pattern) if {
-113: patch := patch_content
-114: patch != null
-115: lines := split(patch, "\n")
-116: some line in lines
-117: startswith(line, "--- a/")
-118: path := replace(line, "--- a/", "")
-119: regex.match(pattern, path)
-120: }
-121:
-122: file_path_pattern := `tests?/.*\.py$`
-123: assertion_pattern := `^\s*assert\s+[^,\n]+\n\s*assert\s+[^,\n]+$`
-124:
-125: # Block Write/Edit operations that introduce assertion roulette
-126:
-127: deny contains decision if {
-128: input.hook_event_name == "PreToolUse"
-129: tool_name in {"Write", "Edit", "NotebookEdit"}
-130:
-131: file_path := resolved_file_path
-132: regex.match(file_path_pattern, file_path)
-133:
-134: content := new_content
-135: content != null
-136: regex.match(assertion_pattern, content)
-137:
-138: decision := {
-139: "rule_id": "TEST-ASSERT-001",
-140: "reason": "Multiple bare asserts detected. Use one assert per test or add assertion messages.",
-141: "severity": "HIGH"
-142: }
-143: }
-144:
-145: deny contains decision if {
-146: input.hook_event_name == "PreToolUse"
-147: tool_name == "MultiEdit"
-148:
-149: some edit in tool_input.edits
-150: file_path := edit_path(edit)
-151: regex.match(file_path_pattern, file_path)
-152:
-153: content := edit_new_content(edit)
-154: content != null
-155: regex.match(assertion_pattern, content)
-156:
-157: decision := {
-158: "rule_id": "TEST-ASSERT-001",
-159: "reason": "Multiple bare asserts detected. Use one assert per test or add assertion messages.",
-160: "severity": "HIGH"
-161: }
-162: }
-163:
-164: deny contains decision if {
-165: input.hook_event_name == "PreToolUse"
-166: tool_name in {"Patch", "Apply_Patch"}
-167:
-168: patch := patch_content
-169: patch != null
-170:
-171: patch_targets_path(file_path_pattern)
-172:
-173: regex.match(assertion_pattern, patch)
-174:
-175: decision := {
-176: "rule_id": "TEST-ASSERT-001",
-177: "reason": "Multiple bare asserts detected. Use one assert per test or add assertion messages.",
-178: "severity": "HIGH"
-179: }
-180: }
-```
-
-## File: .cupcake/policies/opencode/block_biome_ignore.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Biome Ignore
- 4: # description: Blocks ignore directives in JS/TS files
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_biome_ignore
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101:
-102: patch_targets_path(pattern) if {
-103: patch := patch_content
-104: patch != null
-105: lines := split(patch, "\n")
-106: some line in lines
-107: startswith(line, "+++ b/")
-108: path := replace(line, "+++ b/", "")
-109: regex.match(pattern, path)
-110: }
-111:
-112: patch_targets_path(pattern) if {
-113: patch := patch_content
-114: patch != null
-115: lines := split(patch, "\n")
-116: some line in lines
-117: startswith(line, "--- a/")
-118: path := replace(line, "--- a/", "")
-119: regex.match(pattern, path)
-120: }
-121:
-122: file_path_pattern := `\.(js|jsx|ts|tsx|mjs|cjs)$`
-123: ignore_pattern := `//\s*biome-ignore|//\s*@ts-ignore|//\s*@ts-expect-error|//\s*@ts-nocheck|//\s*eslint-disable|/\*\s*eslint-disable`
-124:
-125: # Block Write/Edit operations that introduce ignore directives
-126:
-127: deny contains decision if {
-128: input.hook_event_name == "PreToolUse"
-129: tool_name in {"Write", "Edit", "NotebookEdit"}
-130:
-131: file_path := resolved_file_path
-132: regex.match(file_path_pattern, file_path)
-133:
-134: content := new_content
-135: content != null
-136: regex.match(ignore_pattern, content)
-137:
-138: decision := {
-139: "rule_id": "TS-LINT-002",
-140: "reason": "Ignore directives for Biome/TypeScript/ESLint are prohibited.",
-141: "severity": "HIGH"
-142: }
-143: }
-144:
-145: deny contains decision if {
-146: input.hook_event_name == "PreToolUse"
-147: tool_name == "MultiEdit"
-148:
-149: some edit in tool_input.edits
-150: file_path := edit_path(edit)
-151: regex.match(file_path_pattern, file_path)
-152:
-153: content := edit_new_content(edit)
-154: content != null
-155: regex.match(ignore_pattern, content)
-156:
-157: decision := {
-158: "rule_id": "TS-LINT-002",
-159: "reason": "Ignore directives for Biome/TypeScript/ESLint are prohibited.",
-160: "severity": "HIGH"
-161: }
-162: }
-163:
-164: deny contains decision if {
-165: input.hook_event_name == "PreToolUse"
-166: tool_name in {"Patch", "Apply_Patch"}
-167:
-168: patch := patch_content
-169: patch != null
-170:
-171: patch_targets_path(file_path_pattern)
-172:
-173: regex.match(ignore_pattern, patch)
-174:
-175: decision := {
-176: "rule_id": "TS-LINT-002",
-177: "reason": "Ignore directives for Biome/TypeScript/ESLint are prohibited.",
-178: "severity": "HIGH"
-179: }
-180: }
-```
-
-## File: .cupcake/policies/opencode/block_broad_exception_handler.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Broad Exception Handler
- 4: # description: Blocks bare Exception handlers that only log
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_broad_exception_handler
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: handler_pattern := `except\s+Exception\s*(?:as\s+\w+)?:\s*\n\s+(?:logger\.|logging\.)`
-102:
-103: # Block Write/Edit operations that introduce broad exception handlers
-104:
-105: deny contains decision if {
-106: input.hook_event_name == "PreToolUse"
-107: tool_name in {"Write", "Edit", "NotebookEdit"}
-108:
-109: content := new_content
-110: content != null
-111: regex.match(handler_pattern, content)
-112:
-113: decision := {
-114: "rule_id": "PY-EXC-001",
-115: "reason": "Broad Exception handlers that only log are prohibited.",
-116: "severity": "HIGH"
-117: }
-118: }
-119:
-120: deny contains decision if {
-121: input.hook_event_name == "PreToolUse"
-122: tool_name == "MultiEdit"
-123:
-124: some edit in tool_input.edits
-125: content := edit_new_content(edit)
-126: content != null
-127: regex.match(handler_pattern, content)
-128:
-129: decision := {
-130: "rule_id": "PY-EXC-001",
-131: "reason": "Broad Exception handlers that only log are prohibited.",
-132: "severity": "HIGH"
-133: }
-134: }
-135:
-136: deny contains decision if {
-137: input.hook_event_name == "PreToolUse"
-138: tool_name in {"Patch", "Apply_Patch"}
-139:
-140: patch := patch_content
-141: patch != null
-142:
-143:
-144: regex.match(handler_pattern, patch)
-145:
-146: decision := {
-147: "rule_id": "PY-EXC-001",
-148: "reason": "Broad Exception handlers that only log are prohibited.",
-149: "severity": "HIGH"
-150: }
-151: }
-```
-
-## File: .cupcake/policies/opencode/block_code_quality_test_edits.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Code Quality Test (Edits)
- 4: # description: Blocks file edits to src/test/code-quality.test.ts
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_code_quality_test_edits
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: file_path_pattern := `src/test/code-quality\.test\.ts$`
-102:
-103: # Block Write/Edit operations targeting code-quality test file
-104:
-105: deny contains decision if {
-106: input.hook_event_name == "PreToolUse"
-107: tool_name in {"Write", "Edit", "NotebookEdit"}
-108:
-109: file_path := resolved_file_path
-110: regex.match(file_path_pattern, file_path)
-111:
-112: decision := {
-113: "rule_id": "TS-QUALITY-002",
-114: "reason": "Direct edits to src/test/code-quality.test.ts are prohibited.",
-115: "severity": "HIGH"
-116: }
-117: }
-118:
-119: deny contains decision if {
-120: input.hook_event_name == "PreToolUse"
-121: tool_name == "MultiEdit"
-122:
-123: some edit in tool_input.edits
-124: file_path := edit_path(edit)
-125: regex.match(file_path_pattern, file_path)
-126:
-127: decision := {
-128: "rule_id": "TS-QUALITY-002",
-129: "reason": "Direct edits to src/test/code-quality.test.ts are prohibited.",
-130: "severity": "HIGH"
-131: }
-132: }
-```
-
-## File: .cupcake/policies/opencode/block_datetime_now_fallback.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block datetime.now Fallback
- 4: # description: Blocks returning datetime.now() as a fallback
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_datetime_now_fallback
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: pattern := `return\s+datetime\.now\s*\(`
-102:
-103: # Block Write/Edit operations that introduce datetime.now fallback
-104:
-105: deny contains decision if {
-106: input.hook_event_name == "PreToolUse"
-107: tool_name in {"Write", "Edit", "NotebookEdit"}
-108:
-109: content := new_content
-110: content != null
-111: regex.match(pattern, content)
-112:
-113: decision := {
-114: "rule_id": "PY-DT-001",
-115: "reason": "Returning datetime.now() as a fallback is prohibited. Use a caller-provided timestamp.",
-116: "severity": "HIGH"
-117: }
-118: }
-119:
-120: deny contains decision if {
-121: input.hook_event_name == "PreToolUse"
-122: tool_name == "MultiEdit"
-123:
-124: some edit in tool_input.edits
-125: content := edit_new_content(edit)
-126: content != null
-127: regex.match(pattern, content)
-128:
-129: decision := {
-130: "rule_id": "PY-DT-001",
-131: "reason": "Returning datetime.now() as a fallback is prohibited. Use a caller-provided timestamp.",
-132: "severity": "HIGH"
-133: }
-134: }
-135:
-136: deny contains decision if {
-137: input.hook_event_name == "PreToolUse"
-138: tool_name in {"Patch", "Apply_Patch"}
-139:
-140: patch := patch_content
-141: patch != null
-142:
-143:
-144: regex.match(pattern, patch)
-145:
-146: decision := {
-147: "rule_id": "PY-DT-001",
-148: "reason": "Returning datetime.now() as a fallback is prohibited. Use a caller-provided timestamp.",
-149: "severity": "HIGH"
-150: }
-151: }
-```
-
-## File: .cupcake/policies/opencode/block_default_value_swallow.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Default Value Swallow
- 4: # description: Blocks exception handlers that warn and return defaults
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_default_value_swallow
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: pattern := `except\s+\w*(?:Error|Exception).*?:\s*\n\s+.*?(?:logger\.|logging\.).*?(?:warning|warn).*?\n\s+return\s+(?:\w+Settings|Defaults?\(|default_|\{[^}]*\}|[A-Z_]+_DEFAULT)`
-102:
-103: # Block Write/Edit operations that swallow exceptions with defaults
-104:
-105: deny contains decision if {
-106: input.hook_event_name == "PreToolUse"
-107: tool_name in {"Write", "Edit", "NotebookEdit"}
-108:
-109: content := new_content
-110: content != null
-111: regex.match(pattern, content)
-112:
-113: decision := {
-114: "rule_id": "PY-EXC-002",
-115: "reason": "Swallowing exceptions and returning defaults is prohibited.",
-116: "severity": "HIGH"
-117: }
-118: }
-119:
-120: deny contains decision if {
-121: input.hook_event_name == "PreToolUse"
-122: tool_name == "MultiEdit"
-123:
-124: some edit in tool_input.edits
-125: content := edit_new_content(edit)
-126: content != null
-127: regex.match(pattern, content)
-128:
-129: decision := {
-130: "rule_id": "PY-EXC-002",
-131: "reason": "Swallowing exceptions and returning defaults is prohibited.",
-132: "severity": "HIGH"
-133: }
-134: }
-135:
-136: deny contains decision if {
-137: input.hook_event_name == "PreToolUse"
-138: tool_name in {"Patch", "Apply_Patch"}
-139:
-140: patch := patch_content
-141: patch != null
-142:
-143:
-144: regex.match(pattern, patch)
-145:
-146: decision := {
-147: "rule_id": "PY-EXC-002",
-148: "reason": "Swallowing exceptions and returning defaults is prohibited.",
-149: "severity": "HIGH"
-150: }
-151: }
-```
-
-## File: .cupcake/policies/opencode/block_duplicate_fixtures.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Duplicate Fixtures
- 4: # description: Blocks redefining global pytest fixtures outside conftest.py
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_duplicate_fixtures
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101:
-102: patch_targets_path(pattern) if {
-103: patch := patch_content
-104: patch != null
-105: lines := split(patch, "\n")
-106: some line in lines
-107: startswith(line, "+++ b/")
-108: path := replace(line, "+++ b/", "")
-109: regex.match(pattern, path)
-110: }
-111:
-112: patch_targets_path(pattern) if {
-113: patch := patch_content
-114: patch != null
-115: lines := split(patch, "\n")
-116: some line in lines
-117: startswith(line, "--- a/")
-118: path := replace(line, "--- a/", "")
-119: regex.match(pattern, path)
-120: }
-121:
-122: file_path_pattern := `tests?/.*\.py$`
-123: conftest_pattern := `tests?/conftest\.py$`
-124: fixture_pattern := `@pytest\.fixture[^@]*\ndef\s+(mock_uow|crypto|meetings_dir|webhook_config|webhook_config_all_events|sample_datetime|calendar_settings|meeting_id|sample_meeting|recording_meeting|mock_grpc_context|mock_asr_engine|mock_optional_extras)\s*\(`
-125:
-126: # Block Write/Edit operations that introduce duplicate fixtures
-127:
-128: deny contains decision if {
-129: input.hook_event_name == "PreToolUse"
-130: tool_name in {"Write", "Edit", "NotebookEdit"}
-131:
-132: file_path := resolved_file_path
-133: regex.match(file_path_pattern, file_path)
-134: not regex.match(conftest_pattern, file_path)
-135:
-136: content := new_content
-137: content != null
-138: regex.match(fixture_pattern, content)
-139:
-140: decision := {
-141: "rule_id": "TEST-FIX-001",
-142: "reason": "Duplicate global fixtures are prohibited. Use tests/conftest.py fixtures instead.",
-143: "severity": "HIGH"
-144: }
-145: }
-146:
-147: deny contains decision if {
-148: input.hook_event_name == "PreToolUse"
-149: tool_name == "MultiEdit"
-150:
-151: some edit in tool_input.edits
-152: file_path := edit_path(edit)
-153: regex.match(file_path_pattern, file_path)
-154: not regex.match(conftest_pattern, file_path)
-155:
-156: content := edit_new_content(edit)
-157: content != null
-158: regex.match(fixture_pattern, content)
-159:
-160: decision := {
-161: "rule_id": "TEST-FIX-001",
-162: "reason": "Duplicate global fixtures are prohibited. Use tests/conftest.py fixtures instead.",
-163: "severity": "HIGH"
-164: }
-165: }
-166:
-167: deny contains decision if {
-168: input.hook_event_name == "PreToolUse"
-169: tool_name in {"Patch", "Apply_Patch"}
-170:
-171: patch := patch_content
-172: patch != null
-173:
-174: patch_targets_path(file_path_pattern)
-175: not regex.match(conftest_pattern, patch)
-176:
-177: regex.match(fixture_pattern, patch)
-178:
-179: decision := {
-180: "rule_id": "TEST-FIX-001",
-181: "reason": "Duplicate global fixtures are prohibited. Use tests/conftest.py fixtures instead.",
-182: "severity": "HIGH"
-183: }
-184: }
-```
-
-## File: .cupcake/policies/opencode/block_linter_config_frontend.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Frontend Linter Config
- 4: # description: Blocks edits to frontend linter config files
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_linter_config_frontend
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: file_path_pattern := `(^|/)client/.*(?:\.?eslint(?:rc|\.config).*|\.?prettier(?:rc|\.config).*|biome\.json|tsconfig\.json|\.?rustfmt\.toml|\.?clippy\.toml)$`
-102:
-103: # Block Write/Edit operations targeting frontend linter configs
-104:
-105: deny contains decision if {
-106: input.hook_event_name == "PreToolUse"
-107: tool_name in {"Write", "Edit", "NotebookEdit"}
-108:
-109: file_path := resolved_file_path
-110: regex.match(file_path_pattern, file_path)
-111: not contains(lower(file_path), "node_modules/")
-112:
-113: decision := {
-114: "rule_id": "TS-CONFIG-002",
-115: "reason": "Frontend linter/config file edits are prohibited.",
-116: "severity": "HIGH"
-117: }
-118: }
-119:
-120: deny contains decision if {
-121: input.hook_event_name == "PreToolUse"
-122: tool_name == "MultiEdit"
-123:
-124: some edit in tool_input.edits
-125: file_path := edit_path(edit)
-126: regex.match(file_path_pattern, file_path)
-127: not contains(lower(file_path), "node_modules/")
-128:
-129: decision := {
-130: "rule_id": "TS-CONFIG-002",
-131: "reason": "Frontend linter/config file edits are prohibited.",
-132: "severity": "HIGH"
-133: }
-134: }
-```
-
-## File: .cupcake/policies/opencode/block_linter_config_python.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Python Linter Config
- 4: # description: Blocks edits to Python linter config files
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_linter_config_python
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: file_path_pattern := `(?:pyproject\.toml|\.?ruff\.toml|\.?pyrightconfig\.json|\.?mypy\.ini|setup\.cfg|\.flake8|tox\.ini|\.?pylintrc)$`
-102:
-103: # Block Write/Edit operations targeting Python linter configs
-104:
-105: deny contains decision if {
-106: input.hook_event_name == "PreToolUse"
-107: tool_name in {"Write", "Edit", "NotebookEdit"}
-108:
-109: file_path := resolved_file_path
-110: regex.match(file_path_pattern, file_path)
-111: not contains(lower(file_path), "/.venv/")
-112:
-113: decision := {
-114: "rule_id": "PY-CONFIG-002",
-115: "reason": "Python linter/config file edits are prohibited.",
-116: "severity": "HIGH"
-117: }
-118: }
-119:
-120: deny contains decision if {
-121: input.hook_event_name == "PreToolUse"
-122: tool_name == "MultiEdit"
-123:
-124: some edit in tool_input.edits
-125: file_path := edit_path(edit)
-126: regex.match(file_path_pattern, file_path)
-127: not contains(lower(file_path), "/.venv/")
-128:
-129: decision := {
-130: "rule_id": "PY-CONFIG-002",
-131: "reason": "Python linter/config file edits are prohibited.",
-132: "severity": "HIGH"
-133: }
-134: }
-```
-
-## File: .cupcake/policies/opencode/block_magic_numbers.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Magic Numbers
- 4: # description: Blocks introduction of magic numbers outside constants modules
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_magic_numbers
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101:
-102: patch_targets_path(pattern) if {
-103: patch := patch_content
-104: patch != null
-105: lines := split(patch, "\n")
-106: some line in lines
-107: startswith(line, "+++ b/")
-108: path := replace(line, "+++ b/", "")
-109: regex.match(pattern, path)
-110: }
-111:
-112: patch_targets_path(pattern) if {
-113: patch := patch_content
-114: patch != null
-115: lines := split(patch, "\n")
-116: some line in lines
-117: startswith(line, "--- a/")
-118: path := replace(line, "--- a/", "")
-119: regex.match(pattern, path)
-120: }
-121:
-122: file_path_pattern := `\.(py|ts|tsx|js|jsx)$`
-123: number_pattern := `(?:timeout|delay|interval|duration|limit|max|min|size|count|threshold|retry|retries|attempts|port|width|height|margin|padding|offset|index|length|capacity|buffer|batch|chunk|page|rate|fps|dpi|quality|level|priority|weight|score|factor|multiplier|divisor|percentage|ratio|scale)\s*[=:]\s*([2-9]|[1-9]\d+)|(?:if|while|for|elif|range|slice|sleep|wait|setTimeout|setInterval)\s*\([^)]*([2-9]|[1-9]\d+)`
-124:
-125: # Block Write/Edit operations that introduce magic numbers
-126:
-127: deny contains decision if {
-128: input.hook_event_name == "PreToolUse"
-129: tool_name in {"Write", "Edit", "NotebookEdit"}
-130:
-131: file_path := resolved_file_path
-132: regex.match(file_path_pattern, file_path)
-133: not contains(lower(file_path), "constants")
-134:
-135: content := new_content
-136: content != null
-137: regex.match(number_pattern, content)
-138:
-139: decision := {
-140: "rule_id": "STYLE-001",
-141: "reason": "Magic numbers are prohibited. Use named constants.",
-142: "severity": "HIGH"
-143: }
-144: }
-145:
-146: deny contains decision if {
-147: input.hook_event_name == "PreToolUse"
-148: tool_name == "MultiEdit"
-149:
-150: some edit in tool_input.edits
-151: file_path := edit_path(edit)
-152: regex.match(file_path_pattern, file_path)
-153: not contains(lower(file_path), "constants")
-154:
-155: content := edit_new_content(edit)
-156: content != null
-157: regex.match(number_pattern, content)
-158:
-159: decision := {
-160: "rule_id": "STYLE-001",
-161: "reason": "Magic numbers are prohibited. Use named constants.",
-162: "severity": "HIGH"
-163: }
-164: }
-165:
-166: deny contains decision if {
-167: input.hook_event_name == "PreToolUse"
-168: tool_name in {"Patch", "Apply_Patch"}
-169:
-170: patch := patch_content
-171: patch != null
-172:
-173: patch_targets_path(file_path_pattern)
-174: not contains(lower(patch), "constants")
-175:
-176: regex.match(number_pattern, patch)
-177:
-178: decision := {
-179: "rule_id": "STYLE-001",
-180: "reason": "Magic numbers are prohibited. Use named constants.",
-181: "severity": "HIGH"
-182: }
-183: }
-```
-
-## File: .cupcake/policies/opencode/block_makefile_edit.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Makefile Edit
- 4: # description: Blocks file edits to Makefile
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_makefile_edit
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: file_path_pattern := `(?:^|/)Makefile$`
-102:
-103: # Block Write/Edit operations targeting Makefile
-104:
-105: deny contains decision if {
-106: input.hook_event_name == "PreToolUse"
-107: tool_name in {"Write", "Edit", "NotebookEdit"}
-108:
-109: file_path := resolved_file_path
-110: regex.match(file_path_pattern, file_path)
-111:
-112: decision := {
-113: "rule_id": "BUILD-002",
-114: "reason": "Makefile edits are prohibited.",
-115: "severity": "HIGH"
-116: }
-117: }
-118:
-119: deny contains decision if {
-120: input.hook_event_name == "PreToolUse"
-121: tool_name == "MultiEdit"
-122:
-123: some edit in tool_input.edits
-124: file_path := edit_path(edit)
-125: regex.match(file_path_pattern, file_path)
-126:
-127: decision := {
-128: "rule_id": "BUILD-002",
-129: "reason": "Makefile edits are prohibited.",
-130: "severity": "HIGH"
-131: }
-132: }
-```
-
-## File: .cupcake/policies/opencode/block_silent_none_return.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Silent None Return
- 4: # description: Blocks exception handlers that log and return empty values
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_silent_none_return
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: pattern := `except\s+\w*Error.*?:\s*\n\s+.*?(?:logger\.|logging\.).*?\n\s+return\s+(?:None|\[\]|False|\{\}|0)`
-102:
-103: # Block Write/Edit operations that swallow exceptions with empty returns
-104:
-105: deny contains decision if {
-106: input.hook_event_name == "PreToolUse"
-107: tool_name in {"Write", "Edit", "NotebookEdit"}
-108:
-109: content := new_content
-110: content != null
-111: regex.match(pattern, content)
-112:
-113: decision := {
-114: "rule_id": "PY-EXC-003",
-115: "reason": "Silent exception handlers returning empty values are prohibited.",
-116: "severity": "HIGH"
-117: }
-118: }
-119:
-120: deny contains decision if {
-121: input.hook_event_name == "PreToolUse"
-122: tool_name == "MultiEdit"
-123:
-124: some edit in tool_input.edits
-125: content := edit_new_content(edit)
-126: content != null
-127: regex.match(pattern, content)
-128:
-129: decision := {
-130: "rule_id": "PY-EXC-003",
-131: "reason": "Silent exception handlers returning empty values are prohibited.",
-132: "severity": "HIGH"
-133: }
-134: }
-135:
-136: deny contains decision if {
-137: input.hook_event_name == "PreToolUse"
-138: tool_name in {"Patch", "Apply_Patch"}
-139:
-140: patch := patch_content
-141: patch != null
-142:
-143:
-144: regex.match(pattern, patch)
-145:
-146: decision := {
-147: "rule_id": "PY-EXC-003",
-148: "reason": "Silent exception handlers returning empty values are prohibited.",
-149: "severity": "HIGH"
-150: }
-151: }
-```
-
-## File: .cupcake/policies/opencode/block_test_loops_conditionals.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Test Loops/Conditionals
- 4: # description: Blocks loops or conditionals inside tests with asserts
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_test_loops_conditionals
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101:
-102: patch_targets_path(pattern) if {
-103: patch := patch_content
-104: patch != null
-105: lines := split(patch, "\n")
-106: some line in lines
-107: startswith(line, "+++ b/")
-108: path := replace(line, "+++ b/", "")
-109: regex.match(pattern, path)
-110: }
-111:
-112: patch_targets_path(pattern) if {
-113: patch := patch_content
-114: patch != null
-115: lines := split(patch, "\n")
-116: some line in lines
-117: startswith(line, "--- a/")
-118: path := replace(line, "--- a/", "")
-119: regex.match(pattern, path)
-120: }
-121:
-122: file_path_pattern := `tests?/.*\.py$`
-123: pattern := `def test_[^(]+\([^)]*\)[^:]*:[\s\S]*?\b(for|while|if)\s+[^:]+:[\s\S]*?assert`
-124:
-125: # Block Write/Edit operations that introduce loops/conditionals in tests
-126:
-127: deny contains decision if {
-128: input.hook_event_name == "PreToolUse"
-129: tool_name in {"Write", "Edit", "NotebookEdit"}
-130:
-131: file_path := resolved_file_path
-132: regex.match(file_path_pattern, file_path)
-133:
-134: content := new_content
-135: content != null
-136: regex.match(pattern, content)
-137:
-138: decision := {
-139: "rule_id": "TEST-STRUCT-001",
-140: "reason": "Loops or conditionals inside tests are prohibited. Use parametrization.",
-141: "severity": "HIGH"
-142: }
-143: }
-144:
-145: deny contains decision if {
-146: input.hook_event_name == "PreToolUse"
-147: tool_name == "MultiEdit"
-148:
-149: some edit in tool_input.edits
-150: file_path := edit_path(edit)
-151: regex.match(file_path_pattern, file_path)
-152:
-153: content := edit_new_content(edit)
-154: content != null
-155: regex.match(pattern, content)
-156:
-157: decision := {
-158: "rule_id": "TEST-STRUCT-001",
-159: "reason": "Loops or conditionals inside tests are prohibited. Use parametrization.",
-160: "severity": "HIGH"
-161: }
-162: }
-163:
-164: deny contains decision if {
-165: input.hook_event_name == "PreToolUse"
-166: tool_name in {"Patch", "Apply_Patch"}
-167:
-168: patch := patch_content
-169: patch != null
-170:
-171: patch_targets_path(file_path_pattern)
-172:
-173: regex.match(pattern, patch)
-174:
-175: decision := {
-176: "rule_id": "TEST-STRUCT-001",
-177: "reason": "Loops or conditionals inside tests are prohibited. Use parametrization.",
-178: "severity": "HIGH"
-179: }
-180: }
-```
-
-## File: .cupcake/policies/opencode/block_tests_quality.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Block Tests Quality
- 4: # description: Blocks edits to tests/quality (except baselines.json)
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.block_tests_quality
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: file_path_pattern := `tests/quality/`
-102: exclude_pattern := `baselines\.json$`
-103:
-104: # Block Write/Edit operations targeting tests/quality
-105:
-106: deny contains decision if {
-107: input.hook_event_name == "PreToolUse"
-108: tool_name in {"Write", "Edit", "NotebookEdit"}
-109:
-110: file_path := resolved_file_path
-111: regex.match(file_path_pattern, file_path)
-112: not regex.match(exclude_pattern, file_path)
-113:
-114: decision := {
-115: "rule_id": "TEST-QUALITY-002",
-116: "reason": "Direct edits to tests/quality are prohibited (except baselines.json).",
-117: "severity": "HIGH"
-118: }
-119: }
-120:
-121: deny contains decision if {
-122: input.hook_event_name == "PreToolUse"
-123: tool_name == "MultiEdit"
-124:
-125: some edit in tool_input.edits
-126: file_path := edit_path(edit)
-127: regex.match(file_path_pattern, file_path)
-128: not regex.match(exclude_pattern, file_path)
-129:
-130: decision := {
-131: "rule_id": "TEST-QUALITY-002",
-132: "reason": "Direct edits to tests/quality are prohibited (except baselines.json).",
-133: "severity": "HIGH"
-134: }
-135: }
-```
-
-## File: .cupcake/policies/opencode/prevent_any_type.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Ban Python Any Type
- 4: # description: Blocks introduction of typing.Any in Python code
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.prevent_any_type
- 10:
- 11: import rego.v1
- 12:
- 13: tool_name := input.tool_name if {
- 14: input.tool_name != null
- 15: } else := input.tool
- 16:
- 17: tool_input := input.tool_input if {
- 18: input.tool_input != null
- 19: } else := input.args
- 20:
- 21: resolved_file_path := input.resolved_file_path if {
- 22: input.resolved_file_path != null
- 23: } else := tool_input.file_path if {
- 24: tool_input.file_path != null
- 25: } else := tool_input.filePath if {
- 26: tool_input.filePath != null
- 27: } else := tool_input.path if {
- 28: tool_input.path != null
- 29: } else := tool_input.notebook_path if {
- 30: tool_input.notebook_path != null
- 31: } else := tool_input.notebookPath if {
- 32: tool_input.notebookPath != null
- 33: } else := ""
- 34:
- 35: new_content := tool_input.new_string if {
- 36: tool_input.new_string != null
- 37: } else := tool_input.newText if {
- 38: tool_input.newText != null
- 39: } else := tool_input.new_text if {
- 40: tool_input.new_text != null
- 41: } else := tool_input.content if {
- 42: tool_input.content != null
- 43: } else := ""
- 44:
- 45: old_content := tool_input.old_string if {
- 46: tool_input.old_string != null
- 47: } else := tool_input.oldText if {
- 48: tool_input.oldText != null
- 49: } else := tool_input.old_text if {
- 50: tool_input.old_text != null
- 51: } else := tool_input.previousContent if {
- 52: tool_input.previousContent != null
- 53: } else := ""
- 54:
- 55: patch_content := tool_input.patch if {
- 56: tool_input.patch != null
- 57: } else := tool_input.patchText if {
- 58: tool_input.patchText != null
- 59: } else := tool_input.patch_text if {
- 60: tool_input.patch_text != null
- 61: } else := ""
- 62:
- 63: edit_path(edit) := path if {
- 64: edit.resolved_file_path != null
- 65: path := edit.resolved_file_path
- 66: } else := path if {
- 67: edit.file_path != null
- 68: path := edit.file_path
- 69: } else := path if {
- 70: edit.filePath != null
- 71: path := edit.filePath
- 72: } else := path if {
- 73: edit.path != null
- 74: path := edit.path
- 75: } else := ""
- 76:
- 77: edit_new_content(edit) := content if {
- 78: edit.new_string != null
- 79: content := edit.new_string
- 80: } else := content if {
- 81: edit.newText != null
- 82: content := edit.newText
- 83: } else := content if {
- 84: edit.new_text != null
- 85: content := edit.new_text
- 86: } else := content if {
- 87: edit.content != null
- 88: content := edit.content
- 89: } else := ""
- 90:
- 91: edit_old_content(edit) := content if {
- 92: edit.old_string != null
- 93: content := edit.old_string
- 94: } else := content if {
- 95: edit.oldText != null
- 96: content := edit.oldText
- 97: } else := content if {
- 98: edit.old_text != null
- 99: content := edit.old_text
-100: } else := ""
-101:
-102: is_python_file(path) if {
-103: endswith(path, ".py")
-104: }
-105:
-106: is_python_file(path) if {
-107: endswith(path, ".pyi")
-108: }
-109:
-110: # Regex patterns indicating use of Any in type annotations/imports
-111: any_type_patterns := [
-112: `(?m)^\s*from\s+typing\s+import\s+[^#\n]*\bAny\b`,
-113: `\btyping\.Any\b`,
-114: `:\s*Any\b`,
-115: `:\s*"Any"`,
-116: `:\s*'Any'`,
-117: `->\s*Any\b`,
-118: `->\s*"Any"`,
-119: `->\s*'Any'`,
-120: `\[\s*Any\s*\]`,
-121: `\[\s*Any\s*,`,
-122: `,\s*Any\s*\]`,
-123: `,\s*Any\s*,`,
-124: `Union\[[^\]]*\bAny\b[^\]]*\]`,
-125: `Optional\[Any\]`,
-126: ]
-127:
-128: # Block Write/Edit operations that introduce Any in Python files
-129: deny contains decision if {
-130: input.hook_event_name == "PreToolUse"
-131: tool_name in {"Write", "Edit", "NotebookEdit"}
-132:
-133: # Only enforce for Python files
-134: file_path := lower(resolved_file_path)
-135: is_python_file(file_path)
-136:
-137: content := new_content
-138: content != null
-139:
-140: some pattern in any_type_patterns
-141: regex.match(pattern, content)
-142:
-143: decision := {
-144: "rule_id": "PY-TYPE-001",
-145: "reason": "Use of Any is prohibited in Python type annotations/imports. Replace with Protocol, TypeVar, TypedDict, or a concrete type.",
-146: "severity": "HIGH"
-147: }
-148: }
-149:
-150: deny contains decision if {
-151: input.hook_event_name == "PreToolUse"
-152: tool_name in {"Patch", "Apply_Patch"}
-153:
-154: content := patch_content
-155: content != null
-156:
-157: some pattern in any_type_patterns
-158: regex.match(pattern, content)
-159:
-160: decision := {
-161: "rule_id": "PY-TYPE-001",
-162: "reason": "Use of Any is prohibited in Python type annotations/imports. Replace with Protocol, TypeVar, TypedDict, or a concrete type.",
-163: "severity": "HIGH"
-164: }
-165: }
-166:
-167: deny contains decision if {
-168: input.hook_event_name == "PreToolUse"
-169: tool_name == "MultiEdit"
-170:
-171: some edit in tool_input.edits
-172: file_path := lower(edit_path(edit))
-173: is_python_file(file_path)
-174:
-175: content := edit_new_content(edit)
-176: content != null
-177:
-178: some pattern in any_type_patterns
-179: regex.match(pattern, content)
-180:
-181: decision := {
-182: "rule_id": "PY-TYPE-001",
-183: "reason": "Use of Any is prohibited in Python type annotations/imports. Replace with Protocol, TypeVar, TypedDict, or a concrete type.",
-184: "severity": "HIGH"
-185: }
-186: }
-```
-
-## File: .cupcake/policies/opencode/prevent_type_suppression.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Ban Python Type Suppression
- 4: # description: Blocks type suppression directives in Python code
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.prevent_type_suppression
- 10:
- 11: import rego.v1
- 12:
- 13: tool_name := input.tool_name if {
- 14: input.tool_name != null
- 15: } else := input.tool
- 16:
- 17: tool_input := input.tool_input if {
- 18: input.tool_input != null
- 19: } else := input.args
- 20:
- 21: resolved_file_path := input.resolved_file_path if {
- 22: input.resolved_file_path != null
- 23: } else := tool_input.file_path if {
- 24: tool_input.file_path != null
- 25: } else := tool_input.filePath if {
- 26: tool_input.filePath != null
- 27: } else := tool_input.path if {
- 28: tool_input.path != null
- 29: } else := tool_input.notebook_path if {
- 30: tool_input.notebook_path != null
- 31: } else := tool_input.notebookPath if {
- 32: tool_input.notebookPath != null
- 33: } else := ""
- 34:
- 35: new_content := tool_input.new_string if {
- 36: tool_input.new_string != null
- 37: } else := tool_input.newText if {
- 38: tool_input.newText != null
- 39: } else := tool_input.new_text if {
- 40: tool_input.new_text != null
- 41: } else := tool_input.content if {
- 42: tool_input.content != null
- 43: } else := ""
- 44:
- 45: old_content := tool_input.old_string if {
- 46: tool_input.old_string != null
- 47: } else := tool_input.oldText if {
- 48: tool_input.oldText != null
- 49: } else := tool_input.old_text if {
- 50: tool_input.old_text != null
- 51: } else := tool_input.previousContent if {
- 52: tool_input.previousContent != null
- 53: } else := ""
- 54:
- 55: patch_content := tool_input.patch if {
- 56: tool_input.patch != null
- 57: } else := tool_input.patchText if {
- 58: tool_input.patchText != null
- 59: } else := tool_input.patch_text if {
- 60: tool_input.patch_text != null
- 61: } else := ""
- 62:
- 63: edit_path(edit) := path if {
- 64: edit.resolved_file_path != null
- 65: path := edit.resolved_file_path
- 66: } else := path if {
- 67: edit.file_path != null
- 68: path := edit.file_path
- 69: } else := path if {
- 70: edit.filePath != null
- 71: path := edit.filePath
- 72: } else := path if {
- 73: edit.path != null
- 74: path := edit.path
- 75: } else := ""
- 76:
- 77: edit_new_content(edit) := content if {
- 78: edit.new_string != null
- 79: content := edit.new_string
- 80: } else := content if {
- 81: edit.newText != null
- 82: content := edit.newText
- 83: } else := content if {
- 84: edit.new_text != null
- 85: content := edit.new_text
- 86: } else := content if {
- 87: edit.content != null
- 88: content := edit.content
- 89: } else := ""
- 90:
- 91: edit_old_content(edit) := content if {
- 92: edit.old_string != null
- 93: content := edit.old_string
- 94: } else := content if {
- 95: edit.oldText != null
- 96: content := edit.oldText
- 97: } else := content if {
- 98: edit.old_text != null
- 99: content := edit.old_text
-100: } else := ""
-101:
-102: is_python_file(path) if {
-103: endswith(path, ".py")
-104: }
-105:
-106: is_python_file(path) if {
-107: endswith(path, ".pyi")
-108: }
-109:
-110: # Regex patterns indicating type suppression directives
-111: type_suppression_patterns := [
-112: `#\s*type:\s*ignore(\[[^\]]+\])?\b`,
-113: `#\s*pyright:\s*ignore(\[[^\]]+\])?\b`,
-114: `#\s*mypy:\s*ignore(\[[^\]]+\])?\b`,
-115: `#\s*pyre-ignore\b`,
-116: `#\s*pyre-fixme\b`,
-117: `#\s*pyrefly:\s*ignore(\[[^\]]+\])?\b`,
-118: `#\s*basedpyright:\s*ignore(\[[^\]]+\])?\b`,
-119: `#\s*noqa\b`,
-120: `#\s*noqa:\s*\w+`,
-121: ]
-122:
-123: # Block Write/Edit operations that introduce type suppression in Python files
-124: deny contains decision if {
-125: input.hook_event_name == "PreToolUse"
-126: tool_name in {"Write", "Edit", "NotebookEdit"}
-127:
-128: # Only enforce for Python files
-129: file_path := lower(resolved_file_path)
-130: is_python_file(file_path)
-131:
-132: content := new_content
-133: content != null
-134:
-135: some pattern in type_suppression_patterns
-136: regex.match(pattern, content)
-137:
-138: decision := {
-139: "rule_id": "PY-TYPE-002",
-140: "reason": "Type suppression directives are prohibited in Python code. Fix the underlying type/lint issues instead.",
-141: "severity": "HIGH"
-142: }
-143: }
-144:
-145: deny contains decision if {
-146: input.hook_event_name == "PreToolUse"
-147: tool_name in {"Patch", "Apply_Patch"}
-148:
-149: content := patch_content
-150: content != null
-151:
-152: some pattern in type_suppression_patterns
-153: regex.match(pattern, content)
-154:
-155: decision := {
-156: "rule_id": "PY-TYPE-002",
-157: "reason": "Type suppression directives are prohibited in Python code. Fix the underlying type/lint issues instead.",
-158: "severity": "HIGH"
-159: }
-160: }
-161:
-162: deny contains decision if {
-163: input.hook_event_name == "PreToolUse"
-164: tool_name == "MultiEdit"
-165:
-166: some edit in tool_input.edits
-167: file_path := lower(edit_path(edit))
-168: is_python_file(file_path)
-169:
-170: content := edit_new_content(edit)
-171: content != null
-172:
-173: some pattern in type_suppression_patterns
-174: regex.match(pattern, content)
-175:
-176: decision := {
-177: "rule_id": "PY-TYPE-002",
-178: "reason": "Type suppression directives are prohibited in Python code. Fix the underlying type/lint issues instead.",
-179: "severity": "HIGH"
-180: }
-181: }
-```
-
-## File: .cupcake/policies/opencode/warn_baselines_edit.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Warn on Baselines Edit
- 4: # description: Warns on edits to tests/quality/baselines.json
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.warn_baselines_edit
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101: file_path_pattern := `tests/quality/baselines\.json$`
-102:
-103: # Warn on Write/Edit operations targeting baselines.json
-104:
-105: deny contains decision if {
-106: input.hook_event_name == "PreToolUse"
-107: tool_name in {"Write", "Edit", "NotebookEdit"}
-108:
-109: file_path := resolved_file_path
-110: regex.match(file_path_pattern, file_path)
-111:
-112: decision := {
-113: "rule_id": "TEST-QUALITY-004",
-114: "reason": "Warning: editing tests/quality/baselines.json should be avoided unless explicitly required.",
-115: "severity": "LOW"
-116: }
-117: }
-118:
-119: deny contains decision if {
-120: input.hook_event_name == "PreToolUse"
-121: tool_name == "MultiEdit"
-122:
-123: some edit in tool_input.edits
-124: file_path := edit_path(edit)
-125: regex.match(file_path_pattern, file_path)
-126:
-127: decision := {
-128: "rule_id": "TEST-QUALITY-004",
-129: "reason": "Warning: editing tests/quality/baselines.json should be avoided unless explicitly required.",
-130: "severity": "LOW"
-131: }
-132: }
-```
-
-## File: .cupcake/policies/opencode/warn_large_file.rego
-```
- 1: # METADATA
- 2: # scope: package
- 3: # title: Warn on Large File
- 4: # description: Warns when writing large files (>= 500 lines)
- 5: # custom:
- 6: # routing:
- 7: # required_events: ["PreToolUse"]
- 8: # required_tools: ["Apply_Patch", "Edit", "MultiEdit", "NotebookEdit", "Patch", "Write"]
- 9: package cupcake.policies.opencode.warn_large_file
- 10: import rego.v1
- 11:
- 12: tool_name := input.tool_name if {
- 13: input.tool_name != null
- 14: } else := input.tool
- 15:
- 16: tool_input := input.tool_input if {
- 17: input.tool_input != null
- 18: } else := input.args
- 19:
- 20: resolved_file_path := input.resolved_file_path if {
- 21: input.resolved_file_path != null
- 22: } else := tool_input.file_path if {
- 23: tool_input.file_path != null
- 24: } else := tool_input.filePath if {
- 25: tool_input.filePath != null
- 26: } else := tool_input.path if {
- 27: tool_input.path != null
- 28: } else := tool_input.notebook_path if {
- 29: tool_input.notebook_path != null
- 30: } else := tool_input.notebookPath if {
- 31: tool_input.notebookPath != null
- 32: } else := ""
- 33:
- 34: new_content := tool_input.new_string if {
- 35: tool_input.new_string != null
- 36: } else := tool_input.newText if {
- 37: tool_input.newText != null
- 38: } else := tool_input.new_text if {
- 39: tool_input.new_text != null
- 40: } else := tool_input.content if {
- 41: tool_input.content != null
- 42: } else := ""
- 43:
- 44: old_content := tool_input.old_string if {
- 45: tool_input.old_string != null
- 46: } else := tool_input.oldText if {
- 47: tool_input.oldText != null
- 48: } else := tool_input.old_text if {
- 49: tool_input.old_text != null
- 50: } else := tool_input.previousContent if {
- 51: tool_input.previousContent != null
- 52: } else := ""
- 53:
- 54: patch_content := tool_input.patch if {
- 55: tool_input.patch != null
- 56: } else := tool_input.patchText if {
- 57: tool_input.patchText != null
- 58: } else := tool_input.patch_text if {
- 59: tool_input.patch_text != null
- 60: } else := ""
- 61:
- 62: edit_path(edit) := path if {
- 63: edit.resolved_file_path != null
- 64: path := edit.resolved_file_path
- 65: } else := path if {
- 66: edit.file_path != null
- 67: path := edit.file_path
- 68: } else := path if {
- 69: edit.filePath != null
- 70: path := edit.filePath
- 71: } else := path if {
- 72: edit.path != null
- 73: path := edit.path
- 74: } else := ""
- 75:
- 76: edit_new_content(edit) := content if {
- 77: edit.new_string != null
- 78: content := edit.new_string
- 79: } else := content if {
- 80: edit.newText != null
- 81: content := edit.newText
- 82: } else := content if {
- 83: edit.new_text != null
- 84: content := edit.new_text
- 85: } else := content if {
- 86: edit.content != null
- 87: content := edit.content
- 88: } else := ""
- 89:
- 90: edit_old_content(edit) := content if {
- 91: edit.old_string != null
- 92: content := edit.old_string
- 93: } else := content if {
- 94: edit.oldText != null
- 95: content := edit.oldText
- 96: } else := content if {
- 97: edit.old_text != null
- 98: content := edit.old_text
- 99: } else := ""
-100:
-101:
-102: patch_targets_path(pattern) if {
-103: patch := patch_content
-104: patch != null
-105: lines := split(patch, "\n")
-106: some line in lines
-107: startswith(line, "+++ b/")
-108: path := replace(line, "+++ b/", "")
-109: regex.match(pattern, path)
-110: }
-111:
-112: patch_targets_path(pattern) if {
-113: patch := patch_content
-114: patch != null
-115: lines := split(patch, "\n")
-116: some line in lines
-117: startswith(line, "--- a/")
-118: path := replace(line, "--- a/", "")
-119: regex.match(pattern, path)
-120: }
-121:
-122: file_path_pattern := `\.(py|ts|tsx|js|jsx)$`
-123: pattern := `(?:.*\n){500,}`
-124:
-125: # Warn on Write/Edit operations that introduce large file content
-126:
-127: deny contains decision if {
-128: input.hook_event_name == "PreToolUse"
-129: tool_name in {"Write", "Edit", "NotebookEdit"}
-130:
-131: file_path := resolved_file_path
-132: regex.match(file_path_pattern, file_path)
-133:
-134: content := new_content
-135: content != null
-136: regex.match(pattern, content)
-137:
-138: decision := {
-139: "rule_id": "STYLE-002",
-140: "reason": "Warning: file content exceeds 500 lines. Consider refactoring.",
-141: "severity": "LOW"
-142: }
-143: }
-144:
-145: deny contains decision if {
-146: input.hook_event_name == "PreToolUse"
-147: tool_name == "MultiEdit"
-148:
-149: some edit in tool_input.edits
-150: file_path := edit_path(edit)
-151: regex.match(file_path_pattern, file_path)
-152:
-153: content := edit_new_content(edit)
-154: content != null
-155: regex.match(pattern, content)
-156:
-157: decision := {
-158: "rule_id": "STYLE-002",
-159: "reason": "Warning: file content exceeds 500 lines. Consider refactoring.",
-160: "severity": "LOW"
-161: }
-162: }
-163:
-164: deny contains decision if {
-165: input.hook_event_name == "PreToolUse"
-166: tool_name in {"Patch", "Apply_Patch"}
-167:
-168: patch := patch_content
-169: patch != null
-170:
-171: patch_targets_path(file_path_pattern)
-172:
-173: regex.match(pattern, patch)
-174:
-175: decision := {
-176: "rule_id": "STYLE-002",
-177: "reason": "Warning: file content exceeds 500 lines. Consider refactoring.",
-178: "severity": "LOW"
-179: }
-180: }
-```
diff --git a/test_any_import.py b/test_any_import.py
deleted file mode 100644
index a1560aa..0000000
--- a/test_any_import.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from typing import Any
-
-
-def process(data: Any) -> Any:
- result: Any = data
- return result
-
-
-def store(items: dict[str, Any]) -> None:
- pass