# Weak assertion ratchet baseline (Layer 2 prep - informational, not yet CI-enforced)
#
# METHODOLOGY NOTE (2026-06-12, conscious re-pin — AST counter supersedes grep):
#
# Counter history:
#   grep (original, 2026-06-11)                  : 1230
#   grep (post batch-13 PR #584)                  : 1180
#   AST  (this PR, measured on same HEAD as #584) : 1083
#
# Why AST < grep (~97 fewer false positives removed by the AST approach):
#   1. Multi-line asserts where ">=" appears on a continuation line were
#      invisible to grep (it saw only the line with "assert", not the line
#      with ">="). These are now caught — but many were already exact (==)
#      chained compares or had exemption markers, so net new detections < 97.
#   2. Lines containing "==" elsewhere on the same assert were skipped by
#      the grep rule (any == → skip), but the AST checker skips only if
#      the assert *test expression* contains an Eq compare. Some asserts had
#      "==" in a different sub-expression and were counted by grep but not
#      by AST.
#   3. String literals containing ">=" (e.g. version specs) are naturally
#      excluded by AST parsing; grep had a partial workaround that missed
#      some cases.
#
# The AST count is the authoritative baseline going forward.
# The count must only decrease (or stay equal). Each Layer-2 batch PR
# re-measures and lowers it. Numbers are MEASURED, never hand-derived.
#
# History:
#   batch-14 (AST): 1083 -> 1026, 2026-06-12 (56 pins in top-4 AST-ranked
#   files; pre-batch measure on base ca8e9927 was 1082 — develop had
#   already dropped 1 since the 1083 recording).
#   batch-15 (AST): 1026 -> 977, 2026-06-13 (49 pins in top-4 AST-ranked
#   files).
#   batch-16 (AST): 977 -> 933, 2026-06-13 (44 pins in top-4 AST-ranked
#   files).
#   batch-17 (AST): 933 -> 892, 2026-06-13 (41 flagged pins in top-4
#   AST-ranked files, plus 2 unflagged <= bounds pinned exact).
#   batch-18 (AST): 892 -> 852, 2026-06-13 (40 pins in top-4 AST-ranked
#   files).
#   batch-19 (AST): 852 -> 816, 2026-06-13 (35 exact pins + 1 timing
#   exemption in top-4 AST-ranked files).
#   batch-20 (AST): 816 -> 780, 2026-06-13 (36 pins in top-4 AST-ranked
#   files).
#   batch-21 (AST): 780 -> 744, 2026-06-13 (36 pins in top-4 AST-ranked
#   files).
#   batch-22 (AST): 744 -> 711, 2026-06-13 (30 exact pins + 3 timing
#   exemptions in top-4 AST-ranked files).
#   batch-23 (AST): 711 -> 679, 2026-06-13 (32 exact pins in top-4
#   AST-ranked files, no exemptions).
#   batch-24 (AST): 679 -> 647, 2026-06-13 (32 exact pins in top-4
#   AST-ranked files, no exemptions).
#   batch-25 (AST): 647 -> 619, 2026-06-13 (28 exact pins in top-4
#   AST-ranked files, no exemptions).
#   batch-26 (AST): 619 -> 591, 2026-06-13 (28 exact pins in top-4
#   AST-ranked files, no exemptions).
#   batch-27 (AST): 591 -> 567, 2026-06-13 (14 exact pins + 10 timing/environment/hypothesis
#   exemptions in top-4 AST-ranked files; property files excluded from baseline).
#   batch-28 (AST): 567 -> 543, 2026-06-13 (24 exact pins in top-4 AST-ranked
#   files, no exemptions).
#   batch-29 (AST): 543 -> 519, 2026-06-13 (23 exact pins + 1 timing exemption
#   in top-4 AST-ranked files).
#   batch-30 (AST): 519 -> 499, 2026-06-13 (20 exact pins in top-4 AST-ranked
#   files, no exemptions).
#   batch-31 (AST): 499 -> 479, 2026-06-13 (14 exact pins + 3 truthy conversions
#   + 3 timing exemptions in top-4 AST-ranked files; ran parallel to batch-30,
#   reconciled on merge — both batches' 40 pins now in develop).
#   batch-32 (AST): 479 -> 459, 2026-06-13 (18 exact pins + 2 truthy-set pins
#   in top-4 AST-ranked files, no timing exemptions).
#   batch-33 (AST): 459 -> 439, 2026-06-13 (20 exact pins in top-4 AST-ranked
#   files, no exemptions; loop assert restructured to per-pattern dict).
#   batch-34 (AST): 439 -> 434, 2026-06-13 (5 exact pins in test_logging.py;
#   batch deliberately small — the next-ranked files were poor targets this
#   round: integration_tests.py has 6 pre-existing failures (table_output
#   missing from CSV TOON, filed separately) so its asserts are unreachable,
#   and the CLI-output files need content-invariant pins, not byte counts that
#   vary by platform temp-stem length — both deferred to batch-35).
#   batch-35 (AST): 434 -> 418, 2026-06-13 (15 exact pins + 1 nondeterministic
#   exemption in 4 files: test_parser_readiness_records.py/test_language_detector_helpers.py/
#   test_parser.py/test_queries_ruby.py; loop assert in test_key_languages_have_patterns
#   restructured to per-language exact dict; cache_maxsize pinned == 2000 (the
#   deterministic CI default; env override is out-of-test); only mtime_ns exempt
#   as a genuinely nondeterministic filesystem timestamp).
#   batch-36 (AST): 418 -> 402, 2026-06-13 (16 pins in 4 files:
#   test_query_library_coverage.py (list_queries==71 x2; in-loop description
#   non-empty rewritten as !='' x2), test_csv_control_char_safety.py
#   (parametrized result len rewritten as !=''; html_csv ==93; markdown_csv
#   ==69; CR rows ==2), test_csv_control_char_safety_remaining.py (parametrized
#   result len rewritten as !=''; base_formatter rows ==3; legacy rows ==4),
#   test_legacy_table_formatter_core.py (CSV lines ==1; rows ==2 x3);
#   no exemptions).
#   batch-37 (AST): 402 -> 374, 2026-06-15 (27 exact pins + 1 nondeterministic
#   exemption in 7 files: test_csharp_plugin_integration.py (classes==5/
#   functions==12/imports==3/len(code)==2650), test_csharp_plugin_extraction.py
#   (constructor_names==0/param_names==1/constructors==1/complexity==7),
#   test_csharp_plugin_elements.py (static_imports==1/import start_lines==[2,3]/
#   complexity==7/nodes==98), test_swift_plugin.py (classes==4/functions==5/
#   variables==3/node_count==207), test_python_plugin_edge_cases.py (imports==0
#   x2/concurrent_cache exempt-nondeterministic/complexity loop restructured to
#   per-case dict), test_plugins_base_core.py (functions==0/unicode_len==2/
#   node_name_len==4/traverse_functions==1), test_typescript_formatter_core.py
#   (full==879/compact==416/csv==402/csv_lines==8)).
#   batch-39 (AST): further pins in formatter/CLI/security tests
#   (test_cli_table_partial_query.py, test_cli_queries.py,
#   test_temporal_activation.py, test_file_health_blocks.py,
#   test_codegraph_metrics_tool.py, test_change_impact_analysis.py,
#   test_cached_call_graph.py, test_mcp_security.py, test_error_handler.py,
#   test_safe_to_edit_tool.py, test_project_health_tool.py).
#   batch-39 (AST): 337 -> 302, 2026-06-15 (35 exact pins in 11 files:
#   test_cli_table_partial_query.py, test_cli_queries.py,
#   test_temporal_activation.py, test_file_health_blocks.py,
#   test_codegraph_metrics_tool.py, test_change_impact_analysis.py,
#   test_cached_call_graph.py, test_mcp_security.py, test_error_handler.py,
#   test_safe_to_edit_tool.py, test_project_health_tool.py).
#
#   weak-assertion moat expansion: 302 -> 662, 2026-06-20. The ratchet now
#   also counts unpaired `assert x is not None` placeholders and
#   `assert x is not None or x is None` tautologies. This is a scope expansion,
#   not a regression in fixed loose-bound work.
#
#   cleanup batch: 662 -> 612, 2026-06-20. Replaced all 15 AST tautologies
#   and 35 high-confidence placeholder export/pipeline smoke assertions with
#   concrete behavior, type, callable, logger, or envelope assertions.
#
#   cleanup batch: 612 -> 592, 2026-06-20. Replaced MCP server initialization
#   placeholder assertions with tool-definition, resource-info, callable, and
#   metadata-prefix contracts. Avoided introducing loose version length checks.
#
#   cleanup batch: 592 -> 566, 2026-06-20. Replaced remaining
#   `tests/unit/core/test_engine.py` placeholder component/result assertions
#   with component-contract helpers and `AnalysisResult` assertions.
#
#   cleanup batch: 566 -> 494, 2026-06-20. Parallel cleanup of MCP integration,
#   core manager/conftest query, utils/logging, regex security, HTML/Ruby
#   language-plugin, formatter registry, and checker self-test placeholder
#   assertions. Replaced existence guards with exact metadata, schema, type,
#   attribute, superclass, logger, and formatter contracts.
#
#   cleanup batch: 494 -> 476, 2026-06-20. Replaced CLI/MCP wiring,
#   tree-sitter integration, and CLI query-filter smoke assertions with exact
#   tool/resource classes, cross-language query counts, query result line pins,
#   available-query sentinels, and filtered Java method-name lists.
#
#   cleanup batch: 476 -> 470, 2026-06-20. Replaced Kotlin plugin placeholder
#   checks with extractor/language types and exact AnalysisResult success,
#   element, import, and error-path contracts.
#
#   cleanup batch: 470 -> 445, 2026-06-20. Replaced formatter-registry,
#   core smoke, constructor grammar, Kotlin target, and QueryCommand weak
#   assertions with exact registry state, formatter classes, mapping values,
#   engine reset/cache behavior, language types, parser roots, class lists,
#   query-service calls, and output call counts.
#
#   cleanup batch: 445 -> 428, 2026-06-20. Replaced encoding pipeline,
#   MCP server branch, base-tool, and Rust formatter weak assertions with exact
#   encoding strings, file metrics, analysis-engine types, cache calls, resolver
#   types, JSON error text, CSV headers, and markdown title output.
#
#   recovery re-measure: 428 -> 171, 2026-06-20. After reverting corrupted
#   one-line test files to HEAD, the expanded AST ratchet's current working
#   tree baseline measures substantially lower than the stale document count.
#   This count is now the authoritative baseline for follow-up cleanup.
#
#   Measured category split:
#     placeholder: 130
#     general loose-bound: 30
#     tautology: 10
#     optional-dependency: 1

BASELINE_COUNT=171
MEASUREMENT_DATE=2026-06-20
MEASUREMENT_COMMAND="uv run python scripts/check_loose_assertions.py --baseline"
