Self-organizing AI second brain for Obsidian + Claude Code. Drop any source and Claude reads, links, and files it into one connected knowledge graph of plain Markdown you own. AI note-taking, personal knowledge management (PKM), and an open-source Notion alternative. Based on Karpathy's LLM Wiki pattern.
This report summarizes scanner evidence for AgriciDaniel/claude-obsidian, a Python project with 15,465 GitHub stars. SynthScan examined 53,295 lines of code across 165 source files, recording 824 pattern matches distributed across 14 syntactic categories. The legacy adjusted pattern signal is 20.1 (Elevated pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 653 finding(s).
Are imports declared and package/API names verified? 55 finding(s).
Are wrappers, guards, and repeated structures necessary? 684 finding(s).
Which changes need focused human review? 824 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 824 distinct pattern matches across 14 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/test_contracts.py | 68 | def test_capability_and_transaction_operation_types_cannot_drift(self) -> None: | CODE |
| LOW⚡ | tests/test_contracts.py | 71 | def test_canonical_contracts_validate(self) -> None: | CODE |
| LOW⚡ | tests/test_contracts.py | 74 | def test_manifest_covers_every_current_skill(self) -> None: | CODE |
| LOW⚡ | tests/test_contracts.py | 81 | def test_product_contract_locks_privacy_compatibility_and_release_authority( | CODE |
| LOW⚡ | tests/test_contracts.py | 285 | def test_missing_implementation_path_is_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_contracts.py | 292 | def test_unregistered_skill_is_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_contracts.py | 301 | def test_inconsistent_mutation_and_egress_declarations_are_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_contracts.py | 409 | def test_missing_optional_configuration_is_available(self) -> None: | CODE |
| LOW⚡ | tests/test_contracts.py | 414 | def test_partial_configuration_is_degraded(self) -> None: | CODE |
| LOW⚡ | tests/test_contracts.py | 422 | def test_complete_configuration_is_configured(self) -> None: | CODE |
| LOW⚡ | tests/test_contracts.py | 428 | def test_successful_execution_is_verified(self) -> None: | CODE |
| LOW⚡ | tests/test_contracts.py | 434 | def test_failed_execution_is_degraded(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 105 | def test_evaluation_is_deterministic_and_has_no_absolute_roots(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 113 | def test_canonical_core_capabilities_report_only_behavioral_verification( | CODE |
| LOW | tests/test_contracts.py | 148 | def test_canonical_verifiers_are_behavioral_or_explain_their_absence(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 168 | def test_wiki_cli_and_lint_scopes_are_least_privilege(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 211 | def test_verified_wiki_contract_covers_every_init_target(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 244 | def test_cli_check_only_and_unknown_capability(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 324 | def test_absolute_tracked_paths_are_rejected(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 335 | def test_empty_verifier_requires_an_explicit_reason(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 340 | def test_self_and_schema_only_verifiers_are_rejected(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 379 | def test_malformed_json_returns_structured_errors(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 385 | def test_duplicate_contract_keys_return_structured_errors(self) -> None: | CODE |
| LOW | tests/test_contracts.py | 445 | def test_missing_automated_verifier_stays_configured_with_reason(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 355 | def test_tracked_symlink_is_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 365 | def test_root_raw_and_live_vault_state_are_hard_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 375 | def test_secret_token_is_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 385 | def test_private_repo_url_needs_named_disclosure(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 395 | def test_absolute_home_path_is_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 403 | def test_personal_email_address_is_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 413 | def test_quoted_unicode_literal_and_encoded_email_forms_are_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 459 | def test_unreviewed_binary_is_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 466 | def test_missing_config_and_non_zip_output_fail_closed(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 476 | def test_failed_build_does_not_replace_existing_artifact(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 483 | def test_collected_bytes_must_match_the_clean_git_blob(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 651 | def test_traversal_entry_is_rejected_without_extraction(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 659 | def test_archive_symlink_and_special_entry_are_rejected(self) -> None: | CODE |
| LOW⚡ | tests/test_release.py | 669 | def test_compressed_bomb_is_rejected_before_expansion(self) -> None: | CODE |
| LOW | tests/test_release.py | 66 | def test_public_policy_never_selects_root_contributor_vault_state(self) -> None: | CODE |
| LOW | tests/test_release.py | 170 | def test_release_authority_json_rejects_duplicate_keys(self) -> None: | CODE |
| LOW | tests/test_release.py | 184 | def test_release_authority_json_rejects_nonfinite_numbers(self) -> None: | CODE |
| LOW | tests/test_release.py | 200 | def test_release_authority_rejects_unknown_keys(self) -> None: | CODE |
| LOW | tests/test_release.py | 215 | def test_portable_unicode_path_collision_is_rejected_before_build(self) -> None: | CODE |
| LOW | tests/test_release.py | 232 | def test_two_builds_are_byte_identical_and_self_auditing(self) -> None: | CODE |
| LOW | tests/test_release.py | 267 | def test_public_marketplace_is_injected_only_into_the_audited_artifact( | CODE |
| LOW | tests/test_release.py | 341 | def test_dirty_and_untracked_worktrees_are_rejected_without_output(self) -> None: | CODE |
| LOW | tests/test_release.py | 427 | def test_release_safe_email_addresses_are_accepted(self) -> None: | CODE |
| LOW | tests/test_release.py | 438 | def test_non_email_at_sign_forms_are_accepted(self) -> None: | CODE |
| LOW | tests/test_release.py | 448 | def test_release_safe_domains_require_an_exact_match(self) -> None: | CODE |
| LOW | tests/test_release.py | 501 | def test_git_environment_cannot_redirect_release_provenance(self) -> None: | CODE |
| LOW | tests/test_release.py | 542 | def test_build_does_not_refresh_or_mutate_the_real_git_index(self) -> None: | CODE |
| LOW | tests/test_release.py | 575 | def test_release_version_and_date_surfaces_are_coordinated(self) -> None: | CODE |
| LOW | tests/test_release.py | 612 | def test_repository_allowlist_is_canonical_and_valid(self) -> None: | CODE |
| LOW | tests/test_release.py | 681 | def test_live_state_and_secret_filename_are_rejected(self) -> None: | CODE |
| LOW | tests/test_release.py | 699 | def test_archive_with_personal_email_address_is_rejected(self) -> None: | CODE |
| LOW | tests/test_release.py | 716 | def test_tampered_valid_artifact_fails_manifest_and_checksums(self) -> None: | CODE |
| LOW | tests/test_release.py | 734 | def test_mode_tampering_is_rejected_against_manifest(self) -> None: | CODE |
| LOW | tests/test_release.py | 750 | def test_local_header_timestamp_must_match_central_directory(self) -> None: | CODE |
| LOW | tests/test_release.py | 770 | def test_alternate_valid_zip_header_encoding_is_noncanonical(self) -> None: | CODE |
| LOW | tests/test_release.py | 794 | def test_audit_snapshot_rejects_artifact_path_replacement(self) -> None: | CODE |
| 508 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/test_concurrent_write.sh | 56 | # ── Worker function: acquire lock, append, release ────────────────────────── | COMMENT |
| MEDIUM | tests/test_concurrent_write.sh | 81 | # ── Spawn workers in parallel ─────────────────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_concurrent_write.sh | 100 | # ── Verify: file has seed + exactly N tagged lines ────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_concurrent_write.sh | 117 | # ── Verify: no orphaned lockfiles ─────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_concurrent_write.sh | 121 | # ── Verify: clear-stale reports 0 (nothing to reap) ───────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_concurrent_write.sh | 125 | # ── Verify: file content sanity (no truncated/garbled lines) ──────────────── | COMMENT |
| MEDIUM⚡ | tests/test_contextual_prefix.py | 53 | # ─── Below the floor → no cache_control (silent no-op avoided) ─────────────── | COMMENT |
| MEDIUM⚡ | tests/test_contextual_prefix.py | 63 | # ─── At / above the floor → ephemeral cache_control ────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 77 | # ── acquire on a fresh path returns 0 ──────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 81 | # ── second acquire while the lock is fresh returns 75 ──────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 88 | # ── peek shows the lock ────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 95 | # ── list shows the held lock ───────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 102 | # ── release frees the lock (cross-process release is allowed by design) ───── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 107 | # ── re-acquire after release succeeds ─────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 112 | # ── short --stale-after-sec lets us test age-based reap quickly ───────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 146 | # ── peek on unheld path ────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 150 | # ── path validation: absolute path rejected ────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 154 | # ── path validation: traversal rejected ────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 158 | # ── path validation: empty rejected ────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 162 | # ── path validation: newline rejected (v1.7.2; closes audit M4) ────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 168 | # ── path validation: carriage return rejected (v1.7.2; closes audit M4) ────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_lock.sh | 172 | # ── numeric inputs are validated before Bash arithmetic ────────────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_lock.sh | 306 | # ── stress: 10 unique paths all acquire cleanly ────────────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_lock.sh | 326 | # ── summary ────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_bm25_index.py | 60 | # ─── tokenize() ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_bm25_index.py | 190 | # ─── build_index + query() ─────────────────────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_bm25_index.py | 856 | # ─── CLI smoke test ────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_mode.py | 203 | # ─── Zettel ID format ─────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_mode.py | 479 | # ─── Invalid content type raises ─────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_wiki_mode.py | 489 | # ─── CLI subprocess: `wiki-mode.py get` returns mode string ───────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 51 | # ─── Default-to-generic when no config file ────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 68 | # ─── Existing config load ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 100 | # ─── Existing corrupt mode.json fails closed ──────────────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 119 | # ─── Mode=generic routing matches v1.7 conventions ────────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 145 | # ─── Mode=lyt routing: all atomic notes flat under wiki/notes/ ────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 157 | # ─── Mode=para routing: actionability-based folders ───────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 187 | # ─── Mode=zettelkasten routing: flat, timestamp-prefixed ──────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 249 | # ─── Slugify handles unicode + special chars ──────────────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 408 | # ─── CLI --mode preview override (v1.8.2) ─────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 532 | # ─── CLI subprocess: `wiki-mode.py id` returns a sortable nonce ID ─────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 552 | # ─── CLI subprocess: `wiki-mode.py route source NAME` returns a path ──────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 579 | # ─── CLI subprocess: invalid mode rejected ────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 594 | # ─── CLI subprocess: templates listing returns all 6 ─────────────────────── | COMMENT |
| MEDIUM | tests/test_wiki_mode.py | 610 | # ─── Single-source page vocabulary (claude_obsidian.page_schema) ───────────── | COMMENT |
| MEDIUM⚡ | tests/test_retrieve.py | 81 | # ─── import_sibling ────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_retrieve.py | 89 | # ─── chunk_snippet ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_retrieve.py | 104 | # ─── rerank.cosine() ───────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_retrieve.py | 238 | # ─── rerank.rerank() no-op fallback ────────────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_retrieve.py | 379 | # ─── Nomic asymmetric task prefixes and cache scheme ────────────────────── | COMMENT |
| MEDIUM | tests/test_retrieve.py | 620 | # ─── retrieve.py CLI: exit 10 when not provisioned ──────────────────────────── | COMMENT |
| MEDIUM | tests/test_retrieve.py | 820 | # ─── Integration smoke test: end-to-end with synthetic data ────────────────── | COMMENT |
| MEDIUM | tests/test_retrieve.py | 1109 | # ─── M8 closure: --explain and --no-rerank flag coverage ───────────────────── | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/test_transaction.py | 120 | except RuntimeError: | CODE |
| HIGH | tests/test_transaction.py | 1127 | except Exception: | CODE |
| HIGH | tests/test_transaction.py | 441 | except RuntimeError: | CODE |
| HIGH | tests/test_vault_ops.py | 99 | except LedgerValidationError: | CODE |
| HIGH | tests/test_windows_compat.py | 241 | except NotADirectoryError: | CODE |
| HIGH | tests/test_ledgers.py | 675 | except LedgerValidationError: | CODE |
| HIGH | tests/test_bm25_index.py | 824 | except (OSError, bm25.TransactionError): | CODE |
| HIGH | tests/test_wiki_mode.py | 394 | except ValueError: | CODE |
| HIGH | tests/test_wiki_mode.py | 401 | except ValueError: | CODE |
| HIGH | claude_obsidian/release.py | 2552 | except FileNotFoundError: | CODE |
| HIGH | claude_obsidian/release.py | 2570 | except FileNotFoundError: | CODE |
| HIGH | claude_obsidian/checkpoint.py | 497 | except FileNotFoundError: | CODE |
| HIGH | claude_obsidian/legacy_lock.py | 526 | except FileNotFoundError: | CODE |
| HIGH | claude_obsidian/transaction.py | 1516 | except (ImportError, AttributeError, OSError, TypeError, ValueError): | CODE |
| HIGH | claude_obsidian/transaction.py | 1630 | except FileNotFoundError: | CODE |
| HIGH | claude_obsidian/transaction.py | 1634 | except OSError: | CODE |
| HIGH | claude_obsidian/transaction.py | 1641 | except OSError: | CODE |
| HIGH | claude_obsidian/transaction.py | 2396 | except OSError: | CODE |
| HIGH | claude_obsidian/transaction.py | 886 | except FileNotFoundError: | CODE |
| HIGH | claude_obsidian/transaction.py | 1619 | except FileNotFoundError: | CODE |
| HIGH | claude_obsidian/transaction.py | 2101 | except FileExistsError: | CODE |
| HIGH | claude_obsidian/transaction.py | 2304 | except FileNotFoundError: | CODE |
| HIGH | claude_obsidian/transaction.py | 378 | except FileExistsError: | CODE |
| HIGH | claude_obsidian/transaction.py | 1775 | except OSError: | CODE |
| HIGH | claude_obsidian/transaction.py | 2099 | except OSError: | CODE |
| HIGH | claude_obsidian/transaction.py | 1475 | except FileExistsError: | CODE |
| HIGH | claude_obsidian/transaction.py | 1473 | except OSError: | CODE |
| HIGH | claude_obsidian/transaction.py | 1931 | except OSError: | CODE |
| HIGH | claude_obsidian/transaction.py | 2021 | except (OSError, _LockIdentityChanged): | CODE |
| HIGH | claude_obsidian/transaction.py | 1977 | except OSError: | CODE |
| HIGH | claude_obsidian/capture.py | 387 | except FileNotFoundError: | CODE |
| HIGH | claude_obsidian/capture.py | 1880 | except ( | CODE |
| HIGH | claude_obsidian/capture.py | 1568 | except OSError: | CODE |
| HIGH | claude_obsidian/capture.py | 1657 | except (OSError, _LockIdentityChanged): | CODE |
| HIGH | claude_obsidian/capture.py | 757 | except (KeyError, OSError, zipfile.BadZipFile): | CODE |
| HIGH | claude_obsidian/capture.py | 762 | except UnicodeDecodeError: | CODE |
| HIGH | claude_obsidian/capture.py | 1612 | except OSError: | CODE |
| HIGH | claude_obsidian/cli.py | 1258 | except (OSError, ValueError): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_transaction.py | 2114 | CODE | |
| LOW | tests/test_transaction.py | 2225 | CODE | |
| LOW | tests/test_transaction.py | 2561 | CODE | |
| LOW | tests/test_transaction.py | 2133 | CODE | |
| LOW | tests/test_windows_compat.py | 230 | CODE | |
| LOW | tests/test_contextual_prefix.py | 684 | CODE | |
| LOW | tests/test_contextual_prefix.py | 753 | CODE | |
| LOW | tests/test_lint_engine.py | 284 | CODE | |
| LOW | tests/test_ledgers.py | 321 | CODE | |
| LOW | tests/test_ledgers.py | 532 | CODE | |
| LOW | tests/test_setup_vault.py | 69 | CODE | |
| LOW | tests/test_bm25_index.py | 684 | CODE | |
| LOW | tests/test_bm25_index.py | 748 | CODE | |
| LOW | tests/test_bm25_index.py | 877 | CODE | |
| LOW | tests/test_capture.py | 1068 | CODE | |
| LOW | tests/test_retrieve.py | 579 | CODE | |
| LOW | tests/test_checkpoint.py | 169 | CODE | |
| LOW | tests/test_checkpoint.py | 531 | CODE | |
| LOW | tests/test_installed_tree_boundary.py | 58 | CODE | |
| LOW | claude_obsidian/release.py | 456 | CODE | |
| LOW | claude_obsidian/release.py | 837 | CODE | |
| LOW | claude_obsidian/release.py | 1310 | CODE | |
| LOW | claude_obsidian/release.py | 1336 | CODE | |
| LOW | claude_obsidian/release.py | 1984 | CODE | |
| LOW | claude_obsidian/checkpoint.py | 1190 | CODE | |
| LOW | claude_obsidian/checkpoint.py | 513 | CODE | |
| LOW | claude_obsidian/paths.py | 361 | CODE | |
| LOW | claude_obsidian/legacy_lock.py | 119 | CODE | |
| LOW | claude_obsidian/legacy_lock.py | 293 | CODE | |
| LOW | claude_obsidian/legacy_lock.py | 570 | CODE | |
| LOW | claude_obsidian/transaction.py | 419 | CODE | |
| LOW | claude_obsidian/transaction.py | 471 | CODE | |
| LOW | claude_obsidian/transaction.py | 727 | CODE | |
| LOW | claude_obsidian/transaction.py | 1051 | CODE | |
| LOW | claude_obsidian/transaction.py | 1450 | CODE | |
| LOW | claude_obsidian/transaction.py | 2128 | CODE | |
| LOW | claude_obsidian/transaction.py | 2329 | CODE | |
| LOW | claude_obsidian/transaction.py | 2875 | CODE | |
| LOW | claude_obsidian/transaction.py | 4054 | CODE | |
| LOW | claude_obsidian/transaction.py | 4215 | CODE | |
| LOW | claude_obsidian/transaction.py | 4465 | CODE | |
| LOW | claude_obsidian/transaction.py | 1820 | CODE | |
| LOW | claude_obsidian/package_validation.py | 253 | CODE | |
| LOW | claude_obsidian/package_validation.py | 347 | CODE | |
| LOW | claude_obsidian/gitignore.py | 42 | CODE | |
| LOW | claude_obsidian/gitignore.py | 72 | CODE | |
| LOW | claude_obsidian/capture.py | 586 | CODE | |
| LOW | claude_obsidian/capture.py | 639 | CODE | |
| LOW | claude_obsidian/capture.py | 721 | CODE | |
| LOW | claude_obsidian/capture.py | 822 | CODE | |
| LOW | claude_obsidian/capture.py | 909 | CODE | |
| LOW | claude_obsidian/capture.py | 1519 | CODE | |
| LOW | claude_obsidian/hook_adapter.py | 218 | CODE | |
| LOW | claude_obsidian/ledgers.py | 274 | CODE | |
| LOW | claude_obsidian/ledgers.py | 346 | CODE | |
| LOW | claude_obsidian/ledgers.py | 467 | CODE | |
| LOW | claude_obsidian/ledgers.py | 901 | CODE | |
| LOW | claude_obsidian/contracts.py | 320 | CODE | |
| LOW | claude_obsidian/contracts.py | 620 | CODE | |
| LOW | claude_obsidian/contracts.py | 1203 | CODE | |
| 11 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_contracts.py | 4 | CODE | |
| LOW | tests/test_release.py | 4 | CODE | |
| LOW | tests/test_transaction.py | 4 | CODE | |
| LOW | tests/test_legacy_lock.py | 4 | CODE | |
| LOW | tests/test_setup_wrappers.py | 4 | CODE | |
| LOW | tests/test_distribution_vaults.py | 4 | CODE | |
| LOW | tests/test_setup_retrieve.py | 4 | CODE | |
| LOW | tests/test_vault_root_separation.py | 4 | CODE | |
| LOW | tests/test_vault_ops.py | 3 | CODE | |
| LOW | tests/test_gitignore.py | 4 | CODE | |
| LOW | tests/test_setup_multi_agent.py | 4 | CODE | |
| LOW | tests/test_extensions.py | 4 | CODE | |
| LOW | tests/test_windows_compat.py | 15 | CODE | |
| LOW | tests/test_knowledge_contracts.py | 4 | CODE | |
| LOW | tests/test_cli_mode.py | 4 | CODE | |
| LOW | tests/test_lint_engine.py | 4 | CODE | |
| LOW | tests/test_cli_approval.py | 4 | CODE | |
| LOW | tests/test_ledgers.py | 4 | CODE | |
| LOW | tests/test_setup_vault.py | 4 | CODE | |
| LOW | tests/test_hooks.py | 4 | CODE | |
| LOW | tests/test_package_validation.py | 4 | CODE | |
| LOW | tests/test_capture.py | 4 | CODE | |
| LOW | tests/test_paths.py | 4 | CODE | |
| LOW | tests/test_benchmark_tools.py | 4 | CODE | |
| LOW | tests/test_detect_transport.py | 4 | CODE | |
| LOW | tests/test_gates.py | 4 | CODE | |
| LOW | tests/test_checkpoint.py | 3 | CODE | |
| LOW | tests/test_installed_tree_boundary.py | 4 | CODE | |
| LOW | claude_obsidian/release.py | 9 | CODE | |
| LOW | claude_obsidian/checkpoint.py | 3 | CODE | |
| LOW | claude_obsidian/paths.py | 3 | CODE | |
| LOW | claude_obsidian/url_safety.py | 3 | CODE | |
| LOW | claude_obsidian/legacy_lock.py | 10 | CODE | |
| LOW | claude_obsidian/transaction.py | 9 | CODE | |
| LOW | claude_obsidian/__init__.py | 8 | CODE | |
| LOW | claude_obsidian/gates.py | 3 | CODE | |
| LOW | claude_obsidian/package_validation.py | 10 | CODE | |
| LOW | claude_obsidian/extensions.py | 3 | CODE | |
| LOW | claude_obsidian/gitignore.py | 26 | CODE | |
| LOW | claude_obsidian/capture.py | 8 | CODE | |
| LOW | claude_obsidian/vault_ops.py | 3 | CODE | |
| LOW | claude_obsidian/cli.py | 3 | CODE | |
| LOW | claude_obsidian/hook_adapter.py | 3 | CODE | |
| LOW | claude_obsidian/ledgers.py | 3 | CODE | |
| LOW | claude_obsidian/contracts.py | 8 | CODE | |
| LOW | claude_obsidian/lint_engine.py | 37 | CODE | |
| LOW | claude_obsidian/json_utils.py | 3 | CODE | |
| LOW | claude_obsidian/mode_config.py | 3 | CODE | |
| LOW | claude_obsidian/__main__.py | 1 | CODE | |
| LOW | claude_obsidian/page_schema.py | 29 | CODE | |
| LOW | scripts/claude-obsidian.py | 4 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_transaction.py | 1127 | except Exception: | CODE |
| LOW | tests/test_transaction.py | 1820 | except Exception as exc: | CODE |
| LOW | claude_obsidian/transaction.py | 2018 | except Exception: | CODE |
| LOW | claude_obsidian/transaction.py | 4108 | except Exception as exc: # recovery must aggregate every path failure | CODE |
| LOW | claude_obsidian/capture.py | 1654 | except Exception as exc: | CODE |
| LOW | claude_obsidian/ledgers.py | 572 | except Exception as exc: | CODE |
| LOW | claude_obsidian/ledgers.py | 675 | except Exception as exc: | CODE |
| LOW | claude_obsidian/ledgers.py | 730 | except Exception as exc: | CODE |
| LOW | claude_obsidian/ledgers.py | 997 | except Exception as exc: | CODE |
| LOW | claude_obsidian/ledgers.py | 1231 | except Exception as exc: | CODE |
| LOW | claude_obsidian/lint_engine.py | 698 | except Exception as exc: | CODE |
| LOW | claude_obsidian/lint_engine.py | 717 | except Exception as exc: | CODE |
| LOW | claude_obsidian/lint_engine.py | 875 | except Exception as exc: | CODE |
| MEDIUM | claude_obsidian/lint_engine.py | 850 | def _transaction_reader() -> Any: | CODE |
| LOW | scripts/rerank.py | 429 | except Exception as e: | CODE |
| LOW | scripts/rerank.py | 456 | except Exception as e: | CODE |
| LOW | scripts/tiling-check.py | 407 | except Exception as exc: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | scripts/rerank.py | 0 | parse a positive, bounded result count for command-line callers. | STRING |
| HIGH | scripts/retrieve.py | 0 | parse a positive, bounded result count for command-line callers. | STRING |
| HIGH | scripts/bm25-index.py | 0 | parse a positive, bounded result count for command-line callers. | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_concurrent_write.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | tests/test_wiki_lock.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | tests/test_setup_vault.py | 121 | result = _run("--check", "--vault", str(vault)) | COMMENT |
| LOW | claude_obsidian/page_schema.py | 61 | # vocabulary rejects. Subtraction makes ROUTABLE_TYPES ⊆ PAGE_TYPES structurally | COMMENT |
| LOW | scripts/detect-transport.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | scripts/allocate-address.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | scripts/setup-vault.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | scripts/bm25-index.py | 121 | # Small high-frequency-stopword list (English). Conservative — keep recall high. | COMMENT |
| LOW | scripts/bm25-index.py | 161 | EXIT_LOCK = 1 | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/test_contracts.py | 58 | def update_capability(self, **changes: object) -> None: | CODE |
| LOW | claude_obsidian/release.py | 2591 | __all__ = ["audit_artifact", "build_public_artifact"] | CODE |
| LOW | claude_obsidian/url_safety.py | 94 | __all__ = ["URLSafetyIssue", "url_credential_issue"] | CODE |
| LOW | claude_obsidian/capture.py | 2234 | __all__ = [ | CODE |
| LOW | claude_obsidian/mode_config.py | 83 | __all__ = [ | CODE |
| LOW | claude_obsidian/page_schema.py | 119 | __all__ = [ | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | skills/wiki/SKILL.md | 6 | # Wiki orchestration | COMMENT |
| MEDIUM | skills/wiki/references/modes.md | 1 | # Domain-specific scaffold profiles | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/test_transaction.py | 297 | '"expected_hashes":{"wiki/A.md":null},' | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | claude_obsidian/release.py | 127 | "your-api-key", | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/detect-transport.sh | 8 | # Usage: | COMMENT |
| LOW | scripts/allocate-address.sh | 9 | # Usage: | COMMENT |
| LOW | scripts/setup-vault.sh | 8 | # Usage: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | claude_obsidian/release.py | 123 | "placeholder", | CODE |
| LOW | claude_obsidian/release.py | 396 | word in captured for word in ("example", "placeholder", "redacted", "your-") | CODE |