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