diff --git a/.craftsmanship-baseline.json b/.craftsmanship-baseline.json new file mode 100644 index 00000000..7fe5c5f5 --- /dev/null +++ b/.craftsmanship-baseline.json @@ -0,0 +1,6816 @@ +{ + "$comment": "Generated by `python scripts/check_craftsmanship.py --write-baseline`. Do NOT add an entry by hand to silence a new violation \u2014 that is gaming the ratchet, not maintaining it (CLAUDE.md \u00a7 Code Style). Regenerate only after confirming every new entry is pre-existing debt, not something this PR just introduced. Every entry removed here must correspond to a violation actually fixed in the code.", + "generated": "2026-08-10", + "violations": [ + { + "file": "benchmarks/_repro.py", + "kind": "method-size", + "detail": "multi_run_stats" + }, + { + "file": "benchmarks/active_forgetting/run_benchmark.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/active_forgetting/run_benchmark.py", + "kind": "method-size", + "detail": "derive_thresholds" + }, + { + "file": "benchmarks/active_forgetting/run_benchmark.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/active_forgetting/run_benchmark.py", + "kind": "unsourced-constant", + "detail": "_P" + }, + { + "file": "benchmarks/beam/ablation.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/beam/ablation.py", + "kind": "method-size", + "detail": "_evaluate_with_params" + }, + { + "file": "benchmarks/beam/ablation.py", + "kind": "method-size", + "detail": "ablation_signal_weights" + }, + { + "file": "benchmarks/beam/ablation.py", + "kind": "unsourced-constant", + "detail": "_RECALL_AT_10_K" + }, + { + "file": "benchmarks/beam/data.py", + "kind": "method-size", + "detail": "extract_10m_chat" + }, + { + "file": "benchmarks/beam/data.py", + "kind": "method-size", + "detail": "extract_conversation_turns" + }, + { + "file": "benchmarks/beam/data.py", + "kind": "method-size", + "detail": "turns_to_memories" + }, + { + "file": "benchmarks/beam/run_benchmark.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/beam/run_benchmark.py", + "kind": "method-size", + "detail": "_current_stage_for_question" + }, + { + "file": "benchmarks/beam/run_benchmark.py", + "kind": "method-size", + "detail": "evaluate_retrieval" + }, + { + "file": "benchmarks/beam/run_benchmark.py", + "kind": "method-size", + "detail": "run_benchmark" + }, + { + "file": "benchmarks/beam/run_benchmark.py", + "kind": "unsourced-constant", + "detail": "_RECALL_AT_10_K" + }, + { + "file": "benchmarks/episodic/run_benchmark.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/episodic/run_benchmark.py", + "kind": "method-size", + "detail": "generate_qa_from_events" + }, + { + "file": "benchmarks/episodic/run_benchmark.py", + "kind": "method-size", + "detail": "run_benchmark" + }, + { + "file": "benchmarks/episodic/run_benchmark.py", + "kind": "unsourced-constant", + "detail": "_BIN_3_5_MAX_ITEMS" + }, + { + "file": "benchmarks/episodic/run_benchmark.py", + "kind": "unsourced-constant", + "detail": "_EVENT_FULL_FIELDS" + }, + { + "file": "benchmarks/evermembench/run_benchmark.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/evermembench/run_benchmark.py", + "kind": "method-size", + "detail": "evaluate_qa" + }, + { + "file": "benchmarks/evermembench/run_benchmark.py", + "kind": "method-size", + "detail": "run_benchmark" + }, + { + "file": "benchmarks/forgetting_curve/criteria.py", + "kind": "method-size", + "detail": "criterion_benna_fusi_sqrt_t" + }, + { + "file": "benchmarks/forgetting_curve/criteria.py", + "kind": "unsourced-constant", + "detail": "COLLAPSE_THRESHOLD" + }, + { + "file": "benchmarks/gate_precision/run_benchmark.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/gate_precision/run_benchmark.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/gate_precision/run_benchmark.py", + "kind": "unsourced-constant", + "detail": "MIN_CHARS" + }, + { + "file": "benchmarks/gate_precision/run_benchmark.py", + "kind": "unsourced-constant", + "detail": "PASS_BAND" + }, + { + "file": "benchmarks/gate_precision/run_benchmark.py", + "kind": "unsourced-constant", + "detail": "SEED_N" + }, + { + "file": "benchmarks/lib/_xb_drivers.py", + "kind": "method-size", + "detail": "_drive_locomo" + }, + { + "file": "benchmarks/lib/ablation_runner.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/ablation_runner.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/lib/blend_weight_sweep.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/blend_weight_sweep.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/lib/blend_weight_sweep.py", + "kind": "method-size", + "detail": "run_cell" + }, + { + "file": "benchmarks/lib/cross_benchmark_runner.py", + "kind": "method-size", + "detail": "_summary_md" + }, + { + "file": "benchmarks/lib/cross_benchmark_runner.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/lib/cross_benchmark_runner.py", + "kind": "unsourced-constant", + "detail": "PASS_RATIO" + }, + { + "file": "benchmarks/lib/db_setup.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/db_snapshot.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/decay_sweep_runner.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/decay_sweep_runner.py", + "kind": "method-size", + "detail": "analyze_curve" + }, + { + "file": "benchmarks/lib/decay_sweep_runner.py", + "kind": "method-size", + "detail": "run_sweep" + }, + { + "file": "benchmarks/lib/e2_subsample_runner.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/e2_subsample_runner.py", + "kind": "method-size", + "detail": "run_trial" + }, + { + "file": "benchmarks/lib/e2_subsample_runner.py", + "kind": "unsourced-constant", + "detail": "EMBEDDING_DIM" + }, + { + "file": "benchmarks/lib/e2_zipf_runner.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/e2_zipf_runner.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/lib/e2_zipf_runner.py", + "kind": "unsourced-constant", + "detail": "EMBEDDING_DIM" + }, + { + "file": "benchmarks/lib/fusion.py", + "kind": "unsourced-constant", + "detail": "_ACCEPTABLE_MAX_CHUNKS" + }, + { + "file": "benchmarks/lib/fusion.py", + "kind": "unsourced-constant", + "detail": "_CRITICAL_MAX_CHUNKS" + }, + { + "file": "benchmarks/lib/fusion.py", + "kind": "unsourced-constant", + "detail": "_DEGRADED_MAX_CHUNKS" + }, + { + "file": "benchmarks/lib/latency_runner.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/latency_runner.py", + "kind": "method-size", + "detail": "run_trial" + }, + { + "file": "benchmarks/lib/longitudinal_runner.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/longitudinal_runner.py", + "kind": "method-size", + "detail": "evaluate_buckets" + }, + { + "file": "benchmarks/lib/longitudinal_runner.py", + "kind": "method-size", + "detail": "insert_memories" + }, + { + "file": "benchmarks/lib/longitudinal_runner.py", + "kind": "method-size", + "detail": "run" + }, + { + "file": "benchmarks/lib/noise_floor.py", + "kind": "method-size", + "detail": "measure_noise_floor" + }, + { + "file": "benchmarks/lib/retriever.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/retriever.py", + "kind": "method-size", + "detail": "BenchmarkRetriever.retrieve" + }, + { + "file": "benchmarks/lib/retriever.py", + "kind": "method-size", + "detail": "BenchmarkRetriever.retrieve_multihop" + }, + { + "file": "benchmarks/lib/run_e1_v3_lme.py", + "kind": "method-size", + "detail": "_run_row" + }, + { + "file": "benchmarks/lib/run_e1_v3_lme.py", + "kind": "method-size", + "detail": "_write_summary" + }, + { + "file": "benchmarks/lib/run_e1_v3_lme.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/lib/run_e1_v3_locomo.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/lib/run_e1_v3_locomo.py", + "kind": "method-size", + "detail": "_run_row" + }, + { + "file": "benchmarks/lib/run_e1_v3_locomo.py", + "kind": "method-size", + "detail": "_write_summary" + }, + { + "file": "benchmarks/lib/run_e1_v3_locomo.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/lib/trust_factor_sweep.py", + "kind": "unsourced-constant", + "detail": "_DIM" + }, + { + "file": "benchmarks/lib/verification_report.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/llm_head_to_head/cortex_caller.py", + "kind": "unsourced-constant", + "detail": "CORTEX_MAX_RESULTS" + }, + { + "file": "benchmarks/llm_head_to_head/data_loader.py", + "kind": "method-size", + "detail": "iter_items" + }, + { + "file": "benchmarks/llm_head_to_head/data_loader.py", + "kind": "unsourced-constant", + "detail": "BOOTSTRAP_SEED" + }, + { + "file": "benchmarks/llm_head_to_head/data_loader.py", + "kind": "unsourced-constant", + "detail": "EXPECTED_ITEM_COUNT" + }, + { + "file": "benchmarks/llm_head_to_head/data_loader.py", + "kind": "unsourced-constant", + "detail": "JUDGE_SHUFFLE_BASE" + }, + { + "file": "benchmarks/llm_head_to_head/data_loader.py", + "kind": "unsourced-constant", + "detail": "SHUFFLE_SEED_BASE" + }, + { + "file": "benchmarks/llm_head_to_head/generator.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/llm_head_to_head/generator.py", + "kind": "method-size", + "detail": "_call_google" + }, + { + "file": "benchmarks/llm_head_to_head/generator.py", + "kind": "method-size", + "detail": "call_generator" + }, + { + "file": "benchmarks/llm_head_to_head/generator.py", + "kind": "unsourced-constant", + "detail": "MAX_JITTER_S" + }, + { + "file": "benchmarks/llm_head_to_head/generator.py", + "kind": "unsourced-constant", + "detail": "MAX_RETRIES" + }, + { + "file": "benchmarks/llm_head_to_head/judge.py", + "kind": "method-size", + "detail": "judge_item" + }, + { + "file": "benchmarks/llm_head_to_head/long_context_truncator.py", + "kind": "method-size", + "detail": "build_naive_long_context" + }, + { + "file": "benchmarks/llm_head_to_head/long_context_truncator.py", + "kind": "unsourced-constant", + "detail": "OUTPUT_HEADROOM_TOKENS" + }, + { + "file": "benchmarks/llm_head_to_head/manifest.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/llm_head_to_head/manifest.py", + "kind": "method-size", + "detail": "build_manifest" + }, + { + "file": "benchmarks/llm_head_to_head/orchestrator.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/llm_head_to_head/orchestrator.py", + "kind": "method-size", + "detail": "_generate_one_cell" + }, + { + "file": "benchmarks/llm_head_to_head/orchestrator.py", + "kind": "method-size", + "detail": "build_context" + }, + { + "file": "benchmarks/llm_head_to_head/orchestrator.py", + "kind": "method-size", + "detail": "estimate_run_cost" + }, + { + "file": "benchmarks/llm_head_to_head/orchestrator.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/llm_head_to_head/orchestrator.py", + "kind": "method-size", + "detail": "run_live" + }, + { + "file": "benchmarks/llm_head_to_head/pilot.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/llm_head_to_head/pilot.py", + "kind": "method-size", + "detail": "_build_dryrun_context" + }, + { + "file": "benchmarks/llm_head_to_head/pilot.py", + "kind": "method-size", + "detail": "dry_run" + }, + { + "file": "benchmarks/llm_head_to_head/pilot.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/llm_head_to_head/pilot.py", + "kind": "method-size", + "detail": "run_pilot_live" + }, + { + "file": "benchmarks/llm_head_to_head/pilot.py", + "kind": "unsourced-constant", + "detail": "SMOKE_COST_CEILING_USD" + }, + { + "file": "benchmarks/llm_head_to_head/retriever_baselines.py", + "kind": "method-size", + "detail": "standard_rag" + }, + { + "file": "benchmarks/llm_head_to_head/retriever_baselines.py", + "kind": "unsourced-constant", + "detail": "STANDARD_RAG_TOP_K" + }, + { + "file": "benchmarks/locomo/rrf_vs_tmm_ablation.py", + "kind": "method-size", + "detail": "eval_fusion" + }, + { + "file": "benchmarks/locomo/rrf_vs_tmm_ablation.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/locomo/run_benchmark.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/locomo/run_benchmark.py", + "kind": "method-size", + "detail": "evaluate_conversation" + }, + { + "file": "benchmarks/locomo/run_benchmark.py", + "kind": "method-size", + "detail": "run_benchmark" + }, + { + "file": "benchmarks/locomo/run_benchmark.py", + "kind": "unsourced-constant", + "detail": "_RECALL_AT_10_K" + }, + { + "file": "benchmarks/locomo/run_benchmark_agents.py", + "kind": "method-size", + "detail": "evaluate_conversation" + }, + { + "file": "benchmarks/locomo/run_benchmark_agents.py", + "kind": "method-size", + "detail": "run_benchmark" + }, + { + "file": "benchmarks/longmemeval/run_benchmark.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/longmemeval/run_benchmark.py", + "kind": "method-size", + "detail": "run_benchmark" + }, + { + "file": "benchmarks/memoryagentbench/run_benchmark.py", + "kind": "method-size", + "detail": "run_benchmark" + }, + { + "file": "benchmarks/spell_alteration/run_benchmark.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "benchmarks/spell_alteration/run_benchmark.py", + "kind": "method-size", + "detail": "run_benchmark" + }, + { + "file": "benchmarks/spell_alteration/run_benchmark.py", + "kind": "method-size", + "detail": "test_a_spot_the_fakes" + }, + { + "file": "benchmarks/spell_alteration/run_benchmark.py", + "kind": "method-size", + "detail": "test_b_compare_versions" + }, + { + "file": "benchmarks/spell_alteration/run_benchmark.py", + "kind": "unsourced-constant", + "detail": "CHUNK_SIZE" + }, + { + "file": "benchmarks/streaming_calibration/run.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/streaming_calibration/run.py", + "kind": "unsourced-constant", + "detail": "BATCHES_PER_SIZE" + }, + { + "file": "benchmarks/supersession_gate/guard_atomic_upperbound.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/supersession_gate/guard_atomic_upperbound.py", + "kind": "unsourced-constant", + "detail": "OVERLAP_MIN" + }, + { + "file": "benchmarks/supersession_gate/guard_chunk_granularity.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/supersession_gate/guard_chunk_granularity.py", + "kind": "unsourced-constant", + "detail": "OVERLAP_MIN" + }, + { + "file": "benchmarks/supersession_gate/guard_session_granularity.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "benchmarks/supersession_gate/guard_session_granularity.py", + "kind": "unsourced-constant", + "detail": "OVERLAP_MIN" + }, + { + "file": "benchmarks/tabular_170/measure.py", + "kind": "method-size", + "detail": "measure" + }, + { + "file": "benchmarks/tabular_170/measure.py", + "kind": "unsourced-constant", + "detail": "DECISION_THRESHOLD_PCT" + }, + { + "file": "benchmarks/tabular_170/measure.py", + "kind": "unsourced-constant", + "detail": "MAX_RESULTS" + }, + { + "file": "mcp_server/core/ablation.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/ablation.py", + "kind": "unsourced-constant", + "detail": "_CRITICAL_IMPACT_THRESHOLD" + }, + { + "file": "mcp_server/core/ablation.py", + "kind": "unsourced-constant", + "detail": "_MEANINGFUL_IMPACT_THRESHOLD" + }, + { + "file": "mcp_server/core/ablation.py", + "kind": "unsourced-constant", + "detail": "_NEGLIGIBLE_DELTA_MAGNITUDE" + }, + { + "file": "mcp_server/core/ablation_report.py", + "kind": "method-size", + "detail": "plan_full_ablation_study" + }, + { + "file": "mcp_server/core/ablation_report.py", + "kind": "unsourced-constant", + "detail": "_IMPORTANT_IMPACT_THRESHOLD" + }, + { + "file": "mcp_server/core/abstention_gate.py", + "kind": "layer-violation", + "detail": "cortex_beam_abstain" + }, + { + "file": "mcp_server/core/abstention_gate.py", + "kind": "layer-violation", + "detail": "pathlib" + }, + { + "file": "mcp_server/core/abstention_gate.py", + "kind": "method-size", + "detail": "filter_by_abstention" + }, + { + "file": "mcp_server/core/abstention_gate.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_THRESHOLD" + }, + { + "file": "mcp_server/core/active_forgetting.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/active_forgetting.py", + "kind": "unsourced-constant", + "detail": "ACUTE_RECENCY_WINDOW_HOURS" + }, + { + "file": "mcp_server/core/active_forgetting.py", + "kind": "unsourced-constant", + "detail": "PERMANENT_ACCUM_THRESHOLD" + }, + { + "file": "mcp_server/core/ast_extractors.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/ast_extractors.py", + "kind": "method-size", + "detail": "_extract_js_node" + }, + { + "file": "mcp_server/core/ast_extractors.py", + "kind": "method-size", + "detail": "_walk_for_calls" + }, + { + "file": "mcp_server/core/ast_extractors_extra.py", + "kind": "method-size", + "detail": "extract_go_definitions" + }, + { + "file": "mcp_server/core/ast_parser.py", + "kind": "layer-violation", + "detail": "tree_sitter_language_pack" + }, + { + "file": "mcp_server/core/attentional_control.py", + "kind": "method-size", + "detail": "allocate_attention" + }, + { + "file": "mcp_server/core/attentional_control.py", + "kind": "unsourced-constant", + "detail": "ATTENTION_TEMPERATURE" + }, + { + "file": "mcp_server/core/attentional_control.py", + "kind": "unsourced-constant", + "detail": "SALIENCE_WEIGHT" + }, + { + "file": "mcp_server/core/attribution_tracer.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "method-size", + "detail": "build_clusters" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "method-size", + "detail": "build_coverage_jobs" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "method-size", + "detail": "count_pending_clusters_streamed" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "unsourced-constant", + "detail": "MAX_MEMORIES_PER_PROMPT" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "unsourced-constant", + "detail": "MIN_AVG_HEAT_FOR_PAGE" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "unsourced-constant", + "detail": "MIN_ENTITY_FREQ_FOR_TOPIC" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "unsourced-constant", + "detail": "SKIP_IF_AUTHORED_WITHIN_DAYS" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "unsourced-constant", + "detail": "_EXISTING_BODY_CHARS" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "unsourced-constant", + "detail": "_MEMORY_HEAD_CHARS" + }, + { + "file": "mcp_server/core/auto_curator.py", + "kind": "unsourced-constant", + "detail": "_MIN_SNAKE_ENTITY_LEN" + }, + { + "file": "mcp_server/core/auto_task_record.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/auto_task_record.py", + "kind": "method-size", + "detail": "build_task_record" + }, + { + "file": "mcp_server/core/auto_task_record.py", + "kind": "unsourced-constant", + "detail": "MIN_TOOLS_FOR_RECORD" + }, + { + "file": "mcp_server/core/auto_task_record.py", + "kind": "unsourced-constant", + "detail": "_MAX_FILES_LISTED" + }, + { + "file": "mcp_server/core/blindspot_detector.py", + "kind": "method-size", + "detail": "_detect_tool_blind_spots" + }, + { + "file": "mcp_server/core/blindspot_patterns.py", + "kind": "unsourced-constant", + "detail": "_GLOBAL_DURATION_NORM" + }, + { + "file": "mcp_server/core/blindspot_patterns.py", + "kind": "unsourced-constant", + "detail": "_LONG_SESSION_MINUTES" + }, + { + "file": "mcp_server/core/blindspot_patterns.py", + "kind": "unsourced-constant", + "detail": "_MIN_GLOBAL_EXPLORATION" + }, + { + "file": "mcp_server/core/cascade_advancement.py", + "kind": "unsourced-constant", + "detail": "_EARLY_LTP_IMPORTANCE_BOOST" + }, + { + "file": "mcp_server/core/cascade_advancement.py", + "kind": "unsourced-constant", + "detail": "_SCHEMA_FAST_CONSOLIDATION_MATCH" + }, + { + "file": "mcp_server/core/causal_graph.py", + "kind": "method-size", + "detail": "discover_causal_edges" + }, + { + "file": "mcp_server/core/causal_pc.py", + "kind": "method-size", + "detail": "g2_independent" + }, + { + "file": "mcp_server/core/causal_pc.py", + "kind": "method-size", + "detail": "pc_skeleton" + }, + { + "file": "mcp_server/core/causal_pc.py", + "kind": "unsourced-constant", + "detail": "_EPS" + }, + { + "file": "mcp_server/core/causal_pc.py", + "kind": "unsourced-constant", + "detail": "_FPMIN" + }, + { + "file": "mcp_server/core/causal_pc.py", + "kind": "unsourced-constant", + "detail": "_ITMAX" + }, + { + "file": "mcp_server/core/change_impact_matcher.py", + "kind": "method-size", + "detail": "match_memories" + }, + { + "file": "mcp_server/core/claim_extractor.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/claim_extractor.py", + "kind": "method-size", + "detail": "extract_claims" + }, + { + "file": "mcp_server/core/claim_extractor.py", + "kind": "unsourced-constant", + "detail": "_MAX_SENTENCE_CHARS" + }, + { + "file": "mcp_server/core/claim_extractor.py", + "kind": "unsourced-constant", + "detail": "_MIN_SENTENCE_CHARS" + }, + { + "file": "mcp_server/core/claim_resolver.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/claim_resolver.py", + "kind": "method-size", + "detail": "plan_conflicts" + }, + { + "file": "mcp_server/core/claim_resolver.py", + "kind": "method-size", + "detail": "plan_supersedes" + }, + { + "file": "mcp_server/core/claim_resolver.py", + "kind": "method-size", + "detail": "resolve" + }, + { + "file": "mcp_server/core/codebase_communities.py", + "kind": "layer-violation", + "detail": "igraph" + }, + { + "file": "mcp_server/core/codebase_communities.py", + "kind": "layer-violation", + "detail": "leidenalg" + }, + { + "file": "mcp_server/core/codebase_communities.py", + "kind": "layer-violation", + "detail": "networkx" + }, + { + "file": "mcp_server/core/codebase_graph.py", + "kind": "layer-violation", + "detail": "pathlib" + }, + { + "file": "mcp_server/core/codebase_graph.py", + "kind": "method-size", + "detail": "build_resolved_call_edges" + }, + { + "file": "mcp_server/core/codebase_type_resolver.py", + "kind": "unsourced-constant", + "detail": "MIN_TYPE_NAME_LENGTH" + }, + { + "file": "mcp_server/core/cognitive_map.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/cognitive_map.py", + "kind": "layer-violation", + "detail": "numpy" + }, + { + "file": "mcp_server/core/cognitive_map.py", + "kind": "method-size", + "detail": "project_to_2d" + }, + { + "file": "mcp_server/core/cognitive_map.py", + "kind": "unsourced-constant", + "detail": "_MAX_NAVIGATE_DEPTH" + }, + { + "file": "mcp_server/core/cognitive_map.py", + "kind": "unsourced-constant", + "detail": "_MIN_NODES_FOR_SECOND_AXIS" + }, + { + "file": "mcp_server/core/compression.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/compression.py", + "kind": "method-size", + "detail": "get_compression_schedule" + }, + { + "file": "mcp_server/core/compression.py", + "kind": "unsourced-constant", + "detail": "_HIGH_ACCESS_COUNT" + }, + { + "file": "mcp_server/core/compression.py", + "kind": "unsourced-constant", + "detail": "_HIGH_CONFIDENCE" + }, + { + "file": "mcp_server/core/compression.py", + "kind": "unsourced-constant", + "detail": "_HIGH_SURPRISE" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "method-size", + "detail": "emerge" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "unsourced-constant", + "detail": "ABANDON_AFTER_DAYS" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "unsourced-constant", + "detail": "COLD_START_MEMORY_THRESHOLD" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "unsourced-constant", + "detail": "MERGE_JACCARD" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "unsourced-constant", + "detail": "MIN_AXIAL_SLOTS_FILLED" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "unsourced-constant", + "detail": "MIN_CLAIMS_PER_CONCEPT" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "unsourced-constant", + "detail": "MIN_GROUNDING_MEMORIES" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "unsourced-constant", + "detail": "PROMOTION_PROMOTE_STREAK" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "unsourced-constant", + "detail": "SATURATION_PROMOTE_STREAK" + }, + { + "file": "mcp_server/core/concept_emerger.py", + "kind": "unsourced-constant", + "detail": "_MAX_TERM_CHARS" + }, + { + "file": "mcp_server/core/conflict_monitor.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/conflict_monitor.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/conflict_monitor.py", + "kind": "method-size", + "detail": "assess_conflict" + }, + { + "file": "mcp_server/core/conflict_monitor.py", + "kind": "unsourced-constant", + "detail": "CONFLICT_THRESHOLD" + }, + { + "file": "mcp_server/core/conflict_monitor.py", + "kind": "unsourced-constant", + "detail": "LOSER_PENALTY" + }, + { + "file": "mcp_server/core/confluence_parser.py", + "kind": "method-size", + "detail": "parse_confluence_storage" + }, + { + "file": "mcp_server/core/consolidation_engine.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/consolidation_engine.py", + "kind": "method-size", + "detail": "find_near_duplicates" + }, + { + "file": "mcp_server/core/consolidation_engine.py", + "kind": "method-size", + "detail": "plan_cls_consolidation" + }, + { + "file": "mcp_server/core/consolidation_engine.py", + "kind": "unsourced-constant", + "detail": "_MIN_RELATED_SEMANTICS" + }, + { + "file": "mcp_server/core/context_assembly/coverage.py", + "kind": "layer-violation", + "detail": "numpy" + }, + { + "file": "mcp_server/core/context_assembly/coverage.py", + "kind": "method-size", + "detail": "submodular_select" + }, + { + "file": "mcp_server/core/context_assembly/decomposer.py", + "kind": "method-size", + "detail": "assemble_prompt" + }, + { + "file": "mcp_server/core/context_assembly/ppr_traversal.py", + "kind": "method-size", + "detail": "personalized_pagerank" + }, + { + "file": "mcp_server/core/context_assembly/stage_assembler.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/context_assembly/stage_assembler.py", + "kind": "method-size", + "detail": "StageAwareContextAssembler.assemble" + }, + { + "file": "mcp_server/core/context_assembly/warning.py", + "kind": "unsourced-constant", + "detail": "_SIGNIFICANT_REDUCTION" + }, + { + "file": "mcp_server/core/curation.py", + "kind": "unsourced-constant", + "detail": "LINK_HIGH" + }, + { + "file": "mcp_server/core/curation.py", + "kind": "unsourced-constant", + "detail": "LINK_LOW" + }, + { + "file": "mcp_server/core/curation.py", + "kind": "unsourced-constant", + "detail": "MERGE_THRESHOLD" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "method-size", + "detail": "compute_dendritic_integration" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "unsourced-constant", + "detail": "BRANCH_ADMISSION_THRESHOLD" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "unsourced-constant", + "detail": "MAX_BRANCH_SIZE" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "unsourced-constant", + "detail": "PRIMING_STRENGTH" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "unsourced-constant", + "detail": "SOMA_OFFSET" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "unsourced-constant", + "detail": "SOMA_SCALE" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "unsourced-constant", + "detail": "SOMA_STEEPNESS" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "unsourced-constant", + "detail": "SUBUNIT_HALF_ACTIVATION" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "unsourced-constant", + "detail": "SUBUNIT_LINEAR_COEFF" + }, + { + "file": "mcp_server/core/dendritic_computation.py", + "kind": "unsourced-constant", + "detail": "SUBUNIT_QUADRATIC_COEFF" + }, + { + "file": "mcp_server/core/distillation.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/distillation.py", + "kind": "method-size", + "detail": "build_co_access_dossiers" + }, + { + "file": "mcp_server/core/distillation.py", + "kind": "method-size", + "detail": "build_error_success_dossiers" + }, + { + "file": "mcp_server/core/distillation.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_ERROR_SUCCESS_WINDOW_HOURS" + }, + { + "file": "mcp_server/core/distillation.py", + "kind": "unsourced-constant", + "detail": "_MAX_DOSSIERS_PER_KIND" + }, + { + "file": "mcp_server/core/distillation.py", + "kind": "unsourced-constant", + "detail": "_MAX_MEMORY_IDS_PER_DOSSIER" + }, + { + "file": "mcp_server/core/distillation.py", + "kind": "unsourced-constant", + "detail": "_MIN_CO_ACCESS_CLUSTER_SIZE" + }, + { + "file": "mcp_server/core/distillation.py", + "kind": "unsourced-constant", + "detail": "_MIN_RECURRING_ACCESS" + }, + { + "file": "mcp_server/core/distillation_reporting.py", + "kind": "method-size", + "detail": "build_distill_prompt" + }, + { + "file": "mcp_server/core/document_normalizer.py", + "kind": "method-size", + "detail": "normalize_document" + }, + { + "file": "mcp_server/core/document_normalizer.py", + "kind": "unsourced-constant", + "detail": "_MAX_SLUG" + }, + { + "file": "mcp_server/core/docx_parser.py", + "kind": "method-size", + "detail": "parse_docx_xml" + }, + { + "file": "mcp_server/core/domain_detector.py", + "kind": "unsourced-constant", + "detail": "THRESHOLD_CONFIDENT" + }, + { + "file": "mcp_server/core/domain_detector.py", + "kind": "unsourced-constant", + "detail": "THRESHOLD_TENTATIVE" + }, + { + "file": "mcp_server/core/domain_detector.py", + "kind": "unsourced-constant", + "detail": "W_CATEGORY" + }, + { + "file": "mcp_server/core/domain_detector.py", + "kind": "unsourced-constant", + "detail": "W_CONTENT" + }, + { + "file": "mcp_server/core/domain_detector.py", + "kind": "unsourced-constant", + "detail": "W_PROJECT" + }, + { + "file": "mcp_server/core/draft_compiler.py", + "kind": "method-size", + "detail": "compile_draft" + }, + { + "file": "mcp_server/core/draft_curator.py", + "kind": "method-size", + "detail": "evaluate_draft" + }, + { + "file": "mcp_server/core/draft_curator.py", + "kind": "unsourced-constant", + "detail": "MAX_LEAD_WORDS" + }, + { + "file": "mcp_server/core/draft_curator.py", + "kind": "unsourced-constant", + "detail": "MIN_CONFIDENCE_APPROVE" + }, + { + "file": "mcp_server/core/draft_curator.py", + "kind": "unsourced-constant", + "detail": "MIN_CONFIDENCE_HOLD" + }, + { + "file": "mcp_server/core/draft_synthesizer.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/draft_synthesizer.py", + "kind": "method-size", + "detail": "_route_claims_to_sections" + }, + { + "file": "mcp_server/core/draft_synthesizer.py", + "kind": "method-size", + "detail": "synthesize_draft" + }, + { + "file": "mcp_server/core/draft_synthesizer.py", + "kind": "unsourced-constant", + "detail": "_LEAD_ESSAY_CHARS" + }, + { + "file": "mcp_server/core/draft_synthesizer.py", + "kind": "unsourced-constant", + "detail": "_LEAD_MAX_CHARS" + }, + { + "file": "mcp_server/core/dual_process_retrieval.py", + "kind": "method-size", + "detail": "triage" + }, + { + "file": "mcp_server/core/dual_process_retrieval.py", + "kind": "unsourced-constant", + "detail": "FAMILIARITY_MARGIN" + }, + { + "file": "mcp_server/core/dual_process_retrieval.py", + "kind": "unsourced-constant", + "detail": "FAMILIARITY_THRESHOLD" + }, + { + "file": "mcp_server/core/emergence_metrics.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/emergence_metrics.py", + "kind": "method-size", + "detail": "_schema_acceleration_from_agg" + }, + { + "file": "mcp_server/core/emergence_metrics.py", + "kind": "method-size", + "detail": "generate_emergence_report_streamed" + }, + { + "file": "mcp_server/core/emergence_metrics.py", + "kind": "unsourced-constant", + "detail": "_FIT_R2_WEAK" + }, + { + "file": "mcp_server/core/emergence_metrics.py", + "kind": "unsourced-constant", + "detail": "_MIN_FIT_BINS" + }, + { + "file": "mcp_server/core/emergence_metrics.py", + "kind": "unsourced-constant", + "detail": "_SCHEMA_MATCH_LOW" + }, + { + "file": "mcp_server/core/emergence_tracker.py", + "kind": "method-size", + "detail": "compute_schema_acceleration_metric" + }, + { + "file": "mcp_server/core/emotional_tagging.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/emotional_tagging.py", + "kind": "method-size", + "detail": "detect_emotions" + }, + { + "file": "mcp_server/core/entity_dedup_filters.py", + "kind": "unsourced-constant", + "detail": "ENTROPY_THRESHOLD" + }, + { + "file": "mcp_server/core/entity_dedup_filters.py", + "kind": "unsourced-constant", + "detail": "LSH_THRESHOLD" + }, + { + "file": "mcp_server/core/entity_dedup_filters.py", + "kind": "unsourced-constant", + "detail": "MERGE_THRESHOLD" + }, + { + "file": "mcp_server/core/entity_dedup_filters.py", + "kind": "unsourced-constant", + "detail": "_SAME_LEN_SUB_JW" + }, + { + "file": "mcp_server/core/entity_dedup_filters.py", + "kind": "unsourced-constant", + "detail": "_SHINGLE_K" + }, + { + "file": "mcp_server/core/entity_dedup_filters.py", + "kind": "unsourced-constant", + "detail": "_SHORT_LABEL_MAX" + }, + { + "file": "mcp_server/core/entity_reconciliation.py", + "kind": "method-size", + "detail": "build_reconciliation_sql" + }, + { + "file": "mcp_server/core/entity_reconciliation.py", + "kind": "method-size", + "detail": "reconcile_leak_ratio" + }, + { + "file": "mcp_server/core/entity_reconciliation.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_ENTITY_AGE_HOURS" + }, + { + "file": "mcp_server/core/entity_reconciliation.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_MEMORY_AGE_DAYS" + }, + { + "file": "mcp_server/core/entity_reconciliation.py", + "kind": "unsourced-constant", + "detail": "LEAK_WARNING_THRESHOLD" + }, + { + "file": "mcp_server/core/extinction.py", + "kind": "unsourced-constant", + "detail": "EXTINCTION_TRIAL_GAIN" + }, + { + "file": "mcp_server/core/extinction.py", + "kind": "unsourced-constant", + "detail": "RECOVERY_FLOOR" + }, + { + "file": "mcp_server/core/extinction.py", + "kind": "unsourced-constant", + "detail": "RECOVERY_HALF_LIFE_HOURS" + }, + { + "file": "mcp_server/core/forward_model.py", + "kind": "unsourced-constant", + "detail": "CORRECTION_GAIN" + }, + { + "file": "mcp_server/core/forward_model.py", + "kind": "unsourced-constant", + "detail": "ERROR_DEADBAND" + }, + { + "file": "mcp_server/core/fractal.py", + "kind": "unsourced-constant", + "detail": "_LONG_QUERY_WORDS" + }, + { + "file": "mcp_server/core/gist_extraction.py", + "kind": "unsourced-constant", + "detail": "_HEAD_FRACTION" + }, + { + "file": "mcp_server/core/gist_extraction.py", + "kind": "unsourced-constant", + "detail": "_SIGNAL_FRACTION" + }, + { + "file": "mcp_server/core/global_detector.py", + "kind": "method-size", + "detail": "detect_global" + }, + { + "file": "mcp_server/core/global_detector.py", + "kind": "unsourced-constant", + "detail": "GLOBAL_THRESHOLD" + }, + { + "file": "mcp_server/core/goal_maintenance.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/goal_maintenance.py", + "kind": "method-size", + "detail": "build_goal_from_triggers" + }, + { + "file": "mcp_server/core/goal_maintenance.py", + "kind": "unsourced-constant", + "detail": "GOAL_RECALL_WEIGHT" + }, + { + "file": "mcp_server/core/goal_maintenance.py", + "kind": "unsourced-constant", + "detail": "GOAL_WRITE_WEIGHT" + }, + { + "file": "mcp_server/core/goal_maintenance.py", + "kind": "unsourced-constant", + "detail": "_MIN_KEYWORD_LEN" + }, + { + "file": "mcp_server/core/grooming_health.py", + "kind": "unsourced-constant", + "detail": "GROOMING_STALENESS_THRESHOLD_DAYS" + }, + { + "file": "mcp_server/core/habituation.py", + "kind": "method-size", + "detail": "habituate_novelty" + }, + { + "file": "mcp_server/core/habituation.py", + "kind": "unsourced-constant", + "detail": "HABITUATION_RATE" + }, + { + "file": "mcp_server/core/habituation.py", + "kind": "unsourced-constant", + "detail": "MIN_RESPONSE_GAIN" + }, + { + "file": "mcp_server/core/habituation.py", + "kind": "unsourced-constant", + "detail": "SALIENCE_THRESHOLD" + }, + { + "file": "mcp_server/core/habituation.py", + "kind": "unsourced-constant", + "detail": "SENSITIZATION_DECAY_HOURS" + }, + { + "file": "mcp_server/core/habituation.py", + "kind": "unsourced-constant", + "detail": "SENSITIZATION_PEAK" + }, + { + "file": "mcp_server/core/habituation.py", + "kind": "unsourced-constant", + "detail": "SPONTANEOUS_RECOVERY_PER_HOUR" + }, + { + "file": "mcp_server/core/hdc_encoder.py", + "kind": "layer-violation", + "detail": "numpy" + }, + { + "file": "mcp_server/core/hdc_encoder.py", + "kind": "unsourced-constant", + "detail": "_SEED" + }, + { + "file": "mcp_server/core/hierarchical_predictive_coding.py", + "kind": "method-size", + "detail": "compute_hierarchical_novelty" + }, + { + "file": "mcp_server/core/homeostatic_health.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/homeostatic_health.py", + "kind": "method-size", + "detail": "_compute_moments" + }, + { + "file": "mcp_server/core/homeostatic_health.py", + "kind": "method-size", + "detail": "_merge_chunk_moments" + }, + { + "file": "mcp_server/core/homeostatic_health.py", + "kind": "method-size", + "detail": "compute_distribution_health_streaming_by_class" + }, + { + "file": "mcp_server/core/homeostatic_plasticity.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/homeostatic_plasticity.py", + "kind": "unsourced-constant", + "detail": "_BCM_THETA_DECAY" + }, + { + "file": "mcp_server/core/homeostatic_plasticity.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_COHORT_SIGMA" + }, + { + "file": "mcp_server/core/homeostatic_plasticity.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_COHORT_STRENGTH" + }, + { + "file": "mcp_server/core/homeostatic_plasticity.py", + "kind": "unsourced-constant", + "detail": "_MAX_GLOBAL_EXCITABILITY" + }, + { + "file": "mcp_server/core/homeostatic_plasticity.py", + "kind": "unsourced-constant", + "detail": "_MIN_GLOBAL_EXCITABILITY" + }, + { + "file": "mcp_server/core/homeostatic_plasticity.py", + "kind": "unsourced-constant", + "detail": "_SCALING_RATE" + }, + { + "file": "mcp_server/core/homeostatic_plasticity.py", + "kind": "unsourced-constant", + "detail": "_TARGET_ACTIVE_FRACTION" + }, + { + "file": "mcp_server/core/homeostatic_plasticity.py", + "kind": "unsourced-constant", + "detail": "_TARGET_HEAT" + }, + { + "file": "mcp_server/core/hopfield.py", + "kind": "layer-violation", + "detail": "numpy" + }, + { + "file": "mcp_server/core/interference.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/interference.py", + "kind": "method-size", + "detail": "compute_retrieval_suppression" + }, + { + "file": "mcp_server/core/interference.py", + "kind": "method-size", + "detail": "orthogonalize_pair" + }, + { + "file": "mcp_server/core/interference.py", + "kind": "unsourced-constant", + "detail": "_INTERFERENCE_THRESHOLD" + }, + { + "file": "mcp_server/core/interference.py", + "kind": "unsourced-constant", + "detail": "_MIN_ORTHOGONAL_SIMILARITY" + }, + { + "file": "mcp_server/core/interference.py", + "kind": "unsourced-constant", + "detail": "_ORTHOGONALIZATION_RATE" + }, + { + "file": "mcp_server/core/interference.py", + "kind": "unsourced-constant", + "detail": "_PRESSURE_HIGH" + }, + { + "file": "mcp_server/core/interference.py", + "kind": "unsourced-constant", + "detail": "_PRESSURE_MEDIUM" + }, + { + "file": "mcp_server/core/interference.py", + "kind": "unsourced-constant", + "detail": "_RETRIEVAL_SUPPRESSION" + }, + { + "file": "mcp_server/core/interference_detection.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/interference_detection.py", + "kind": "unsourced-constant", + "detail": "_CONTEXT_DISCOUNT" + }, + { + "file": "mcp_server/core/interference_detection.py", + "kind": "unsourced-constant", + "detail": "_CRITICAL_INTERFERENCE" + }, + { + "file": "mcp_server/core/interference_detection.py", + "kind": "unsourced-constant", + "detail": "_INTERFERENCE_THRESHOLD" + }, + { + "file": "mcp_server/core/memory_decomposer.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/memory_decomposer.py", + "kind": "method-size", + "detail": "decompose_memory" + }, + { + "file": "mcp_server/core/memory_dedup_exact.py", + "kind": "method-size", + "detail": "elect_survivor" + }, + { + "file": "mcp_server/core/memory_ingest.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/core/memory_ingest.py", + "kind": "method-size", + "detail": "ingest_memory" + }, + { + "file": "mcp_server/core/memory_reheat.py", + "kind": "method-size", + "detail": "compute_reheat_target" + }, + { + "file": "mcp_server/core/memory_reheat.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_REHEAT_TARGET" + }, + { + "file": "mcp_server/core/memory_reheat.py", + "kind": "unsourced-constant", + "detail": "_EPSILON" + }, + { + "file": "mcp_server/core/memory_rules.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/memory_rules.py", + "kind": "method-size", + "detail": "apply_rules" + }, + { + "file": "mcp_server/core/metacognition_analysis.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/metacognition_analysis.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_MAX_CHUNKS" + }, + { + "file": "mcp_server/core/metacognition_analysis.py", + "kind": "unsourced-constant", + "detail": "_COVERAGE_PARTIAL" + }, + { + "file": "mcp_server/core/metacognition_analysis.py", + "kind": "unsourced-constant", + "detail": "_RECENCY_DEFAULT" + }, + { + "file": "mcp_server/core/microglial_pruning.py", + "kind": "method-size", + "detail": "identify_prunable_edges" + }, + { + "file": "mcp_server/core/microglial_pruning.py", + "kind": "unsourced-constant", + "detail": "_ALPHA_THRESHOLD" + }, + { + "file": "mcp_server/core/microglial_pruning.py", + "kind": "unsourced-constant", + "detail": "_MIN_ENTITY_HEAT" + }, + { + "file": "mcp_server/core/microglial_pruning.py", + "kind": "unsourced-constant", + "detail": "_PROTECTION_ACCESS_THRESHOLD" + }, + { + "file": "mcp_server/core/microglial_pruning.py", + "kind": "unsourced-constant", + "detail": "_TEMPORAL_HALF_LIFE_HOURS" + }, + { + "file": "mcp_server/core/mmr_diversity.py", + "kind": "layer-violation", + "detail": "numpy" + }, + { + "file": "mcp_server/core/mmr_diversity.py", + "kind": "method-size", + "detail": "mmr_rerank" + }, + { + "file": "mcp_server/core/narrative.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "method-size", + "detail": "compute_dopamine_rpe" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "unsourced-constant", + "detail": "ACH_ALPHA" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "unsourced-constant", + "detail": "DA_ALPHA" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "unsourced-constant", + "detail": "NE_ALPHA" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "unsourced-constant", + "detail": "NE_HABITUATION_DECAY" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "unsourced-constant", + "detail": "NE_HABITUATION_RATE" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "unsourced-constant", + "detail": "SER_ALPHA" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "unsourced-constant", + "detail": "_ACH_SER_COUPLING" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "unsourced-constant", + "detail": "_DA_NE_COUPLING" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "unsourced-constant", + "detail": "_NE_ACH_COUPLING" + }, + { + "file": "mcp_server/core/neuromodulation_channels.py", + "kind": "unsourced-constant", + "detail": "_SER_DA_COUPLING" + }, + { + "file": "mcp_server/core/oscillatory_phases.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/oscillatory_phases.py", + "kind": "unsourced-constant", + "detail": "ACH_BASELINE" + }, + { + "file": "mcp_server/core/oscillatory_phases.py", + "kind": "unsourced-constant", + "detail": "GAMMA_CAPACITY" + }, + { + "file": "mcp_server/core/oscillatory_phases.py", + "kind": "unsourced-constant", + "detail": "SIGMOID_STEEPNESS" + }, + { + "file": "mcp_server/core/oscillatory_phases.py", + "kind": "unsourced-constant", + "detail": "SUPPRESSION_X" + }, + { + "file": "mcp_server/core/oscillatory_phases.py", + "kind": "unsourced-constant", + "detail": "SWR_BASE_PROBABILITY" + }, + { + "file": "mcp_server/core/oscillatory_phases.py", + "kind": "unsourced-constant", + "detail": "SWR_BURST_STEPS" + }, + { + "file": "mcp_server/core/oscillatory_phases.py", + "kind": "unsourced-constant", + "detail": "SWR_MIN_INTERVAL_HOURS" + }, + { + "file": "mcp_server/core/oscillatory_phases.py", + "kind": "unsourced-constant", + "detail": "SWR_REFRACTORY_STEPS" + }, + { + "file": "mcp_server/core/oscillatory_phases.py", + "kind": "unsourced-constant", + "detail": "TRANSITION_WIDTH" + }, + { + "file": "mcp_server/core/pg_recall_assembly.py", + "kind": "method-size", + "detail": "assemble_context" + }, + { + "file": "mcp_server/core/pg_recall_assembly.py", + "kind": "method-size", + "detail": "assemble_context._memories_by_entity_fn" + }, + { + "file": "mcp_server/core/pg_recall_assembly.py", + "kind": "method-size", + "detail": "assemble_context._retrieve_fn" + }, + { + "file": "mcp_server/core/pg_recall_signals.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/core/pg_recall_weights.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/pg_recall_weights.py", + "kind": "method-size", + "detail": "compute_pg_weights" + }, + { + "file": "mcp_server/core/platt_calibration.py", + "kind": "method-size", + "detail": "fit_platt" + }, + { + "file": "mcp_server/core/predictive_coding_flat.py", + "kind": "unsourced-constant", + "detail": "_PAGE_MAX_CHARS" + }, + { + "file": "mcp_server/core/predictive_coding_flat.py", + "kind": "unsourced-constant", + "detail": "_PARAGRAPH_MAX_CHARS" + }, + { + "file": "mcp_server/core/predictive_coding_flat.py", + "kind": "unsourced-constant", + "detail": "_SECTION_MAX_CHARS" + }, + { + "file": "mcp_server/core/predictive_coding_gate.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_THRESHOLD" + }, + { + "file": "mcp_server/core/procedural_memory.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/procedural_memory.py", + "kind": "method-size", + "detail": "match_skills" + }, + { + "file": "mcp_server/core/procedural_memory.py", + "kind": "method-size", + "detail": "mine_skills" + }, + { + "file": "mcp_server/core/procedural_memory.py", + "kind": "unsourced-constant", + "detail": "HABITUAL_THRESHOLD" + }, + { + "file": "mcp_server/core/procedural_memory.py", + "kind": "unsourced-constant", + "detail": "MAX_SKILL_LEN" + }, + { + "file": "mcp_server/core/procedural_memory.py", + "kind": "unsourced-constant", + "detail": "MIN_SKILL_SUPPORT" + }, + { + "file": "mcp_server/core/procedural_memory.py", + "kind": "unsourced-constant", + "detail": "PROFICIENCY_ALPHA" + }, + { + "file": "mcp_server/core/profile_assembler.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/profile_assembler.py", + "kind": "method-size", + "detail": "_build_single_domain" + }, + { + "file": "mcp_server/core/profile_builder.py", + "kind": "unsourced-constant", + "detail": "_BURST_THRESHOLD_MS" + }, + { + "file": "mcp_server/core/profile_builder.py", + "kind": "unsourced-constant", + "detail": "_EMA_ALPHA" + }, + { + "file": "mcp_server/core/profile_builder.py", + "kind": "unsourced-constant", + "detail": "_EXPLORATION_THRESHOLD_TURNS" + }, + { + "file": "mcp_server/core/provenance.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/provenance.py", + "kind": "method-size", + "detail": "_unverifiable_hint" + }, + { + "file": "mcp_server/core/provenance.py", + "kind": "method-size", + "detail": "grade_provenance" + }, + { + "file": "mcp_server/core/query_intent.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/query_intent.py", + "kind": "method-size", + "detail": "_score_patterns" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "_resolve_query_entity_ids" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "_run_spread_activation" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "attentional_focus_rerank" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "conflict_monitor_rerank" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "dendritic_modulate" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "emotional_retrieval_rerank" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "familiarity_triage" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "hopfield_complete" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "mood_congruent_rerank" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "reconsolidation_apply" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "spreading_activation_expand" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "method-size", + "detail": "spreading_activation_tail_fill" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "unsourced-constant", + "detail": "_EMOTIONAL_QUERY_VALENCE_FLOOR" + }, + { + "file": "mcp_server/core/recall_pipeline.py", + "kind": "unsourced-constant", + "detail": "_RRF_K" + }, + { + "file": "mcp_server/core/reconsolidation.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/reconsolidation.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/reconsolidation.py", + "kind": "method-size", + "detail": "compute_extinction_action" + }, + { + "file": "mcp_server/core/reconsolidation.py", + "kind": "method-size", + "detail": "compute_reconsolidation_action" + }, + { + "file": "mcp_server/core/reconsolidation.py", + "kind": "method-size", + "detail": "decide_action" + }, + { + "file": "mcp_server/core/reconsolidation.py", + "kind": "unsourced-constant", + "detail": "_RECONS_HEAT_BUMP_ARCHIVE" + }, + { + "file": "mcp_server/core/reconsolidation.py", + "kind": "unsourced-constant", + "detail": "_RECONS_HEAT_BUMP_NONE" + }, + { + "file": "mcp_server/core/reconsolidation.py", + "kind": "unsourced-constant", + "detail": "_RECONS_QUERY_VALENCE_FLOOR" + }, + { + "file": "mcp_server/core/reconsolidation.py", + "kind": "unsourced-constant", + "detail": "_RECONS_VALENCE_STEP" + }, + { + "file": "mcp_server/core/replay.py", + "kind": "unsourced-constant", + "detail": "_MAX_SEQUENCES_PER_SWR" + }, + { + "file": "mcp_server/core/replay_execution.py", + "kind": "unsourced-constant", + "detail": "_COMPRESSION_RATIO" + }, + { + "file": "mcp_server/core/replay_execution.py", + "kind": "unsourced-constant", + "detail": "_STDP_REPLAY_SCALE" + }, + { + "file": "mcp_server/core/replay_selection.py", + "kind": "unsourced-constant", + "detail": "_MAX_SEQUENCES_PER_SWR" + }, + { + "file": "mcp_server/core/replay_selection.py", + "kind": "unsourced-constant", + "detail": "_PRIORITY_THRESHOLD" + }, + { + "file": "mcp_server/core/reranker.py", + "kind": "layer-violation", + "detail": "flashrank" + }, + { + "file": "mcp_server/core/reranker.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/core/reranker.py", + "kind": "method-size", + "detail": "_ensure_reranker" + }, + { + "file": "mcp_server/core/reranker.py", + "kind": "method-size", + "detail": "rerank_results" + }, + { + "file": "mcp_server/core/reranker_calibration.py", + "kind": "unsourced-constant", + "detail": "MAX_SAMPLES" + }, + { + "file": "mcp_server/core/reranker_calibration.py", + "kind": "unsourced-constant", + "detail": "REFIT_EVERY" + }, + { + "file": "mcp_server/core/reranker_model.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/reranker_model.py", + "kind": "layer-violation", + "detail": "pathlib" + }, + { + "file": "mcp_server/core/reranker_scoring.py", + "kind": "method-size", + "detail": "_blend_scores" + }, + { + "file": "mcp_server/core/reranker_scoring.py", + "kind": "method-size", + "detail": "_compute_adaptive_alpha" + }, + { + "file": "mcp_server/core/retrieval_dispatch.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/core/retrieval_dispatch.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/retrieval_dispatch.py", + "kind": "method-size", + "detail": "dispatch_retrieval" + }, + { + "file": "mcp_server/core/retrieval_signals.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/core/retrieval_signals.py", + "kind": "method-size", + "detail": "compute_hopfield_hdc" + }, + { + "file": "mcp_server/core/schema_engine.py", + "kind": "unsourced-constant", + "detail": "_HIGH_MATCH_THRESHOLD" + }, + { + "file": "mcp_server/core/schema_engine.py", + "kind": "unsourced-constant", + "detail": "_MAX_VIOLATIONS_BEFORE_REVISION" + }, + { + "file": "mcp_server/core/schema_engine.py", + "kind": "unsourced-constant", + "detail": "_MEDIUM_MATCH_THRESHOLD" + }, + { + "file": "mcp_server/core/schema_engine.py", + "kind": "unsourced-constant", + "detail": "_SCHEMA_EMA_ALPHA" + }, + { + "file": "mcp_server/core/schema_extraction.py", + "kind": "unsourced-constant", + "detail": "_ENTITY_FREQUENCY_THRESHOLD" + }, + { + "file": "mcp_server/core/schema_extraction.py", + "kind": "unsourced-constant", + "detail": "_HIGH_MATCH_THRESHOLD" + }, + { + "file": "mcp_server/core/schema_extraction.py", + "kind": "unsourced-constant", + "detail": "_MIN_FORMATION_COUNT" + }, + { + "file": "mcp_server/core/schema_extraction.py", + "kind": "unsourced-constant", + "detail": "_RELATIONSHIP_FREQUENCY_THRESHOLD" + }, + { + "file": "mcp_server/core/schema_extraction.py", + "kind": "unsourced-constant", + "detail": "_SCHEMA_EMA_ALPHA" + }, + { + "file": "mcp_server/core/schema_extraction.py", + "kind": "unsourced-constant", + "detail": "_SCHEMA_MERGE_THRESHOLD" + }, + { + "file": "mcp_server/core/separation_core.py", + "kind": "unsourced-constant", + "detail": "_IDENTITY_THRESHOLD" + }, + { + "file": "mcp_server/core/separation_core.py", + "kind": "unsourced-constant", + "detail": "_MIN_POST_SEPARATION_SIMILARITY" + }, + { + "file": "mcp_server/core/separation_core.py", + "kind": "unsourced-constant", + "detail": "_SEPARATION_THRESHOLD" + }, + { + "file": "mcp_server/core/separation_core.py", + "kind": "unsourced-constant", + "detail": "_SPARSITY_TARGET" + }, + { + "file": "mcp_server/core/session_critique.py", + "kind": "unsourced-constant", + "detail": "_DEEP_FOCUS_DEPTH" + }, + { + "file": "mcp_server/core/session_critique.py", + "kind": "unsourced-constant", + "detail": "_LOW_DIVERSITY_THRESHOLD" + }, + { + "file": "mcp_server/core/session_critique.py", + "kind": "unsourced-constant", + "detail": "_LOW_ENTITY_COVERAGE" + }, + { + "file": "mcp_server/core/session_critique.py", + "kind": "unsourced-constant", + "detail": "_MAX_UNDER_USED_BEFORE_SUGGESTION" + }, + { + "file": "mcp_server/core/session_critique.py", + "kind": "unsourced-constant", + "detail": "_MIN_CALLS_FOR_OVER_RELIANCE" + }, + { + "file": "mcp_server/core/session_critique.py", + "kind": "unsourced-constant", + "detail": "_MIN_ENTITIES_FOR_COVERAGE_SUGGESTION" + }, + { + "file": "mcp_server/core/session_critique.py", + "kind": "unsourced-constant", + "detail": "_MIN_FILES_FOR_BREADTH_SUGGESTION" + }, + { + "file": "mcp_server/core/session_critique.py", + "kind": "unsourced-constant", + "detail": "_NARROW_BREADTH_THRESHOLD" + }, + { + "file": "mcp_server/core/session_critique.py", + "kind": "unsourced-constant", + "detail": "_NARROW_FOCUS_BREADTH" + }, + { + "file": "mcp_server/core/session_critique.py", + "kind": "unsourced-constant", + "detail": "_OVER_RELIANCE_SHARE" + }, + { + "file": "mcp_server/core/session_critique_format.py", + "kind": "unsourced-constant", + "detail": "_LOW_CONFIDENCE_THRESHOLD" + }, + { + "file": "mcp_server/core/session_critique_format.py", + "kind": "unsourced-constant", + "detail": "_MIN_MEMORIES_FOR_DECISION_CHECK" + }, + { + "file": "mcp_server/core/session_extractor.py", + "kind": "unsourced-constant", + "detail": "_MAX_CONTENT_LEN" + }, + { + "file": "mcp_server/core/session_extractor.py", + "kind": "unsourced-constant", + "detail": "_MIN_CONTENT_LEN" + }, + { + "file": "mcp_server/core/session_shape.py", + "kind": "unsourced-constant", + "detail": "_BURST_THRESHOLD_MS" + }, + { + "file": "mcp_server/core/session_shape.py", + "kind": "unsourced-constant", + "detail": "_EXPLORATION_THRESHOLD_TURNS" + }, + { + "file": "mcp_server/core/sleep_compute.py", + "kind": "method-size", + "detail": "run_sleep_compute_streamed" + }, + { + "file": "mcp_server/core/sleep_phases.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/sleep_phases.py", + "kind": "method-size", + "detail": "run_rem_phase" + }, + { + "file": "mcp_server/core/sleep_phases.py", + "kind": "method-size", + "detail": "run_two_phase_consolidation" + }, + { + "file": "mcp_server/core/source_monitoring.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/source_monitoring.py", + "kind": "method-size", + "detail": "classify_source" + }, + { + "file": "mcp_server/core/source_monitoring.py", + "kind": "unsourced-constant", + "detail": "_GIT_FULL_SHA_LEN" + }, + { + "file": "mcp_server/core/spreading_activation.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_DECAY" + }, + { + "file": "mcp_server/core/spreading_activation.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_MAX_DEPTH" + }, + { + "file": "mcp_server/core/spreading_activation.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_MAX_NODES" + }, + { + "file": "mcp_server/core/spreading_activation.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_THRESHOLD" + }, + { + "file": "mcp_server/core/staleness.py", + "kind": "method-size", + "detail": "assess_staleness" + }, + { + "file": "mcp_server/core/streaming/adaptive_writer.py", + "kind": "method-size", + "detail": "adaptive_drain" + }, + { + "file": "mcp_server/core/streaming/calibrated.py", + "kind": "unsourced-constant", + "detail": "EDGE_B_MAX" + }, + { + "file": "mcp_server/core/streaming/calibrated.py", + "kind": "unsourced-constant", + "detail": "EDGE_ROW_BYTES" + }, + { + "file": "mcp_server/core/streaming/calibrated.py", + "kind": "unsourced-constant", + "detail": "EDGE_W_TARGET_S" + }, + { + "file": "mcp_server/core/streaming/calibrated.py", + "kind": "unsourced-constant", + "detail": "ENTITY_B_MAX" + }, + { + "file": "mcp_server/core/streaming/calibrated.py", + "kind": "unsourced-constant", + "detail": "ENTITY_B_MIN" + }, + { + "file": "mcp_server/core/streaming/calibrated.py", + "kind": "unsourced-constant", + "detail": "ENTITY_ROW_BYTES" + }, + { + "file": "mcp_server/core/streaming/calibrated.py", + "kind": "unsourced-constant", + "detail": "ENTITY_W_TARGET_S" + }, + { + "file": "mcp_server/core/stress_modulation.py", + "kind": "unsourced-constant", + "detail": "STRESS_ENHANCE_HEIGHT" + }, + { + "file": "mcp_server/core/stress_modulation.py", + "kind": "unsourced-constant", + "detail": "STRESS_ENHANCE_PEAK" + }, + { + "file": "mcp_server/core/stress_modulation.py", + "kind": "unsourced-constant", + "detail": "STRESS_GAIN_FLOOR" + }, + { + "file": "mcp_server/core/stress_modulation.py", + "kind": "unsourced-constant", + "detail": "STRESS_MARKER_CAP" + }, + { + "file": "mcp_server/core/stress_modulation.py", + "kind": "unsourced-constant", + "detail": "STRESS_OVERLOAD_ONSET" + }, + { + "file": "mcp_server/core/stress_modulation.py", + "kind": "unsourced-constant", + "detail": "STRESS_OVERLOAD_PENALTY" + }, + { + "file": "mcp_server/core/stress_modulation.py", + "kind": "unsourced-constant", + "detail": "STRESS_W_ERROR" + }, + { + "file": "mcp_server/core/stress_modulation.py", + "kind": "unsourced-constant", + "detail": "STRESS_W_FAILURE" + }, + { + "file": "mcp_server/core/stress_modulation.py", + "kind": "unsourced-constant", + "detail": "STRESS_W_URGENCY" + }, + { + "file": "mcp_server/core/style_classifier.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/style_classifier.py", + "kind": "unsourced-constant", + "detail": "_ACTIVE_SESSION_MAX_MINUTES" + }, + { + "file": "mcp_server/core/style_classifier.py", + "kind": "unsourced-constant", + "detail": "_BREADTH_CALLS_PER_FILE_MAX" + }, + { + "file": "mcp_server/core/style_classifier.py", + "kind": "unsourced-constant", + "detail": "_DEPTH_CALLS_PER_FILE_MIN" + }, + { + "file": "mcp_server/core/style_classifier.py", + "kind": "unsourced-constant", + "detail": "_DOMINANT_TOOL_SHARE" + }, + { + "file": "mcp_server/core/style_classifier.py", + "kind": "unsourced-constant", + "detail": "_MANY_FILES_TOUCHED" + }, + { + "file": "mcp_server/core/style_classifier.py", + "kind": "unsourced-constant", + "detail": "_REFLECTIVE_SESSION_MIN_MINUTES" + }, + { + "file": "mcp_server/core/style_classifier.py", + "kind": "unsourced-constant", + "detail": "_SEQUENTIAL_CALLS_PER_FILE_MIN" + }, + { + "file": "mcp_server/core/synaptic_plasticity_hebbian.py", + "kind": "unsourced-constant", + "detail": "_BCM_THETA_DECAY" + }, + { + "file": "mcp_server/core/synaptic_plasticity_hebbian.py", + "kind": "unsourced-constant", + "detail": "_LTD_RATE" + }, + { + "file": "mcp_server/core/synaptic_plasticity_hebbian.py", + "kind": "unsourced-constant", + "detail": "_LTP_RATE" + }, + { + "file": "mcp_server/core/synaptic_plasticity_hebbian.py", + "kind": "unsourced-constant", + "detail": "_STDP_A_MINUS" + }, + { + "file": "mcp_server/core/synaptic_plasticity_hebbian.py", + "kind": "unsourced-constant", + "detail": "_STDP_A_PLUS" + }, + { + "file": "mcp_server/core/synaptic_plasticity_hebbian.py", + "kind": "unsourced-constant", + "detail": "_STDP_TAU_MINUS" + }, + { + "file": "mcp_server/core/synaptic_plasticity_hebbian.py", + "kind": "unsourced-constant", + "detail": "_STDP_TAU_PLUS" + }, + { + "file": "mcp_server/core/synaptic_plasticity_stochastic.py", + "kind": "method-size", + "detail": "_stochastic_single" + }, + { + "file": "mcp_server/core/synaptic_plasticity_stp.py", + "kind": "unsourced-constant", + "detail": "_MIN_WEIGHT" + }, + { + "file": "mcp_server/core/synaptic_plasticity_stp.py", + "kind": "unsourced-constant", + "detail": "_NOISE_SCALE" + }, + { + "file": "mcp_server/core/synaptic_plasticity_stp.py", + "kind": "unsourced-constant", + "detail": "_TAU_F_HOURS" + }, + { + "file": "mcp_server/core/synaptic_plasticity_stp.py", + "kind": "unsourced-constant", + "detail": "_U_INCREMENT" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "method-size", + "detail": "_score_candidate" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "method-size", + "detail": "apply_synaptic_tags" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "method-size", + "detail": "compute_tag_boosts" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "method-size", + "detail": "find_tagging_candidates" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "unsourced-constant", + "detail": "_BISTABLE_THRESHOLD" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_HEAT_BOOST" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_IMPORTANCE_BOOST" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_MAX_PROMOTIONS" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_MAX_WEAK_IMPORTANCE" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_MIN_OVERLAP" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_TAG_WINDOW_HOURS" + }, + { + "file": "mcp_server/core/synaptic_tagging.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_TRIGGER_IMPORTANCE" + }, + { + "file": "mcp_server/core/telemetry.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/telemetry.py", + "kind": "layer-violation", + "detail": "pathlib" + }, + { + "file": "mcp_server/core/telemetry.py", + "kind": "method-size", + "detail": "record" + }, + { + "file": "mcp_server/core/thermodynamics.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/thermodynamics.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/thermodynamics.py", + "kind": "method-size", + "detail": "compute_decay" + }, + { + "file": "mcp_server/core/thermodynamics.py", + "kind": "method-size", + "detail": "compute_importance" + }, + { + "file": "mcp_server/core/titans_memory.py", + "kind": "layer-violation", + "detail": "numpy" + }, + { + "file": "mcp_server/core/titans_memory.py", + "kind": "layer-violation", + "detail": "torch" + }, + { + "file": "mcp_server/core/titans_memory.py", + "kind": "method-size", + "detail": "TitansMemory.compute_surprise" + }, + { + "file": "mcp_server/core/titans_memory.py", + "kind": "method-size", + "detail": "TitansMemory.update" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "A_2" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "CA_LOW_THRESHOLD" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "CA_MEDIUM_THRESHOLD" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "CA_RESTING_UM" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "C_1" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "DSERINE_LTP_BOOST" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "D_1" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "D_2" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "D_3" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "D_5" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "GLUT_LTD_STRENGTH" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "IP3_PER_EVENT" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "K_ER" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "METABOLIC_BOOST" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "METABOLIC_STARVATION" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "R_C" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "R_L" + }, + { + "file": "mcp_server/core/tripartite_calcium.py", + "kind": "unsourced-constant", + "detail": "V_ER" + }, + { + "file": "mcp_server/core/two_stage_model.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/two_stage_transfer.py", + "kind": "method-size", + "detail": "compute_transfer_delta" + }, + { + "file": "mcp_server/core/two_stage_transfer.py", + "kind": "unsourced-constant", + "detail": "_REPLAY_TRANSFER_RATE" + }, + { + "file": "mcp_server/core/two_stage_transfer.py", + "kind": "unsourced-constant", + "detail": "_SCHEMA_ACCELERATION" + }, + { + "file": "mcp_server/core/unified_search_fusion.py", + "kind": "method-size", + "detail": "fuse" + }, + { + "file": "mcp_server/core/unified_search_fusion.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_K" + }, + { + "file": "mcp_server/core/value_learning.py", + "kind": "method-size", + "detail": "assign_credit" + }, + { + "file": "mcp_server/core/value_learning.py", + "kind": "unsourced-constant", + "detail": "TRACE_LAMBDA" + }, + { + "file": "mcp_server/core/value_learning.py", + "kind": "unsourced-constant", + "detail": "VALUE_ALPHA" + }, + { + "file": "mcp_server/core/value_learning.py", + "kind": "unsourced-constant", + "detail": "VALUE_PRIOR" + }, + { + "file": "mcp_server/core/wiki_axis_defaults.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_axis_registry.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_axis_registry.py", + "kind": "layer-violation", + "detail": "pathlib" + }, + { + "file": "mcp_server/core/wiki_axis_registry.py", + "kind": "method-size", + "detail": "_parse_axis_value_file" + }, + { + "file": "mcp_server/core/wiki_axis_registry.py", + "kind": "method-size", + "detail": "match_axis" + }, + { + "file": "mcp_server/core/wiki_classifier.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_classifier.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/core/wiki_classifier.py", + "kind": "method-size", + "detail": "_classify_to_legacy_kind" + }, + { + "file": "mcp_server/core/wiki_classifier.py", + "kind": "method-size", + "detail": "classify_memory" + }, + { + "file": "mcp_server/core/wiki_classifier_gates.py", + "kind": "method-size", + "detail": "positive_score" + }, + { + "file": "mcp_server/core/wiki_classifier_gates.py", + "kind": "unsourced-constant", + "detail": "_ATOMIC_SCOPE_MAX_CHARS" + }, + { + "file": "mcp_server/core/wiki_classifier_gates.py", + "kind": "unsourced-constant", + "detail": "_ATOMIC_SCOPE_MIN_CHARS" + }, + { + "file": "mcp_server/core/wiki_classifier_patterns.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_coverage.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_coverage.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/wiki_coverage.py", + "kind": "method-size", + "detail": "_index_wiki_file_references" + }, + { + "file": "mcp_server/core/wiki_coverage.py", + "kind": "method-size", + "detail": "audit_domain" + }, + { + "file": "mcp_server/core/wiki_coverage.py", + "kind": "method-size", + "detail": "audit_files" + }, + { + "file": "mcp_server/core/wiki_coverage.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_MAX_AGE_DAYS" + }, + { + "file": "mcp_server/core/wiki_coverage.py", + "kind": "unsourced-constant", + "detail": "_MIN_PAGE_BYTES" + }, + { + "file": "mcp_server/core/wiki_coverage_dashboard.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_coverage_dashboard.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/core/wiki_coverage_dashboard.py", + "kind": "layer-violation", + "detail": "pathlib" + }, + { + "file": "mcp_server/core/wiki_coverage_dashboard.py", + "kind": "method-size", + "detail": "render_dashboard" + }, + { + "file": "mcp_server/core/wiki_coverage_dashboard.py", + "kind": "method-size", + "detail": "write_dashboards" + }, + { + "file": "mcp_server/core/wiki_curation_gaps.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_drift.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_drift.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/wiki_drift.py", + "kind": "method-size", + "detail": "audit_page_drift" + }, + { + "file": "mcp_server/core/wiki_drift.py", + "kind": "method-size", + "detail": "audit_wiki_drift" + }, + { + "file": "mcp_server/core/wiki_drift.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_REAUTHOR_AGE_DAYS" + }, + { + "file": "mcp_server/core/wiki_file_doc_skeleton.py", + "kind": "layer-violation", + "detail": "os" + }, + { + "file": "mcp_server/core/wiki_file_doc_skeleton.py", + "kind": "method-size", + "detail": "build_file_doc" + }, + { + "file": "mcp_server/core/wiki_groomer.py", + "kind": "layer-violation", + "detail": "pathlib" + }, + { + "file": "mcp_server/core/wiki_groomer.py", + "kind": "method-size", + "detail": "audit_page" + }, + { + "file": "mcp_server/core/wiki_redirect.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_redirect.py", + "kind": "method-size", + "detail": "build_redirect_stub" + }, + { + "file": "mcp_server/core/wiki_redirect.py", + "kind": "method-size", + "detail": "parse_frontmatter" + }, + { + "file": "mcp_server/core/wiki_redirect.py", + "kind": "method-size", + "detail": "resolve_chain" + }, + { + "file": "mcp_server/core/wiki_redirect.py", + "kind": "unsourced-constant", + "detail": "MAX_REDIRECT_DEPTH" + }, + { + "file": "mcp_server/core/wiki_rule_engine.py", + "kind": "method-size", + "detail": "apply_rules" + }, + { + "file": "mcp_server/core/wiki_staleness.py", + "kind": "method-size", + "detail": "evaluate_staleness" + }, + { + "file": "mcp_server/core/wiki_staleness.py", + "kind": "unsourced-constant", + "detail": "STALE_THRESHOLD" + }, + { + "file": "mcp_server/core/wiki_stub_detector.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_SHALLOW_THRESHOLD" + }, + { + "file": "mcp_server/core/wiki_stub_detector.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_STUB_THRESHOLD" + }, + { + "file": "mcp_server/core/wiki_symbol_verify.py", + "kind": "method-size", + "detail": "evaluate_symbol_staleness" + }, + { + "file": "mcp_server/core/wiki_symbol_verify.py", + "kind": "unsourced-constant", + "detail": "MIN_SYMBOL_REFS" + }, + { + "file": "mcp_server/core/wiki_symbol_verify.py", + "kind": "unsourced-constant", + "detail": "STALE_THRESHOLD" + }, + { + "file": "mcp_server/core/wiki_sync.py", + "kind": "method-size", + "detail": "build_from_memory" + }, + { + "file": "mcp_server/core/wiki_sync.py", + "kind": "unsourced-constant", + "detail": "_TITLE_MAX_LEN" + }, + { + "file": "mcp_server/core/wiki_templates.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_thermodynamics.py", + "kind": "method-size", + "detail": "transition_lifecycle" + }, + { + "file": "mcp_server/core/wiki_thermodynamics.py", + "kind": "unsourced-constant", + "detail": "ACTIVE_TO_AREA_IDLE_DAYS" + }, + { + "file": "mcp_server/core/wiki_thermodynamics.py", + "kind": "unsourced-constant", + "detail": "ARCHIVED_REVIVAL_HEAT" + }, + { + "file": "mcp_server/core/wiki_thermodynamics.py", + "kind": "unsourced-constant", + "detail": "AREA_TO_ARCHIVED_HEAT" + }, + { + "file": "mcp_server/core/wiki_thermodynamics.py", + "kind": "unsourced-constant", + "detail": "AREA_TO_ARCHIVED_IDLE_DAYS" + }, + { + "file": "mcp_server/core/wiki_title.py", + "kind": "method-size", + "detail": "derive_title" + }, + { + "file": "mcp_server/core/wiki_view_executor.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/wiki_view_executor.py", + "kind": "method-size", + "detail": "_parse_yamlish" + }, + { + "file": "mcp_server/core/wiki_view_executor.py", + "kind": "method-size", + "detail": "compile_view" + }, + { + "file": "mcp_server/core/wiki_view_executor.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_LIMIT" + }, + { + "file": "mcp_server/core/wiki_view_executor.py", + "kind": "unsourced-constant", + "detail": "_MAX_LIMIT" + }, + { + "file": "mcp_server/core/wiki_view_executor.py", + "kind": "unsourced-constant", + "detail": "_MAX_LINE_LEN" + }, + { + "file": "mcp_server/core/write_gate.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/write_gate.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/core/write_gate.py", + "kind": "method-size", + "detail": "apply_goal_maintenance" + }, + { + "file": "mcp_server/core/write_gate.py", + "kind": "method-size", + "detail": "apply_habituation" + }, + { + "file": "mcp_server/core/write_gate.py", + "kind": "method-size", + "detail": "determine_bypass" + }, + { + "file": "mcp_server/core/write_gate_calibration.py", + "kind": "method-size", + "detail": "observe_gate_decision" + }, + { + "file": "mcp_server/core/write_gate_calibration.py", + "kind": "unsourced-constant", + "detail": "ADJUSTMENT_STEP" + }, + { + "file": "mcp_server/core/write_gate_calibration.py", + "kind": "unsourced-constant", + "detail": "EMA_DECAY" + }, + { + "file": "mcp_server/core/write_gate_calibration.py", + "kind": "unsourced-constant", + "detail": "MAX_THRESHOLD" + }, + { + "file": "mcp_server/core/write_gate_calibration.py", + "kind": "unsourced-constant", + "detail": "MIN_SAMPLES_BEFORE_ADJUST" + }, + { + "file": "mcp_server/core/write_gate_calibration.py", + "kind": "unsourced-constant", + "detail": "MIN_THRESHOLD" + }, + { + "file": "mcp_server/core/write_gate_calibration.py", + "kind": "unsourced-constant", + "detail": "TARGET_ACCEPTANCE_RATE" + }, + { + "file": "mcp_server/core/write_gate_calibration.py", + "kind": "unsourced-constant", + "detail": "TOLERANCE_BAND" + }, + { + "file": "mcp_server/core/write_post_store.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/core/write_post_store.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/core/write_post_store.py", + "kind": "method-size", + "detail": "_find_shared_entities" + }, + { + "file": "mcp_server/doctor.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/doctor.py", + "kind": "method-size", + "detail": "_codebase_pipeline" + }, + { + "file": "mcp_server/doctor.py", + "kind": "method-size", + "detail": "_run_full_check" + }, + { + "file": "mcp_server/doctor_mcp.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/doctor_mcp.py", + "kind": "method-size", + "detail": "_check_cortex_plugin_entry" + }, + { + "file": "mcp_server/doctor_mcp.py", + "kind": "method-size", + "detail": "_check_installed_plugins_json" + }, + { + "file": "mcp_server/doctor_mcp.py", + "kind": "method-size", + "detail": "_check_launcher_smoke" + }, + { + "file": "mcp_server/doctor_mcp.py", + "kind": "method-size", + "detail": "_check_pg_extensions" + }, + { + "file": "mcp_server/doctor_mcp.py", + "kind": "method-size", + "detail": "_check_pg_reachable" + }, + { + "file": "mcp_server/doctor_mcp.py", + "kind": "method-size", + "detail": "_check_python_interpreter" + }, + { + "file": "mcp_server/doctor_mcp.py", + "kind": "method-size", + "detail": "_print_human" + }, + { + "file": "mcp_server/errors/__init__.py", + "kind": "layer-violation", + "detail": "__future__" + }, + { + "file": "mcp_server/handlers/anchor.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/assess_coverage.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/assess_coverage.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/assess_coverage.py", + "kind": "unsourced-constant", + "detail": "_MIN_BALANCE_SCORE" + }, + { + "file": "mcp_server/handlers/assess_coverage.py", + "kind": "unsourced-constant", + "detail": "_MIN_ENTITY_DENSITY" + }, + { + "file": "mcp_server/handlers/auto_task_record_writer.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/auto_task_record_writer.py", + "kind": "method-size", + "detail": "_git_commits_in_window" + }, + { + "file": "mcp_server/handlers/auto_task_record_writer.py", + "kind": "method-size", + "detail": "maybe_write_task_record" + }, + { + "file": "mcp_server/handlers/backfill_helpers.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/backfill_memories.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/backfill_memories.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/backfill_memories.py", + "kind": "method-size", + "detail": "_process_imports" + }, + { + "file": "mcp_server/handlers/backfill_memories.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/change_impact.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/change_impact.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/change_impact.py", + "kind": "unsourced-constant", + "detail": "_IMPACT_BOOST" + }, + { + "file": "mcp_server/handlers/change_impact.py", + "kind": "unsourced-constant", + "detail": "_MAX_HEAT_BUMPS" + }, + { + "file": "mcp_server/handlers/check_setup.py", + "kind": "layer-violation", + "detail": "mcp_server.doctor" + }, + { + "file": "mcp_server/handlers/checkpoint.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/checkpoint.py", + "kind": "method-size", + "detail": "_restore_context" + }, + { + "file": "mcp_server/handlers/codebase_analyze.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/codebase_analyze.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/codebase_analyze.py", + "kind": "method-size", + "detail": "_process_files" + }, + { + "file": "mcp_server/handlers/codebase_analyze.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/codebase_analyze_helpers.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/codebase_analyze_helpers.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/codebase_analyze_helpers.py", + "kind": "unsourced-constant", + "detail": "CANDIDATE_MULTIPLIER" + }, + { + "file": "mcp_server/handlers/consolidate.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/consolidate.py", + "kind": "layer-violation", + "detail": "mcp_server.hooks.consolidate_background" + }, + { + "file": "mcp_server/handlers/consolidate.py", + "kind": "method-size", + "detail": "_run_cycles" + }, + { + "file": "mcp_server/handlers/consolidate.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/consolidation/authoring_prompts.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/consolidation/authoring_prompts.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/consolidation/authoring_prompts.py", + "kind": "method-size", + "detail": "_build_page_prompt" + }, + { + "file": "mcp_server/handlers/consolidation/authoring_prompts.py", + "kind": "method-size", + "detail": "_build_section_prompt" + }, + { + "file": "mcp_server/handlers/consolidation/candidate_scan.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/consolidation/cascade.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/consolidation/cascade.py", + "kind": "method-size", + "detail": "_try_advance" + }, + { + "file": "mcp_server/handlers/consolidation/cascade.py", + "kind": "method-size", + "detail": "run_cascade_advancement" + }, + { + "file": "mcp_server/handlers/consolidation/cascade.py", + "kind": "unsourced-constant", + "detail": "_TRANSITION_PREVIEW_CAP" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "method-size", + "detail": "_classify_cls_zero_reason" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "method-size", + "detail": "_create_semantic_memories" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "method-size", + "detail": "_discover_causal_edges" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "method-size", + "detail": "run_cls_cycle" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "unsourced-constant", + "detail": "_CLUSTER_THRESHOLD" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "unsourced-constant", + "detail": "_EPISODIC_SAMPLE_CAP" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "unsourced-constant", + "detail": "_MIN_ENTITIES_FOR_PC" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "unsourced-constant", + "detail": "_MIN_PATTERN_SIZE" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "unsourced-constant", + "detail": "_PC_MIN_OBSERVATIONS" + }, + { + "file": "mcp_server/handlers/consolidation/cls.py", + "kind": "unsourced-constant", + "detail": "_SEMANTICS_SAMPLE_CAP" + }, + { + "file": "mcp_server/handlers/consolidation/distill_drain.py", + "kind": "method-size", + "detail": "run_distill_drain_cycle" + }, + { + "file": "mcp_server/handlers/consolidation/drain_operations.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/consolidation/forgetting.py", + "kind": "method-size", + "detail": "_evaluate_memory" + }, + { + "file": "mcp_server/handlers/consolidation/headless_authoring.py", + "kind": "unsourced-constant", + "detail": "CLAUDE_CALL_TIMEOUT_SEC" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic.py", + "kind": "method-size", + "detail": "_dispatch_class" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic.py", + "kind": "method-size", + "detail": "_streaming_health_by_class" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic.py", + "kind": "method-size", + "detail": "run_homeostatic_cycle" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic_apply.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic_apply.py", + "kind": "method-size", + "detail": "_apply_fold" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic_apply.py", + "kind": "method-size", + "detail": "apply_cohort" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic_apply.py", + "kind": "method-size", + "detail": "apply_scalar" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic_apply.py", + "kind": "unsourced-constant", + "detail": "TARGET_HEAT" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic_apply.py", + "kind": "unsourced-constant", + "detail": "_MAX_STEP" + }, + { + "file": "mcp_server/handlers/consolidation/homeostatic_apply.py", + "kind": "unsourced-constant", + "detail": "_MIN_SAFE_MEAN" + }, + { + "file": "mcp_server/handlers/consolidation/memify.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/consolidation/memify.py", + "kind": "method-size", + "detail": "_classify_memify_reason" + }, + { + "file": "mcp_server/handlers/consolidation/memify.py", + "kind": "unsourced-constant", + "detail": "_PRUNE_CONFIDENCE_THRESHOLD" + }, + { + "file": "mcp_server/handlers/consolidation/memify.py", + "kind": "unsourced-constant", + "detail": "_PRUNE_HEAT_THRESHOLD" + }, + { + "file": "mcp_server/handlers/consolidation/memify.py", + "kind": "unsourced-constant", + "detail": "_STRENGTHEN_MIN_ACCESS" + }, + { + "file": "mcp_server/handlers/consolidation/memify.py", + "kind": "unsourced-constant", + "detail": "_STRENGTHEN_MIN_CONFIDENCE" + }, + { + "file": "mcp_server/handlers/consolidation/memify_derive.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/consolidation/memify_derive.py", + "kind": "method-size", + "detail": "run_memify_derivation_cycle" + }, + { + "file": "mcp_server/handlers/consolidation/memify_derive.py", + "kind": "unsourced-constant", + "detail": "_CANDIDATE_SCAN_LIMIT" + }, + { + "file": "mcp_server/handlers/consolidation/memify_derive.py", + "kind": "unsourced-constant", + "detail": "_MAX_DERIVATIONS_PER_RUN" + }, + { + "file": "mcp_server/handlers/consolidation/memify_derive.py", + "kind": "unsourced-constant", + "detail": "_PROVENANCE_SRC_CAP" + }, + { + "file": "mcp_server/handlers/consolidation/memify_derive.py", + "kind": "unsourced-constant", + "detail": "_WEIGHT_THRESHOLD" + }, + { + "file": "mcp_server/handlers/consolidation/memory_dedup_exact_pass.py", + "kind": "method-size", + "detail": "run_memory_dedup_exact_pass" + }, + { + "file": "mcp_server/handlers/consolidation/memory_dedup_exact_pass.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_DEDUP_SCAN_LIMIT" + }, + { + "file": "mcp_server/handlers/consolidation/memory_domain_backfill_pass.py", + "kind": "method-size", + "detail": "run_memory_domain_backfill_pass" + }, + { + "file": "mcp_server/handlers/consolidation/memory_domain_backfill_pass.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_MEMORY_DOMAIN_BACKFILL_LIMIT" + }, + { + "file": "mcp_server/handlers/consolidation/memory_reheat_pass.py", + "kind": "method-size", + "detail": "_process_row" + }, + { + "file": "mcp_server/handlers/consolidation/memory_reheat_pass.py", + "kind": "method-size", + "detail": "run_memory_reheat_pass" + }, + { + "file": "mcp_server/handlers/consolidation/memory_reheat_pass.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_REHEAT_SCAN_LIMIT" + }, + { + "file": "mcp_server/handlers/consolidation/near_dup_calibration_pass.py", + "kind": "method-size", + "detail": "_elect_and_journal_component" + }, + { + "file": "mcp_server/handlers/consolidation/near_dup_calibration_pass.py", + "kind": "method-size", + "detail": "run_near_dup_apply_pass" + }, + { + "file": "mcp_server/handlers/consolidation/near_dup_calibration_pass.py", + "kind": "method-size", + "detail": "run_near_dup_sample" + }, + { + "file": "mcp_server/handlers/consolidation/near_dup_calibration_pass.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_PER_STRATUM" + }, + { + "file": "mcp_server/handlers/consolidation/page_io.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/consolidation/page_io.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/consolidation/page_io.py", + "kind": "method-size", + "detail": "_compute_rewritten_page" + }, + { + "file": "mcp_server/handlers/consolidation/page_io.py", + "kind": "method-size", + "detail": "_parse_frontmatter" + }, + { + "file": "mcp_server/handlers/consolidation/page_io.py", + "kind": "method-size", + "detail": "_rewrite_page" + }, + { + "file": "mcp_server/handlers/consolidation/page_io.py", + "kind": "method-size", + "detail": "_scope_anchor_prompt" + }, + { + "file": "mcp_server/handlers/consolidation/page_io.py", + "kind": "method-size", + "detail": "_write_anchor_page" + }, + { + "file": "mcp_server/handlers/consolidation/page_io.py", + "kind": "unsourced-constant", + "detail": "_CONTEXT_BYTES_CAP" + }, + { + "file": "mcp_server/handlers/consolidation/plasticity.py", + "kind": "method-size", + "detail": "run_plasticity_cycle" + }, + { + "file": "mcp_server/handlers/consolidation/plasticity.py", + "kind": "unsourced-constant", + "detail": "_CO_ACCESS_SAMPLE_CAP" + }, + { + "file": "mcp_server/handlers/consolidation/sleep.py", + "kind": "method-size", + "detail": "run_deep_sleep" + }, + { + "file": "mcp_server/handlers/consolidation/wiki_backlog_pass.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/consolidation/wiki_backlog_pass.py", + "kind": "method-size", + "detail": "run_backlog_pass" + }, + { + "file": "mcp_server/handlers/consolidation/wiki_citation_seed_pass.py", + "kind": "method-size", + "detail": "run_wiki_citation_seed_pass" + }, + { + "file": "mcp_server/handlers/consolidation/wiki_citation_seed_pass.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_SEED_SCAN_LIMIT" + }, + { + "file": "mcp_server/handlers/consolidation/wiki_domain_backfill_pass.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_DOMAIN_BACKFILL_LIMIT" + }, + { + "file": "mcp_server/handlers/consolidation/wiki_maintenance.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/consolidation/wiki_maintenance.py", + "kind": "method-size", + "detail": "run_wiki_maintenance" + }, + { + "file": "mcp_server/handlers/consolidation/wiki_maintenance.py", + "kind": "unsourced-constant", + "detail": "MAX_PURGES_PER_CYCLE" + }, + { + "file": "mcp_server/handlers/consolidation/wiki_source_backfill_pass.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_BACKFILL_LIMIT" + }, + { + "file": "mcp_server/handlers/consolidation/write_class_backfill_pass.py", + "kind": "method-size", + "detail": "run_write_class_backfill_pass" + }, + { + "file": "mcp_server/handlers/consolidation/write_class_backfill_pass.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_WRITE_CLASS_BACKFILL_LIMIT" + }, + { + "file": "mcp_server/handlers/create_trigger.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/curate_distill.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/curate_distill.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/curate_distill.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/curate_distill.py", + "kind": "unsourced-constant", + "detail": "_CO_ACCESS_SCAN_LIMIT" + }, + { + "file": "mcp_server/handlers/curate_distill.py", + "kind": "unsourced-constant", + "detail": "_MEMORY_PREVIEW_CAP" + }, + { + "file": "mcp_server/handlers/curate_distill.py", + "kind": "unsourced-constant", + "detail": "_MIN_RECURRING_ACCESS" + }, + { + "file": "mcp_server/handlers/curate_distill.py", + "kind": "unsourced-constant", + "detail": "_TAG_SCAN_LIMIT" + }, + { + "file": "mcp_server/handlers/curate_wiki.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/curate_wiki.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/curate_wiki_serialize.py", + "kind": "method-size", + "detail": "instructions_for_llm" + }, + { + "file": "mcp_server/handlers/curate_wiki_uncited.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/detect_gaps.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/explore_features.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/forget.py", + "kind": "method-size", + "detail": "_handler_impl" + }, + { + "file": "mcp_server/handlers/get_causal_chain.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/get_grooming_health.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/import_sessions.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/import_sessions.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/import_sessions.py", + "kind": "unsourced-constant", + "detail": "_PREVIEW_ITEMS_CAP" + }, + { + "file": "mcp_server/handlers/ingest_codebase.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/ingest_codebase.py", + "kind": "method-size", + "detail": "_ingest_edges" + }, + { + "file": "mcp_server/handlers/ingest_codebase.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/ingest_codebase.py", + "kind": "unsourced-constant", + "detail": "_PROCESS_SYMBOLS_LIMIT" + }, + { + "file": "mcp_server/handlers/ingest_codebase_cypher.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/ingest_codebase_cypher.py", + "kind": "method-size", + "detail": "_run_query" + }, + { + "file": "mcp_server/handlers/ingest_codebase_cypher.py", + "kind": "method-size", + "detail": "fetch_files" + }, + { + "file": "mcp_server/handlers/ingest_codebase_cypher.py", + "kind": "method-size", + "detail": "fetch_symbols_page" + }, + { + "file": "mcp_server/handlers/ingest_codebase_cypher.py", + "kind": "method-size", + "detail": "file_path_from_qn" + }, + { + "file": "mcp_server/handlers/ingest_codebase_cypher.py", + "kind": "method-size", + "detail": "iter_call_edges" + }, + { + "file": "mcp_server/handlers/ingest_codebase_cypher.py", + "kind": "unsourced-constant", + "detail": "_FILE_PAGE_SIZE" + }, + { + "file": "mcp_server/handlers/ingest_codebase_graph.py", + "kind": "method-size", + "detail": "ensure_graph" + }, + { + "file": "mcp_server/handlers/ingest_codebase_writers.py", + "kind": "unsourced-constant", + "detail": "_FILE_HEAT" + }, + { + "file": "mcp_server/handlers/ingest_codebase_writers.py", + "kind": "unsourced-constant", + "detail": "_SYMBOL_HEAT" + }, + { + "file": "mcp_server/handlers/ingest_docs_content.py", + "kind": "method-size", + "detail": "run_docs_pass" + }, + { + "file": "mcp_server/handlers/ingest_docs_content_cypher.py", + "kind": "method-size", + "detail": "fetch_doc_references" + }, + { + "file": "mcp_server/handlers/ingest_docs_content_writers.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/ingest_docs_content_writers.py", + "kind": "method-size", + "detail": "write_doc_memory" + }, + { + "file": "mcp_server/handlers/ingest_document.py", + "kind": "method-size", + "detail": "_persist" + }, + { + "file": "mcp_server/handlers/ingest_document_writers.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/ingest_document_writers.py", + "kind": "method-size", + "detail": "write_document_memories" + }, + { + "file": "mcp_server/handlers/ingest_findings.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/ingest_findings_resolve.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/ingest_findings_writers.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/ingest_findings_writers.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/ingest_findings_writers.py", + "kind": "method-size", + "detail": "write_finding_memory" + }, + { + "file": "mcp_server/handlers/ingest_findings_writers.py", + "kind": "method-size", + "detail": "write_finding_page" + }, + { + "file": "mcp_server/handlers/ingest_findings_writers.py", + "kind": "method-size", + "detail": "write_receipt_memos" + }, + { + "file": "mcp_server/handlers/ingest_helpers.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/ingest_helpers.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/ingest_helpers.py", + "kind": "method-size", + "detail": "find_cached_graph" + }, + { + "file": "mcp_server/handlers/ingest_prd.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/ingest_prd.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/lesson_promotion.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/lesson_promotion.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/procedural_skill_writer.py", + "kind": "method-size", + "detail": "maybe_mine_skills" + }, + { + "file": "mcp_server/handlers/procedural_skill_writer.py", + "kind": "unsourced-constant", + "detail": "_MAX_SESSIONS_FOR_MINING" + }, + { + "file": "mcp_server/handlers/query_methodology.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/query_methodology.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/rate_memory.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/rate_memory.py", + "kind": "method-size", + "detail": "_handler_impl" + }, + { + "file": "mcp_server/handlers/rebuild_profiles.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/recall.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/recall.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/recall.py", + "kind": "method-size", + "detail": "_handler_impl" + }, + { + "file": "mcp_server/handlers/recall_helpers.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/recall_helpers.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/recall_helpers.py", + "kind": "method-size", + "detail": "inject_triggered_memories" + }, + { + "file": "mcp_server/handlers/recall_helpers.py", + "kind": "unsourced-constant", + "detail": "_GIST_CHARS" + }, + { + "file": "mcp_server/handlers/recall_hierarchical.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/recall_hierarchical.py", + "kind": "method-size", + "detail": "_handler_impl" + }, + { + "file": "mcp_server/handlers/record_session_end.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/record_session_end.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/remember.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/remember.py", + "kind": "method-size", + "detail": "_handler_impl" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "method-size", + "detail": "_build_insert_record" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "method-size", + "detail": "_grade_content_best_effort" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "method-size", + "detail": "compute_template_normalized_similarities" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "method-size", + "detail": "evaluate_gate" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "method-size", + "detail": "insert_and_post_process" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "method-size", + "detail": "try_block_replica_upsert" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "method-size", + "detail": "try_curation" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "method-size", + "detail": "update_user_mood_ema" + }, + { + "file": "mcp_server/handlers/remember_helpers.py", + "kind": "unsourced-constant", + "detail": "MOOD_EMA_ALPHA" + }, + { + "file": "mcp_server/handlers/remember_response.py", + "kind": "method-size", + "detail": "build_response" + }, + { + "file": "mcp_server/handlers/remember_schema.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/replay_tracking.py", + "kind": "method-size", + "detail": "track_replay_event" + }, + { + "file": "mcp_server/handlers/seed_project.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/sync_instructions.py", + "kind": "method-size", + "detail": "_update_claude_md" + }, + { + "file": "mcp_server/handlers/unified_search.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/validate_memory.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/validate_memory.py", + "kind": "method-size", + "detail": "_grade_memories" + }, + { + "file": "mcp_server/handlers/validate_memory.py", + "kind": "method-size", + "detail": "_handler_impl" + }, + { + "file": "mcp_server/handlers/validate_memory.py", + "kind": "method-size", + "detail": "grade_from_content" + }, + { + "file": "mcp_server/handlers/validate_memory.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_URL_CHECK_LIMIT" + }, + { + "file": "mcp_server/handlers/validate_memory.py", + "kind": "unsourced-constant", + "detail": "_HTTP_REACHABLE_END" + }, + { + "file": "mcp_server/handlers/validate_memory.py", + "kind": "unsourced-constant", + "detail": "_URL_CHECK_TIMEOUT_S" + }, + { + "file": "mcp_server/handlers/why.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/why.py", + "kind": "unsourced-constant", + "detail": "_INT4_MAX" + }, + { + "file": "mcp_server/handlers/why.py", + "kind": "unsourced-constant", + "detail": "_MAX_RECEIPT_IDS" + }, + { + "file": "mcp_server/handlers/wiki_adr.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/wiki_adr.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_compile.py", + "kind": "method-size", + "detail": "_publish_one" + }, + { + "file": "mcp_server/handlers/wiki_consolidate.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_consolidate_staleness.py", + "kind": "method-size", + "detail": "run_staleness_pass" + }, + { + "file": "mcp_server/handlers/wiki_curate.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_emerge.py", + "kind": "method-size", + "detail": "_persist_plan" + }, + { + "file": "mcp_server/handlers/wiki_emerge.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_extract.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_list.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_memory_sync.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/wiki_migrate.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/wiki_migrate.py", + "kind": "method-size", + "detail": "migrate_wiki" + }, + { + "file": "mcp_server/handlers/wiki_migrate.py", + "kind": "method-size", + "detail": "page_row_from_md" + }, + { + "file": "mcp_server/handlers/wiki_purge.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/wiki_purge.py", + "kind": "method-size", + "detail": "_evaluate_page" + }, + { + "file": "mcp_server/handlers/wiki_purge.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_read.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_refine.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/wiki_refine.py", + "kind": "method-size", + "detail": "handler_get" + }, + { + "file": "mcp_server/handlers/wiki_refine.py", + "kind": "method-size", + "detail": "handler_refine" + }, + { + "file": "mcp_server/handlers/wiki_reindex.py", + "kind": "method-size", + "detail": "_render_index" + }, + { + "file": "mcp_server/handlers/wiki_reindex.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_rename.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_resolve.py", + "kind": "method-size", + "detail": "_fetch_claims" + }, + { + "file": "mcp_server/handlers/wiki_resolve.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_seed_codebase.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_synthesize.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/wiki_synthesize.py", + "kind": "method-size", + "detail": "handler" + }, + { + "file": "mcp_server/handlers/wiki_write.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/handlers/wiki_write.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/handlers/wiki_write.py", + "kind": "method-size", + "detail": "_sync_page_and_cite" + }, + { + "file": "mcp_server/handlers/wiki_write.py", + "kind": "method-size", + "detail": "write_governed_page" + }, + { + "file": "mcp_server/hooks/agent_briefing.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.injection_receipts" + }, + { + "file": "mcp_server/hooks/agent_briefing.py", + "kind": "method-size", + "detail": "process_event" + }, + { + "file": "mcp_server/hooks/agent_briefing_query.py", + "kind": "method-size", + "detail": "_fetch_agent_context" + }, + { + "file": "mcp_server/hooks/agent_briefing_query.py", + "kind": "unsourced-constant", + "detail": "_MAX_MEMORIES" + }, + { + "file": "mcp_server/hooks/agent_briefing_query.py", + "kind": "unsourced-constant", + "detail": "_MIN_HEAT" + }, + { + "file": "mcp_server/hooks/auto_recall.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/hooks/auto_recall.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.injection_receipts" + }, + { + "file": "mcp_server/hooks/auto_recall.py", + "kind": "method-size", + "detail": "_recall_memories" + }, + { + "file": "mcp_server/hooks/auto_recall.py", + "kind": "method-size", + "detail": "process_event" + }, + { + "file": "mcp_server/hooks/auto_recall.py", + "kind": "unsourced-constant", + "detail": "_MAX_INJECTION_CHARS" + }, + { + "file": "mcp_server/hooks/auto_recall.py", + "kind": "unsourced-constant", + "detail": "_MAX_MEMORIES" + }, + { + "file": "mcp_server/hooks/auto_recall.py", + "kind": "unsourced-constant", + "detail": "_MAX_MEMORY_CHARS" + }, + { + "file": "mcp_server/hooks/auto_recall.py", + "kind": "unsourced-constant", + "detail": "_MIN_HEAT" + }, + { + "file": "mcp_server/hooks/auto_recall.py", + "kind": "unsourced-constant", + "detail": "_MIN_QUERY_LENGTH" + }, + { + "file": "mcp_server/hooks/compaction_checkpoint.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.checkpoint" + }, + { + "file": "mcp_server/hooks/compaction_checkpoint.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.consolidation.cascade" + }, + { + "file": "mcp_server/hooks/compaction_checkpoint.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.injection_receipts" + }, + { + "file": "mcp_server/hooks/compaction_checkpoint.py", + "kind": "method-size", + "detail": "process_event" + }, + { + "file": "mcp_server/hooks/consolidate_background.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.consolidate" + }, + { + "file": "mcp_server/hooks/consolidate_background.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "mcp_server/hooks/ingest_codebase_background.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.ingest_codebase" + }, + { + "file": "mcp_server/hooks/ingest_codebase_background.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "mcp_server/hooks/pipeline_impact_bump.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.ingest_helpers" + }, + { + "file": "mcp_server/hooks/pipeline_impact_bump.py", + "kind": "method-size", + "detail": "_pipeline_detect_changes" + }, + { + "file": "mcp_server/hooks/pipeline_impact_bump.py", + "kind": "unsourced-constant", + "detail": "_COOLDOWN_SECONDS" + }, + { + "file": "mcp_server/hooks/pipeline_impact_bump.py", + "kind": "unsourced-constant", + "detail": "_IMPACT_BOOST" + }, + { + "file": "mcp_server/hooks/pipeline_impact_bump.py", + "kind": "unsourced-constant", + "detail": "_MAX_BUMPS" + }, + { + "file": "mcp_server/hooks/post_commit_reindex.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/hooks/post_tool_capture.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/hooks/post_tool_capture.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.consolidation.cascade" + }, + { + "file": "mcp_server/hooks/post_tool_capture.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.remember" + }, + { + "file": "mcp_server/hooks/post_tool_capture.py", + "kind": "method-size", + "detail": "_normalize_output" + }, + { + "file": "mcp_server/hooks/post_tool_capture.py", + "kind": "unsourced-constant", + "detail": "_CASCADE_INTERVAL" + }, + { + "file": "mcp_server/hooks/post_tool_capture.py", + "kind": "unsourced-constant", + "detail": "_MIN_OUTPUT_LENGTH" + }, + { + "file": "mcp_server/hooks/preemptive_context.py", + "kind": "method-size", + "detail": "_prime_file_memories" + }, + { + "file": "mcp_server/hooks/preemptive_context.py", + "kind": "unsourced-constant", + "detail": "_COOLDOWN_SECONDS" + }, + { + "file": "mcp_server/hooks/preemptive_context.py", + "kind": "unsourced-constant", + "detail": "_HEAT_BOOST" + }, + { + "file": "mcp_server/hooks/session_lifecycle.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/hooks/session_lifecycle.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.consolidate" + }, + { + "file": "mcp_server/hooks/session_lifecycle.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.injection_receipts" + }, + { + "file": "mcp_server/hooks/session_lifecycle.py", + "kind": "method-size", + "detail": "_run_consolidation" + }, + { + "file": "mcp_server/hooks/session_lifecycle.py", + "kind": "unsourced-constant", + "detail": "_LONG_SESSION_TURNS" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.backfill_memories" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.ingest_helpers" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "layer-violation", + "detail": "mcp_server.handlers.injection_receipts" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "_build_cold_start_message" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "_build_context" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "_count_pending_curations" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "_detect_external_sources" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "_fetch_anchors" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "_fetch_grooming_staleness" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "_fetch_hot_memories" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "_fetch_team_decisions" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "_maybe_background_reanalyze" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "_sqlite_context" + }, + { + "file": "mcp_server/hooks/session_start.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "mcp_server/infrastructure/ap_bridge.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/ap_bridge.py", + "kind": "layer-violation", + "detail": "mcp_server.errors" + }, + { + "file": "mcp_server/infrastructure/ap_bridge.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/infrastructure/ap_bridge.py", + "kind": "method-size", + "detail": "APBridge.connect" + }, + { + "file": "mcp_server/infrastructure/ap_bridge.py", + "kind": "method-size", + "detail": "_resolve_command" + }, + { + "file": "mcp_server/infrastructure/ap_sync_loop.py", + "kind": "layer-violation", + "detail": "mcp_server.errors" + }, + { + "file": "mcp_server/infrastructure/artifact_gc.py", + "kind": "method-size", + "detail": "delete_artifact_if_unreferenced" + }, + { + "file": "mcp_server/infrastructure/document_reader.py", + "kind": "unsourced-constant", + "detail": "MAX_DOCUMENT_BYTES" + }, + { + "file": "mcp_server/infrastructure/groomer_coordinator.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/groomer_coordinator.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/infrastructure/mcp_call_timeout.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_CALL_TIMEOUT_S" + }, + { + "file": "mcp_server/infrastructure/mcp_client.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/mcp_client.py", + "kind": "layer-violation", + "detail": "mcp_server.errors" + }, + { + "file": "mcp_server/infrastructure/mcp_client.py", + "kind": "method-size", + "detail": "MCPClient._read_loop" + }, + { + "file": "mcp_server/infrastructure/mcp_client.py", + "kind": "method-size", + "detail": "MCPClient._send" + }, + { + "file": "mcp_server/infrastructure/mcp_client.py", + "kind": "method-size", + "detail": "MCPClient._spawn_process" + }, + { + "file": "mcp_server/infrastructure/mcp_client_pool.py", + "kind": "layer-violation", + "detail": "mcp_server.errors" + }, + { + "file": "mcp_server/infrastructure/memory_store.py", + "kind": "method-size", + "detail": "_construct_store" + }, + { + "file": "mcp_server/infrastructure/pg_schema.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/pg_schema.py", + "kind": "method-size", + "detail": "get_all_ddl" + }, + { + "file": "mcp_server/infrastructure/pg_store_entities.py", + "kind": "method-size", + "detail": "PgEntityMixin.insert_entity" + }, + { + "file": "mcp_server/infrastructure/pg_store_entity_merge.py", + "kind": "method-size", + "detail": "PgEntityMergeMixin.merge_entities" + }, + { + "file": "mcp_server/infrastructure/pg_store_memory_dedup.py", + "kind": "method-size", + "detail": "list_exact_duplicate_groups" + }, + { + "file": "mcp_server/infrastructure/pg_store_memory_dedup.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_DEDUP_SCAN_LIMIT" + }, + { + "file": "mcp_server/infrastructure/pg_store_memory_domain.py", + "kind": "method-size", + "detail": "list_domainless_memories" + }, + { + "file": "mcp_server/infrastructure/pg_store_memory_meta.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/infrastructure/pg_store_memory_reheat.py", + "kind": "method-size", + "detail": "apply_reheat" + }, + { + "file": "mcp_server/infrastructure/pg_store_memory_reheat.py", + "kind": "method-size", + "detail": "list_deliberate_below_target" + }, + { + "file": "mcp_server/infrastructure/pg_store_memory_reheat.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_REHEAT_SCAN_LIMIT" + }, + { + "file": "mcp_server/infrastructure/pg_store_near_dup.py", + "kind": "method-size", + "detail": "fetch_member_stats" + }, + { + "file": "mcp_server/infrastructure/pg_store_near_dup.py", + "kind": "method-size", + "detail": "list_candidate_pairs" + }, + { + "file": "mcp_server/infrastructure/pg_store_near_dup.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_ANCHOR_LIMIT" + }, + { + "file": "mcp_server/infrastructure/pg_store_near_dup.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_TOP_K" + }, + { + "file": "mcp_server/infrastructure/pg_store_relationships.py", + "kind": "method-size", + "detail": "PgRelationshipMixin.reinforce_or_create_relationship" + }, + { + "file": "mcp_server/infrastructure/pg_store_supersede.py", + "kind": "unsourced-constant", + "detail": "_CHAIN_HEAD_MAX_DEPTH" + }, + { + "file": "mcp_server/infrastructure/pg_store_supersede.py", + "kind": "unsourced-constant", + "detail": "_SUPERSEDE_REBASE_ATTEMPTS" + }, + { + "file": "mcp_server/infrastructure/pg_store_wiki_citation_seed.py", + "kind": "unsourced-constant", + "detail": "DEFAULT_SEED_SCAN_LIMIT" + }, + { + "file": "mcp_server/infrastructure/pg_store_wiki_drafts.py", + "kind": "method-size", + "detail": "update_draft" + }, + { + "file": "mcp_server/infrastructure/pg_store_wiki_notes.py", + "kind": "method-size", + "detail": "list_uncited_deliberate_memories" + }, + { + "file": "mcp_server/infrastructure/pg_store_wiki_pages.py", + "kind": "method-size", + "detail": "upsert_page" + }, + { + "file": "mcp_server/infrastructure/pg_store_wiki_sources.py", + "kind": "method-size", + "detail": "upsert_page_sources" + }, + { + "file": "mcp_server/infrastructure/pipeline_discovery.py", + "kind": "method-size", + "detail": "discover_pipeline_command" + }, + { + "file": "mcp_server/infrastructure/pipeline_discovery.py", + "kind": "method-size", + "detail": "ensure_pipeline_connection" + }, + { + "file": "mcp_server/infrastructure/pipeline_graph_ttl.py", + "kind": "unsourced-constant", + "detail": "_DEFAULT_TTL_HOURS" + }, + { + "file": "mcp_server/infrastructure/pipeline_install_release.py", + "kind": "method-size", + "detail": "try_install_prebuilt" + }, + { + "file": "mcp_server/infrastructure/pipeline_install_release.py", + "kind": "unsourced-constant", + "detail": "_REQUEST_TIMEOUT" + }, + { + "file": "mcp_server/infrastructure/pipeline_install_rust.py", + "kind": "method-size", + "detail": "_install_with_hash_pin" + }, + { + "file": "mcp_server/infrastructure/pipeline_install_rust.py", + "kind": "method-size", + "detail": "install_rust_toolchain" + }, + { + "file": "mcp_server/infrastructure/pipeline_installer.py", + "kind": "method-size", + "detail": "_install_locked" + }, + { + "file": "mcp_server/infrastructure/scanner.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/session_registry.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/session_registry.py", + "kind": "method-size", + "detail": "has_active_session_window" + }, + { + "file": "mcp_server/infrastructure/session_registry.py", + "kind": "unsourced-constant", + "detail": "_MAX_ANCESTOR_DEPTH" + }, + { + "file": "mcp_server/infrastructure/sqlite_schema.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/sqlite_sql_translate.py", + "kind": "method-size", + "detail": "_translate_sql" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "method-size", + "detail": "SqliteMemoryStore._insert_memory_rows" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "method-size", + "detail": "SqliteMemoryStore._migrate_heat_to_heat_base" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "method-size", + "detail": "SqliteMemoryStore._migrate_homeostatic_state_write_class" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "method-size", + "detail": "SqliteMemoryStore._normalize_memory_row" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "method-size", + "detail": "SqliteMemoryStore.search_newer_neighbors" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "method-size", + "detail": "SqliteMemoryStore.supersede_atomic" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "method-size", + "detail": "SqliteMemoryStore.update_memory_compression" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "unsourced-constant", + "detail": "_CHAIN_HEAD_MAX_DEPTH" + }, + { + "file": "mcp_server/infrastructure/sqlite_store.py", + "kind": "unsourced-constant", + "detail": "_SUPERSEDE_REBASE_ATTEMPTS" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_auxiliary.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_entity_merge.py", + "kind": "method-size", + "detail": "SqliteEntityMergeMixin.merge_entities" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_queries.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_queries.py", + "kind": "method-size", + "detail": "SqliteQueryMixin.delete_memories_by_tag" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_relationships.py", + "kind": "method-size", + "detail": "SqliteRelationshipMixin.reinforce_or_create_relationship" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_search.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_search.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_search.py", + "kind": "method-size", + "detail": "SqliteSearchMixin._fetch_ranked_results" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_search.py", + "kind": "method-size", + "detail": "SqliteSearchMixin.get_temporal_co_access" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_search.py", + "kind": "method-size", + "detail": "SqliteSearchMixin.recall_memories" + }, + { + "file": "mcp_server/infrastructure/sqlite_store_stats.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/infrastructure/viz_client.py", + "kind": "method-size", + "detail": "fetch_live_graph" + }, + { + "file": "mcp_server/infrastructure/viz_client.py", + "kind": "unsourced-constant", + "detail": "_PAGE_LIMIT" + }, + { + "file": "mcp_server/infrastructure/viz_client.py", + "kind": "unsourced-constant", + "detail": "_TIMEOUT_S" + }, + { + "file": "mcp_server/infrastructure/wiki_reindex_io.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/infrastructure/wiki_schema_reader.py", + "kind": "layer-violation", + "detail": "mcp_server.observability" + }, + { + "file": "mcp_server/migrate.py", + "kind": "method-size", + "detail": "_run" + }, + { + "file": "mcp_server/observability/metrics.py", + "kind": "method-size", + "detail": "render" + }, + { + "file": "mcp_server/shared/algorithmic_embedding.py", + "kind": "layer-violation", + "detail": "numpy" + }, + { + "file": "mcp_server/shared/domain_mapping.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/shared/domain_mapping.py", + "kind": "method-size", + "detail": "_dereference_worktree_gitdir" + }, + { + "file": "mcp_server/shared/domain_mapping.py", + "kind": "method-size", + "detail": "_get_remote_url" + }, + { + "file": "mcp_server/shared/domain_mapping.py", + "kind": "method-size", + "detail": "_group_repos" + }, + { + "file": "mcp_server/shared/domain_mapping.py", + "kind": "method-size", + "detail": "resolve_domain" + }, + { + "file": "mcp_server/shared/entity_canonical.py", + "kind": "unsourced-constant", + "detail": "_ALLCAPS_TITLE_CUTOFF" + }, + { + "file": "mcp_server/shared/linear_algebra.py", + "kind": "layer-violation", + "detail": "numpy" + }, + { + "file": "mcp_server/shared/linear_algebra.py", + "kind": "layer-violation", + "detail": "numpy.typing" + }, + { + "file": "mcp_server/shared/minhash.py", + "kind": "layer-violation", + "detail": "numpy" + }, + { + "file": "mcp_server/shared/near_dup_calibration.py", + "kind": "method-size", + "detail": "build_components" + }, + { + "file": "mcp_server/shared/near_dup_calibration.py", + "kind": "unsourced-constant", + "detail": "SCAN_FLOOR" + }, + { + "file": "mcp_server/shared/redaction.py", + "kind": "method-size", + "detail": "redact_url" + }, + { + "file": "mcp_server/shared/subprocess_safe.py", + "kind": "method-size", + "detail": "run_with_hard_timeout" + }, + { + "file": "mcp_server/shared/temporal_normalize.py", + "kind": "layer-violation", + "detail": "dateutil" + }, + { + "file": "mcp_server/shared/types_features.py", + "kind": "layer-violation", + "detail": "pydantic" + }, + { + "file": "mcp_server/shared/types_profiles.py", + "kind": "layer-violation", + "detail": "pydantic" + }, + { + "file": "mcp_server/shared/vader.py", + "kind": "method-size", + "detail": "vader_compound" + }, + { + "file": "mcp_server/shared/vader.py", + "kind": "unsourced-constant", + "detail": "_ALPHA" + }, + { + "file": "mcp_server/shared/vader.py", + "kind": "unsourced-constant", + "detail": "_N_SCALAR" + }, + { + "file": "mcp_server/shared/wiki_classification.py", + "kind": "layer-violation", + "detail": "mcp_server.core.wiki_axis_registry" + }, + { + "file": "mcp_server/shared/wiki_index.py", + "kind": "unsourced-constant", + "detail": "_DOMAIN_SCOPED_PATH_PARTS" + }, + { + "file": "mcp_server/shared/wiki_ir.py", + "kind": "layer-violation", + "detail": "pydantic" + }, + { + "file": "mcp_server/shared/wiki_layout.py", + "kind": "unsourced-constant", + "detail": "_MAX_SLUG_LEN" + }, + { + "file": "mcp_server/shared/wiki_source_paths.py", + "kind": "method-size", + "detail": "extract_document_paths" + }, + { + "file": "mcp_server/shared/wiki_source_paths.py", + "kind": "method-size", + "detail": "normalize_source_path" + }, + { + "file": "mcp_server/shared/write_class.py", + "kind": "method-size", + "detail": "classify_write_class" + }, + { + "file": "mcp_server/tool_error_handler.py", + "kind": "method-size", + "detail": "_classify_error" + }, + { + "file": "mcp_server/tool_error_handler.py", + "kind": "method-size", + "detail": "safe_handler" + }, + { + "file": "mcp_server/tool_registry_ingest.py", + "kind": "method-size", + "detail": "_register_ingest_codebase" + }, + { + "file": "mcp_server/tool_registry_memory.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/tool_registry_memory.py", + "kind": "method-size", + "detail": "_register_recall" + }, + { + "file": "mcp_server/tool_registry_memory.py", + "kind": "method-size", + "detail": "_register_remember" + }, + { + "file": "mcp_server/validation/schemas.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "mcp_server/validation/schemas.py", + "kind": "method-size", + "detail": "_check_array_envelope" + }, + { + "file": "scripts/backfill_write_class.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/badge_render.py", + "kind": "method-size", + "detail": "render" + }, + { + "file": "scripts/badge_render.py", + "kind": "unsourced-constant", + "detail": "_BADGE_HEIGHT" + }, + { + "file": "scripts/badge_render.py", + "kind": "unsourced-constant", + "detail": "_FONT_SIZE" + }, + { + "file": "scripts/badge_render.py", + "kind": "unsourced-constant", + "detail": "_SIDE_PADDING" + }, + { + "file": "scripts/check_marketplace_pins.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "scripts/check_marketplace_pins.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/groomer.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "scripts/groomer.py", + "kind": "method-size", + "detail": "_run" + }, + { + "file": "scripts/groomer.py", + "kind": "method-size", + "detail": "_write_journal" + }, + { + "file": "scripts/groomer.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/launcher.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/launcher_deps.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "scripts/launcher_deps.py", + "kind": "method-size", + "detail": "_deps_lock" + }, + { + "file": "scripts/launcher_deps.py", + "kind": "method-size", + "detail": "ensure_deps" + }, + { + "file": "scripts/launcher_deps.py", + "kind": "unsourced-constant", + "detail": "_LOCK_WAIT_SECONDS" + }, + { + "file": "scripts/launcher_deps.py", + "kind": "unsourced-constant", + "detail": "_STALE_LOCK_SECONDS" + }, + { + "file": "scripts/launcher_deps_install.py", + "kind": "method-size", + "detail": "pip_install" + }, + { + "file": "scripts/mcp_toplist_ranking.py", + "kind": "unsourced-constant", + "detail": "_MIN_PRINTABLE_PCT" + }, + { + "file": "scripts/memory_dedup_exact.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/memory_domain_backfill.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/memory_reheat.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/mutation_equivalents.py", + "kind": "method-size", + "detail": "format_report" + }, + { + "file": "scripts/mutation_equivalents.py", + "kind": "unsourced-constant", + "detail": "_MIN_RATIONALE" + }, + { + "file": "scripts/near_dup_calibrate.py", + "kind": "method-size", + "detail": "_cmd_apply" + }, + { + "file": "scripts/near_dup_calibrate.py", + "kind": "method-size", + "detail": "_cmd_calibrate" + }, + { + "file": "scripts/provenance_sweep.py", + "kind": "method-size", + "detail": "_run" + }, + { + "file": "scripts/provenance_sweep.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/refresh_mcp_toplist_badge.py", + "kind": "unsourced-constant", + "detail": "_LABEL_PANEL_W" + }, + { + "file": "scripts/regenerate_file_docs.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/setup.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "scripts/setup.py", + "kind": "method-size", + "detail": "_redact_db_url" + }, + { + "file": "scripts/setup.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/setup.py", + "kind": "unsourced-constant", + "detail": "_MIN_PY_MINOR" + }, + { + "file": "scripts/setup_db.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "scripts/setup_db.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/verify_mcp_hosts.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "scripts/verify_mcp_hosts.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/wiki_backfill_ids.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/wiki_bulk_migrate.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "scripts/wiki_bulk_migrate.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/wiki_bulk_migrate.py", + "kind": "method-size", + "detail": "plan" + }, + { + "file": "scripts/wiki_citation_seed.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/wiki_pilot_migration.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "scripts/wiki_pilot_migration.py", + "kind": "method-size", + "detail": "_evaluate" + }, + { + "file": "scripts/wiki_pilot_migration.py", + "kind": "method-size", + "detail": "_format_report" + }, + { + "file": "scripts/wiki_pilot_migration.py", + "kind": "method-size", + "detail": "_parse_frontmatter" + }, + { + "file": "scripts/wiki_pilot_migration.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/wiki_rebucket_file_docs.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "scripts/wiki_rebucket_file_docs.py", + "kind": "method-size", + "detail": "_rewrite_frontmatter" + }, + { + "file": "scripts/wiki_rebucket_file_docs.py", + "kind": "method-size", + "detail": "apply" + }, + { + "file": "scripts/wiki_rebucket_file_docs.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "scripts/wiki_rebucket_file_docs.py", + "kind": "method-size", + "detail": "plan" + }, + { + "file": "tests_py/_store_cleanup.py", + "kind": "method-size", + "detail": "_clean_sqlite_via_singleton" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "method-size", + "detail": "benchmark_consolidation_cascade" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "method-size", + "detail": "benchmark_homeostatic_plasticity" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "method-size", + "detail": "benchmark_microglial_pruning" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "method-size", + "detail": "benchmark_pattern_separation" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "method-size", + "detail": "benchmark_spreading_activation" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "method-size", + "detail": "benchmark_synaptic_plasticity" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "method-size", + "detail": "benchmark_synaptic_tagging" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "method-size", + "detail": "benchmark_write_gate" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "method-size", + "detail": "benchmark_write_gate._simulate_signals" + }, + { + "file": "tests_py/benchmarks/benchmark_harness.py", + "kind": "method-size", + "detail": "run_all_benchmarks" + }, + { + "file": "tests_py/benchmarks/test_codebase_alteration.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/benchmarks/test_lib_init_no_psycopg.py", + "kind": "method-size", + "detail": "test_benchmark_db_resolves_with_psycopg_present" + }, + { + "file": "tests_py/benchmarks/test_spell_alteration.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/context_assembly/test_condense_code.py", + "kind": "unsourced-constant", + "detail": "FOUR_BLOCKS_CODE_TOKENS" + }, + { + "file": "tests_py/core/context_assembly/test_condense_dispatch.py", + "kind": "unsourced-constant", + "detail": "THREE_SENTENCES_TOKENS" + }, + { + "file": "tests_py/core/context_assembly/test_condense_text.py", + "kind": "unsourced-constant", + "detail": "THREE_SENTENCES_TOKENS" + }, + { + "file": "tests_py/core/context_assembly/test_condensers.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/context_assembly/test_condensers.py", + "kind": "unsourced-constant", + "detail": "FITS" + }, + { + "file": "tests_py/core/test_ablation_hooks.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_ast_extractor_definitions.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_ast_extractor_depth.py", + "kind": "unsourced-constant", + "detail": "_NESTING" + }, + { + "file": "tests_py/core/test_ast_extractor_edges.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_ast_extractor_walkers.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_bridge_finder.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_codebase_graph.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_concept_emerger.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_concept_emerger.py", + "kind": "method-size", + "detail": "TestEmerge.test_stats_count_saturating_and_abandoned_plans" + }, + { + "file": "tests_py/core/test_concept_emerger.py", + "kind": "method-size", + "detail": "TestEmerge.test_stats_counts_promoted_plans" + }, + { + "file": "tests_py/core/test_gist_extraction.py", + "kind": "unsourced-constant", + "detail": "_OVERHEAD" + }, + { + "file": "tests_py/core/test_memory_rules.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_metacognition.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_microglial_pruning.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_pg_recall_pipeline.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_pg_recall_pipeline.py", + "kind": "method-size", + "detail": "test_mood_congruent_ema_driven_path_produces_delta" + }, + { + "file": "tests_py/core/test_profile_builder.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_provenance.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_reranker.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_session_extractor.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_stochastic_transmission.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_swr_replay.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_temporal_timezones.py", + "kind": "unsourced-constant", + "detail": "_HOUR" + }, + { + "file": "tests_py/core/test_wiki_classifier.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_wiki_view_executor.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/core/test_write_gate.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/consolidation/test_import_cycle_237.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/consolidation/test_memory_dedup_exact_pass.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/consolidation/test_memory_domain_backfill_pass.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/consolidation/test_memory_reheat_pass.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/consolidation/test_memory_reheat_pass.py", + "kind": "method-size", + "detail": "TestUnreachableRowIsReportedNotFabricated.test_row_that_cannot_reach_target_even_at_max_heat_base_stays_unchanged" + }, + { + "file": "tests_py/handlers/consolidation/test_near_dup_calibration_pass.py", + "kind": "unsourced-constant", + "detail": "EMBEDDING_DIM" + }, + { + "file": "tests_py/handlers/consolidation/test_wiki_maintenance.py", + "kind": "method-size", + "detail": "_silence_everything_except_citation_seed" + }, + { + "file": "tests_py/handlers/test_add_rule.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_assess_coverage.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_backfill_discover_files_issue15.py", + "kind": "method-size", + "detail": "fake_projects_dir" + }, + { + "file": "tests_py/handlers/test_ble001_sweep_handlers.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_cls_diagnostics.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_cls_diagnostics.py", + "kind": "method-size", + "detail": "TestClsReasonForZero.test_passed_through_when_no_gates_failed_but_no_mutation" + }, + { + "file": "tests_py/handlers/test_curate_distill.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_explore_features.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_explore_features.py", + "kind": "method-size", + "detail": "TestExploreAttributionRealSource.test_classifier_activation_is_always_numeric" + }, + { + "file": "tests_py/handlers/test_forget.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_get_causal_chain.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_headless_authoring_throttle.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_headless_authoring_throttle.py", + "kind": "method-size", + "detail": "TestConcurrencyCap.test_max_concurrent_pages_respects_cap" + }, + { + "file": "tests_py/handlers/test_headless_authoring_throttle.py", + "kind": "method-size", + "detail": "TestGroundableFilter.test_cycle_never_invokes_ungroundable_scopes" + }, + { + "file": "tests_py/handlers/test_headless_authoring_throttle.py", + "kind": "method-size", + "detail": "TestGroundableFilter.test_drain_missing_anchors_skips_ungroundable" + }, + { + "file": "tests_py/handlers/test_headless_authoring_throttle.py", + "kind": "method-size", + "detail": "TestUsdCap.test_usd_cap_stops_further_calls" + }, + { + "file": "tests_py/handlers/test_headless_authoring_throttle.py", + "kind": "method-size", + "detail": "TestWallClockDeadline.test_expired_deadline_skips_remaining" + }, + { + "file": "tests_py/handlers/test_import_sessions.py", + "kind": "method-size", + "detail": "tmp_claude_dir" + }, + { + "file": "tests_py/handlers/test_import_sessions_stream.py", + "kind": "unsourced-constant", + "detail": "_TARGET_LINES" + }, + { + "file": "tests_py/handlers/test_ingest_codebase.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_ingest_codebase.py", + "kind": "method-size", + "detail": "TestIngestCodebaseFailures.test_file_attribution_uses_containment_not_qn_split" + }, + { + "file": "tests_py/handlers/test_ingest_codebase.py", + "kind": "method-size", + "detail": "TestIngestCodebaseHappyPath.test_happy_path_writes_memories_entities_edges_and_pages" + }, + { + "file": "tests_py/handlers/test_ingest_codebase.py", + "kind": "method-size", + "detail": "TestSymbolPageStride.test_paging_loop_visits_every_symbol_without_gaps" + }, + { + "file": "tests_py/handlers/test_ingest_docs_content.py", + "kind": "method-size", + "detail": "TestRunDocsPass.test_writes_one_memory_per_readable_doc_and_skips_oversized" + }, + { + "file": "tests_py/handlers/test_ingest_document.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_ingest_document.py", + "kind": "method-size", + "detail": "TestDocxHappyPath.test_ingests_docx_end_to_end_exact_response" + }, + { + "file": "tests_py/handlers/test_ingest_findings.py", + "kind": "method-size", + "detail": "_make_run" + }, + { + "file": "tests_py/handlers/test_lesson_promotion.py", + "kind": "method-size", + "detail": "TestFullPromotionRoundTrip.test_lesson_to_rule_round_trip" + }, + { + "file": "tests_py/handlers/test_memify_derive.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_memify_diagnostics.py", + "kind": "method-size", + "detail": "TestMemifyReasonForInaction.test_reweight_only_gate_emits_inaction" + }, + { + "file": "tests_py/handlers/test_memory_tier_model.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_navigate_memory.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_rebuild_profiles.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_recall_include_related.py", + "kind": "unsourced-constant", + "detail": "_DIM" + }, + { + "file": "tests_py/handlers/test_record_session_end.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_remember.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_remember_link_provenance.py", + "kind": "method-size", + "detail": "TestInsertAndPostProcessLinkAction.test_link_row_carries_derived_src_tag" + }, + { + "file": "tests_py/handlers/test_s110_sweep_handlers.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_validate_memory.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_wiki_migrate.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_wiki_pipeline_sqlite.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/handlers/test_wiki_redirect_handlers.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/hooks/test_agent_briefing.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/hooks/test_auto_recall.py", + "kind": "method-size", + "detail": "_seeded_db" + }, + { + "file": "tests_py/hooks/test_hook_receipts.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/hooks/test_hook_receipts.py", + "kind": "method-size", + "detail": "test_agent_briefing_emits_receipt_with_marker" + }, + { + "file": "tests_py/hooks/test_hook_receipts.py", + "kind": "method-size", + "detail": "test_agent_briefing_falls_back_when_only_dispatch_agent_is_installed" + }, + { + "file": "tests_py/hooks/test_hook_receipts.py", + "kind": "method-size", + "detail": "test_session_start_banner_receipt_roundtrip" + }, + { + "file": "tests_py/hooks/test_session_lifecycle.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/hooks/test_session_start.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/hooks/test_session_start.py", + "kind": "method-size", + "detail": "TestMaybeBackgroundReanalyzeInterpreterResolution.test_never_consults_shutil_which_and_builds_expected_command" + }, + { + "file": "tests_py/infrastructure/test_capture_origin_legacy_backfill.py", + "kind": "unsourced-constant", + "detail": "_DIM" + }, + { + "file": "tests_py/infrastructure/test_capture_origin_legacy_backfill.py", + "kind": "unsourced-constant", + "detail": "_W" + }, + { + "file": "tests_py/infrastructure/test_embedding_engine.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/infrastructure/test_embedding_live_contract.py", + "kind": "method-size", + "detail": "test_live_encode_batch_agrees_with_single_encode" + }, + { + "file": "tests_py/infrastructure/test_embedding_live_contract.py", + "kind": "unsourced-constant", + "detail": "EXPECTED_DIM" + }, + { + "file": "tests_py/infrastructure/test_groomer_coordinator.py", + "kind": "unsourced-constant", + "detail": "_PERIOD_H" + }, + { + "file": "tests_py/infrastructure/test_injection_receipts_fetch_pg.py", + "kind": "method-size", + "detail": "test_fetch_pg_resolves_joins_and_orders_by_recorded_facts" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "method-size", + "detail": "TestMCPClientCall.test_call_increments_tool_calls" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "method-size", + "detail": "TestMCPClientCall.test_call_increments_tool_calls._test" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "method-size", + "detail": "TestMCPClientConnect.test_connect_handshake_defaults_missing_fields" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "method-size", + "detail": "TestMCPClientConnect.test_connect_handshake_defaults_missing_fields._test" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "method-size", + "detail": "TestMCPClientConnect.test_connect_handshake_failure_closes_and_raises" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "method-size", + "detail": "TestMCPClientConnect.test_connect_handshake_failure_closes_and_raises._test" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "method-size", + "detail": "TestMCPClientConnect.test_connect_handshake_success" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "method-size", + "detail": "TestMCPClientConnect.test_connect_handshake_success._test" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "method-size", + "detail": "TestMCPClientConnect.test_connect_uses_config_env_and_cwd" + }, + { + "file": "tests_py/infrastructure/test_mcp_client.py", + "kind": "method-size", + "detail": "TestMCPClientConnect.test_connect_uses_config_env_and_cwd._test" + }, + { + "file": "tests_py/infrastructure/test_mcp_client_pool.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/infrastructure/test_pg_alpha_integral.py", + "kind": "method-size", + "detail": "_alpha_integral_oracle" + }, + { + "file": "tests_py/infrastructure/test_pg_recall_scoring_debias.py", + "kind": "method-size", + "detail": "TestAutoCaptureDebias.test_curated_lesson_outranks_fresh_auto_capture" + }, + { + "file": "tests_py/infrastructure/test_pg_recall_scoring_debias.py", + "kind": "unsourced-constant", + "detail": "_DIM" + }, + { + "file": "tests_py/infrastructure/test_pg_store_near_dup.py", + "kind": "method-size", + "detail": "TestFetchMemberStats.test_returns_effective_heat_and_created_at" + }, + { + "file": "tests_py/infrastructure/test_pg_store_near_dup.py", + "kind": "unsourced-constant", + "detail": "EMBEDDING_DIM" + }, + { + "file": "tests_py/infrastructure/test_pg_supersession.py", + "kind": "method-size", + "detail": "test_supersede_atomic_conflict_rolls_back_no_orphan" + }, + { + "file": "tests_py/infrastructure/test_pg_supersession.py", + "kind": "unsourced-constant", + "detail": "_DIM" + }, + { + "file": "tests_py/infrastructure/test_pipeline_discovery.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/infrastructure/test_profile_split.py", + "kind": "method-size", + "detail": "TestTargetedSaveAmplification.test_save_one_does_not_touch_other_files" + }, + { + "file": "tests_py/infrastructure/test_s110_sweep_infrastructure.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/infrastructure/test_schema_integrity.py", + "kind": "method-size", + "detail": "test_no_hook_or_handler_sql_references_unknown_columns" + }, + { + "file": "tests_py/infrastructure/test_semantic_fallback_169.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/infrastructure/test_session_registry.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/infrastructure/test_sqlite_backend.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/infrastructure/test_sqlite_trust_ranking.py", + "kind": "unsourced-constant", + "detail": "_DIM" + }, + { + "file": "tests_py/infrastructure/test_sqlite_trust_ranking.py", + "kind": "unsourced-constant", + "detail": "_TEST_UNTRUSTED_FACTOR" + }, + { + "file": "tests_py/infrastructure/test_staging_resolve_sink.py", + "kind": "method-size", + "detail": "test_entity_dedup_is_domain_scoped" + }, + { + "file": "tests_py/infrastructure/test_stdio_transport.py", + "kind": "method-size", + "detail": "TestGuardedRunDeliversLateResponse.test_guarded_run_delivers_the_late_response" + }, + { + "file": "tests_py/infrastructure/test_stdio_transport.py", + "kind": "method-size", + "detail": "TestUnguardedRaceCharacterization.test_unguarded_low_level_run_drops_the_late_response" + }, + { + "file": "tests_py/infrastructure/test_stdio_transport_wiring.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/infrastructure/test_supersession_read_path.py", + "kind": "unsourced-constant", + "detail": "_DIM" + }, + { + "file": "tests_py/infrastructure/test_workflow_graph_source_ast.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/infrastructure/test_workflow_graph_source_ast.py", + "kind": "method-size", + "detail": "TestDrainPendingTasks.test_drain_times_out_and_logs_when_a_task_is_slow_to_cancel" + }, + { + "file": "tests_py/integration/test_cold_start.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/integration/test_cold_start.py", + "kind": "method-size", + "detail": "TestSessionStartHook.test_normal_session_with_memories" + }, + { + "file": "tests_py/integration/test_cold_start.py", + "kind": "method-size", + "detail": "TestSetupScript.test_setup_reports_ready_or_needs_install" + }, + { + "file": "tests_py/integration/test_memory_lifecycle.py", + "kind": "method-size", + "detail": "TestFullLifecycle.test_store_recall_checkpoint_restore" + }, + { + "file": "tests_py/integration/test_recall_e2e.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/integration/test_recall_e2e.py", + "kind": "method-size", + "detail": "TestRankingInvariants.test_content_relevance_determines_rank" + }, + { + "file": "tests_py/integration/test_recall_e2e.py", + "kind": "unsourced-constant", + "detail": "_DIM" + }, + { + "file": "tests_py/integration/test_recall_sa_mode_wiring.py", + "kind": "method-size", + "detail": "TestTailModeFillsSparsePool.test_tail_mode_appends_sa_reachable_memory_when_pool_is_short" + }, + { + "file": "tests_py/integration/test_recall_sa_mode_wiring.py", + "kind": "unsourced-constant", + "detail": "_DIM" + }, + { + "file": "tests_py/integration/test_recall_trust_ranking.py", + "kind": "unsourced-constant", + "detail": "_TEST_UNTRUSTED_FACTOR" + }, + { + "file": "tests_py/integration/test_spread_activation_candidate_contract.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/integration/test_spread_activation_candidate_contract.py", + "kind": "method-size", + "detail": "wrrf_and_sa_candidates" + }, + { + "file": "tests_py/integration/test_spread_activation_candidate_contract.py", + "kind": "unsourced-constant", + "detail": "_DIM" + }, + { + "file": "tests_py/invariants/test_I2_canonical_writer.py", + "kind": "method-size", + "detail": "_scan_heat_writers" + }, + { + "file": "tests_py/invariants/test_phase2_parity.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/invariants/test_phase2_parity.py", + "kind": "method-size", + "detail": "_setup_fixture" + }, + { + "file": "tests_py/scripts/test_check_doc_claims.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/scripts/test_generate_pip_constraints.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/scripts/test_generate_repo_badges.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/scripts/test_groomer.py", + "kind": "method-size", + "detail": "TestJournalArtifact.test_write_journal_produces_json_and_markdown" + }, + { + "file": "tests_py/scripts/test_launcher_constraints_extras.py", + "kind": "method-size", + "detail": "test_written_constraints_file_carries_no_extras" + }, + { + "file": "tests_py/scripts/test_launcher_deps.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/scripts/test_launcher_deps.py", + "kind": "method-size", + "detail": "test_ensure_all_deps_full_install_path_threads_exact_arguments" + }, + { + "file": "tests_py/scripts/test_launcher_deps.py", + "kind": "method-size", + "detail": "test_ensure_deps_full_install_path_threads_exact_arguments" + }, + { + "file": "tests_py/scripts/test_launcher_deps.py", + "kind": "method-size", + "detail": "test_ensure_deps_skips_pip_entirely_when_stamp_matches" + }, + { + "file": "tests_py/scripts/test_launcher_deps.py", + "kind": "method-size", + "detail": "test_pip_install_rollback_on_mid_commit_failure" + }, + { + "file": "tests_py/scripts/test_launcher_deps.py", + "kind": "method-size", + "detail": "test_pip_install_rollback_preserves_dist_info_regardless_of_commit_order" + }, + { + "file": "tests_py/scripts/test_mutation_recheck_survivors.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/scripts/test_refresh_mcp_toplist_badge.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/scripts/test_version_surfaces.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/scripts/test_wiki_rebucket_file_docs.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/shared/test_domain_mapping.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/shared/test_types.py", + "kind": "method-size", + "detail": "TestProfilesV2.test_loads_js_format_json" + }, + { + "file": "tests_py/test_doctor_mcp.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/test_ingest_progress.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "tests_py/test_ingest_progress.py", + "kind": "method-size", + "detail": "TestHandlerStageOrdering.test_handler_emits_all_stages_in_order" + }, + { + "file": "tests_py/test_ingest_progress.py", + "kind": "method-size", + "detail": "TestHandlerStageOrdering.test_null_progress_does_not_alter_handler_result" + }, + { + "file": "tests_py/test_main.py", + "kind": "method-size", + "detail": "TestMain.test_standalone_baseline_is_52_tools" + }, + { + "file": "video/generate.py", + "kind": "file-size", + "detail": "exceeds 300-line cap" + }, + { + "file": "video/generate.py", + "kind": "method-size", + "detail": "main" + }, + { + "file": "video/generate.py", + "kind": "method-size", + "detail": "render_beat1" + }, + { + "file": "video/generate.py", + "kind": "method-size", + "detail": "render_beat2" + }, + { + "file": "video/generate.py", + "kind": "method-size", + "detail": "render_beat3" + }, + { + "file": "video/generate.py", + "kind": "method-size", + "detail": "render_beat4" + }, + { + "file": "video/generate.py", + "kind": "method-size", + "detail": "render_beat5" + }, + { + "file": "video/generate.py", + "kind": "unsourced-constant", + "detail": "BEAT3_START" + }, + { + "file": "video/generate.py", + "kind": "unsourced-constant", + "detail": "BEAT4_START" + }, + { + "file": "video/generate.py", + "kind": "unsourced-constant", + "detail": "BEAT5_START" + }, + { + "file": "video/generate.py", + "kind": "unsourced-constant", + "detail": "FPS" + }, + { + "file": "video/generate.py", + "kind": "unsourced-constant", + "detail": "TOTAL" + } + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84927ffb..72405d5c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -558,6 +558,40 @@ jobs: - name: Check workflow files (actionlint + shellcheck) run: actionlint -color + # Deterministic enforcement of CLAUDE.md § Code Style — file size, method + # size, layer-boundary imports, and unsourced magic numbers — which that + # section admitted was "enforced by code review today; no automated + # pre-commit hook checks this yet" until this job. Measured on a single + # PR the night before this job was added: a 301-line file reported as + # 280, three of four over-40-line methods unseen, one layer violation + # justified by a fabricated citation — each caught only by a human or + # agent re-reading the diff, never by a machine. See + # scripts/check_craftsmanship.py's module docstring for the rules and + # scripts/craftsmanship_baseline.py for why pre-existing debt (recorded + # in .craftsmanship-baseline.json) does not retroactively block. + craftsmanship: + name: Craftsmanship Gate + runs-on: ubuntu-latest + steps: + # fetch-depth: 0 so `origin/main` — the diff base the gate compares + # the PR's changed files against — is resolvable locally, not just + # the single commit a shallow checkout would leave. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + + # Standard library only (scripts/check_craftsmanship.py's module + # docstring) — no dependency install needed, matching the + # doc-claim/version-surface/ci-gate-completeness gates in `lint` + # below, which are static for the same reason. + - name: Run the craftsmanship gate on this PR's changed files + run: python scripts/check_craftsmanship.py + typecheck: name: Type Check runs-on: ubuntu-latest @@ -767,6 +801,7 @@ jobs: - mcp-host-config - test-windows - release-deps + - craftsmanship - lint - typecheck - build diff --git a/CLAUDE.md b/CLAUDE.md index 349e3f73..0d50e705 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -77,26 +77,53 @@ separate **cortex-viz** MCP (reads this same store read-only). - 300 lines max per file; 40 lines max per method — a local tightening of coding-standards.md §4.1/§4.2 (≤500/≤50; CONTRIBUTING.md § Code Style - cites the same 300/40 numbers). Enforced by code review today; no - automated pre-commit hook checks this yet (issue #276 corrected the - prior claim of a "craftsmanship-checker" hook — none exists in - `.git/hooks/` or a `.pre-commit-config.yaml`). -- Import rule: `core/` imports only `shared/` + stdlib; `infrastructure/` - never imports core/handlers. Verify both directions before every PR — - `grep -rn "from mcp_server.infrastructure" mcp_server/core/` and - `grep -rn "from mcp_server\.core\." mcp_server/infrastructure/*.py` — - both currently return nothing (re-verified 2026-08-10 while fixing - issue: `wiki_store.py`/`wiki_schema_reader.py` importing `core/`, - PR #409 round 3). The three violations this line used to name - (`wiki_axis_registry.py`, `wiki_classifier.py`, `wiki_schema_loader.py`, - found 2026-07-14 during #114) no longer exist: `wiki_schema_loader.py` - moved `core/` → `shared/` in the same fix, and the other two do not - import `infrastructure/` as of this measurement. Treat a future zero - as the standard, not as evidence the check is unnecessary — re-run the - greps, don't assume they still pass. + cites the same 300/40 numbers). +- Import rule: a TRUE whitelist per layer, all eight rows of + `docs/module-inventory.md` § Dependency Rules — `shared/` and `core/` + are pure (no third-party imports at all; `core/` additionally bans + `os`/`pathlib` even though they are stdlib, since it is zero-I/O + business logic); `infrastructure/`, `validation/`, `handlers/`, + `server/`, `hooks/` are boundary/adapter layers where third-party + imports are the point, but their `mcp_server.` cross-references + are still checked against the table's named whitelist, not a blacklist + of a few forbidden ones. This replaces the former manual-grep + verification step (`grep -rn "from mcp_server.infrastructure" + mcp_server/core/`, etc.) — the craftsmanship gate below runs it, in + both directions, across all eight layers, on every push and PR, so + "re-run the greps before every PR" is no longer the standard: the gate + is. - No invented constants: every hardcoded number carries a `# source:` comment (paper, committed benchmark, or dated measurement naming the - environment and conditions). A number without one blocks the diff in review. + environment and conditions). +- **Enforced by `scripts/check_craftsmanship.py`**, run in CI on every push + and PR (`.github/workflows/ci.yml`, `craftsmanship` job) and locally via + `python scripts/check_craftsmanship.py`. It checks the four rules above, + by AST, on the files a diff touches — never the whole repository. The + layer whitelist is *parsed* from `docs/module-inventory.md`'s own table + at run time (`scripts/craftsmanship_layer_table.py`), never a second + hardcoded copy that could silently diverge from it. + The comparison baseline is read via `git show :.craftsmanship-baseline.json` + — the PR's BASE ref, immutable to the PR's own commits — never the + working tree: a working-tree-only baseline is self-service (add a + violation, run `--write-baseline` in the same tree, the gate would pass + on it — this exact exploit is reproduced and closed in + `tests_py/scripts/test_check_craftsmanship.py::SneakyLimitExploitTests`). + The gate fails the diff on: any violation absent from that base-ref + baseline (new debt); any base-ref-baselined entry whose violation no + longer reproduces (fixed but not pruned); or any entry present in the + working-tree `.craftsmanship-baseline.json` but absent from the base + ref's (the file may only SHRINK within a PR — an addition is refused + outright, matched or not, because debt discovered mid-PR gets fixed at + the source, not grandfathered). Regenerate with + `python scripts/check_craftsmanship.py --write-baseline` only to prune + entries whose violations you actually fixed. Previously "enforced by + code review today; no automated pre-commit hook checks this yet" (issue + #276 corrected an earlier, false claim of a "craftsmanship-checker" + hook) — that gap is what this gate closes. Historical import-rule + violations once tracked ad hoc in this section (`wiki_axis_registry.py`, + `wiki_classifier.py`, `wiki_schema_loader.py`, found 2026-07-14 during + #114) now live in the baseline like any other pre-existing debt, not as + separate prose here. ## What NOT to do diff --git a/SECURITY.md b/SECURITY.md index d501e220..3f540359 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -66,6 +66,15 @@ As of issue #178, every Cortex release ships with verifiable provenance `Docker Smoke`, `Test` on Python 3.10–3.13, `Test (SQLite backend)`, and `Test (Windows, SQLite backend)`). Force-pushes and branch deletion are blocked, and conversation resolution is required. + `ci.yml`'s own aggregate gate (`CI Green`, the single context branch + protection actually names — see `scripts/check_ci_gate_complete.py`) + additionally runs a `Craftsmanship Gate` job as of the PR that added + `scripts/check_craftsmanship.py`; this number becomes twelve once a + repository admin adds it to the required-checks list in GitHub's branch + protection settings (Settings → Branches) — an action this document + cannot perform and the PR that introduced the job does not perform + either, so the two may legitimately disagree until that setting is + updated by hand. ### Why Scorecard's Code-Review check will not go green here diff --git a/scripts/check_craftsmanship.py b/scripts/check_craftsmanship.py new file mode 100644 index 00000000..bfeaa5c8 --- /dev/null +++ b/scripts/check_craftsmanship.py @@ -0,0 +1,207 @@ +"""Craftsmanship gate: a deterministic pass/fail check for the rules +``CLAUDE.md`` § Code Style states but — until this script — nothing +verified. See ``craftsmanship_rules.py`` for what each rule checks and why +its violation identifier is stable; see ``craftsmanship_baseline.py`` for +the ratchet that lets pre-existing debt through without blocking new debt, +and for why the comparison source is the PR's BASE ref, never the working +tree — a working-tree baseline can be tampered with in either direction +(add a violation and self-regenerate; or hand-delete an entry and leave +the violation in place), both reproduced and closed, see that module's +docstring. Git plumbing (resolving the base ref, reading the baseline as +committed there) lives in ``craftsmanship_git.py``. + +Scope: by default, only the files a PR's diff touches (never the whole +repository) — a file untouched by this change is not this change's +problem. ``--write-baseline`` is the one mode that scans everything, because +regenerating the baseline is exactly the operation that must see the whole +tree. + +Usage:: + + python scripts/check_craftsmanship.py # diff vs origin/main + python scripts/check_craftsmanship.py --base main # diff vs an explicit ref + python scripts/check_craftsmanship.py path/to/file.py # explicit files + python scripts/check_craftsmanship.py --write-baseline # regenerate the baseline + +Exit codes: 0 clean, 1 new/stale/added/falsified-removal violations found, +2 could not determine which files to check (git diff failed and no files +were given explicitly) or could not resolve the base ref in diff mode. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +_SCRIPTS_DIR = str(Path(__file__).resolve().parent) +if _SCRIPTS_DIR not in sys.path: + sys.path.insert(0, _SCRIPTS_DIR) +import craftsmanship_rules as rules # noqa: E402 +import craftsmanship_baseline as baseline_mod # noqa: E402 +import craftsmanship_git # noqa: E402 + +REPO_ROOT = Path(__file__).resolve().parent.parent +DEFAULT_BASELINE = REPO_ROOT / ".craftsmanship-baseline.json" + + +def scan_files(rel_paths: list[str]) -> set[rules.Violation]: + """Scan each path (relative to REPO_ROOT); a missing file yields nothing.""" + found: set[rules.Violation] = set() + for rel_path in rel_paths: + full_path = REPO_ROOT / rel_path + if not full_path.is_file(): + continue + source = full_path.read_text(encoding="utf-8") + found.update(rules.scan_source(rel_path, source)) + return found + + +def _report( + new: list[rules.Violation], + stale: list[rules.Violation], + added: list[rules.Violation], + falsified: list[rules.Violation], +) -> None: + if added: + print( + "Craftsmanship gate: baseline entries ADDED without a base-ref " + "match (the ratchet only shrinks — fix the violation, don't " + "grandfather it):", + file=sys.stderr, + ) + for v in added: + print(f" - [{v.kind}] {v.file}: {v.detail}", file=sys.stderr) + if falsified: + print( + "Craftsmanship gate: baseline entries REMOVED but the violation " + "still reproduces (a falsified prune — fix the code, or leave " + "the entry, don't just delete the JSON line):", + file=sys.stderr, + ) + for v in falsified: + print(f" - [{v.kind}] {v.file}: {v.detail}", file=sys.stderr) + if new: + print( + "Craftsmanship gate: NEW violations (not in the base-ref baseline):", + file=sys.stderr, + ) + for v in new: + print(f" - [{v.kind}] {v.file}: {v.detail}", file=sys.stderr) + if stale: + print( + "Craftsmanship gate: STALE baseline entries " + "(fixed in code but still listed — prune them):", + file=sys.stderr, + ) + for v in stale: + print(f" - [{v.kind}] {v.file}: {v.detail}", file=sys.stderr) + if not new and not stale and not added and not falsified: + print("Craftsmanship gate: OK") + + +def _write_baseline(baseline_path: Path) -> int: + files = craftsmanship_git.all_tracked_python_files(REPO_ROOT) + violations = scan_files(files) + baseline_mod.save_baseline(baseline_path, violations) + print(f"Wrote {len(violations)} violation(s) to {baseline_path}") + for kind, count in baseline_mod.count_by_kind(violations).items(): + print(f" {kind}: {count}") + return 0 + + +def _added_and_falsified( + working_baseline: set[rules.Violation], base_baseline: set[rules.Violation] | None +) -> tuple[list[rules.Violation], list[rules.Violation]]: + """The two base-ref-anchored ratchet checks — skipped (empty) in the + bootstrap case (no base-ref baseline to compare against yet). + """ + if base_baseline is None: + return [], [] + added = baseline_mod.added_entries(working_baseline, base_baseline) + removed_files = sorted({v.file for v in base_baseline - working_baseline}) + removed_rescanned = {f: scan_files([f]) for f in removed_files} + falsified = baseline_mod.falsified_removals( + base_baseline, working_baseline, removed_rescanned + ) + return added, falsified + + +def _run_gate( + target_files: list[str], baseline_path: Path, base_ref: str | None +) -> int: + current = scan_files(target_files) + working_baseline = baseline_mod.load_baseline(baseline_path) + base_baseline = craftsmanship_git.load_baseline_from_ref( + REPO_ROOT, base_ref, baseline_path + ) + + # The tamper-proof comparison source for "is this violation already + # known" is the base ref's baseline — falling back to the working + # tree's only in the bootstrap case (base_baseline is None: no base + # ref, or the file does not exist at the base ref yet). + comparison_baseline = ( + base_baseline if base_baseline is not None else working_baseline + ) + new = baseline_mod.new_violations(current, comparison_baseline) + added, falsified = _added_and_falsified(working_baseline, base_baseline) + + baseline_files = sorted({v.file for v in working_baseline}) + rescanned = {f: scan_files([f]) for f in baseline_files} + stale = baseline_mod.stale_entries(working_baseline, rescanned) + + _report(new, stale, added, falsified) + return 1 if (new or stale or added or falsified) else 0 + + +def _parse_args(argv: list[str] | None) -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("files", nargs="*", help="explicit files to check") + parser.add_argument("--base", default=None, help="git ref to diff/compare against") + parser.add_argument( + "--baseline", default=str(DEFAULT_BASELINE), help="baseline JSON path" + ) + parser.add_argument( + "--write-baseline", + action="store_true", + help="regenerate the baseline from the full tree", + ) + return parser.parse_args(argv) + + +def main(argv: list[str] | None = None) -> int: + args = _parse_args(argv) + baseline_path = Path(args.baseline) + + if args.write_baseline: + return _write_baseline(baseline_path) + + if args.files: + # Ad hoc/local usage: the base ref still feeds the ratchet-file + # and base-baseline comparisons below when it resolves, but an + # unresolved ref here is NOT fatal (offline/no-remote local runs + # stay usable) — it just falls back to bootstrap semantics. + target_files = args.files + base_ref = craftsmanship_git.resolve_base_ref(REPO_ROOT, args.base) + else: + base_ref = craftsmanship_git.resolve_base_ref(REPO_ROOT, args.base) + if base_ref is None: + print( + "Craftsmanship gate: could not resolve a base ref to diff against", + file=sys.stderr, + ) + return 2 + diffed = craftsmanship_git.changed_python_files(REPO_ROOT, base_ref) + if diffed is None: + print( + f"Craftsmanship gate: `git diff` against {base_ref} failed", + file=sys.stderr, + ) + return 2 + target_files = diffed + + return _run_gate(target_files, baseline_path, base_ref) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/craftsmanship_baseline.py b/scripts/craftsmanship_baseline.py new file mode 100644 index 00000000..880ed5a1 --- /dev/null +++ b/scripts/craftsmanship_baseline.py @@ -0,0 +1,167 @@ +"""Baseline load/save/diff for the craftsmanship gate. + +The baseline is the ratchet: it lists every violation known at the date it +was introduced (or last regenerated), so ``check_craftsmanship.py`` never +blocks retroactively on pre-existing debt — only on a violation that is +NEW (absent from the baseline) or a baseline entry that has silently gone +STALE (its violation no longer exists in the code, meaning someone fixed it +without pruning the baseline — see this module's docstring in +check_craftsmanship.py for why that also fails the gate). + +**A working-tree baseline is not, by itself, a trustworthy comparison +source** — flagged in review, reproduced live: add +``SNEAKY_LIMIT = 12345`` to a tracked file, the gate blocks it; run +``--write-baseline`` in the same working tree, the gate now passes on the +identical violation, because ``load_baseline`` just re-read whatever the +PR itself had just written. The ``$comment`` warning above is a social +contract, not a mechanism. ``check_craftsmanship.py`` closes this by +comparing new violations against the baseline **as committed at the PR's +base ref** (``git show :``, immutable to the PR's own commits) +and separately enforcing that the working-tree file is a SUBSET of that +base-ref baseline — an addition with no matching fix is refused outright; +see ``added_entries`` below. + +That first fix only closed the ADDITION side. A second review round +reproduced the mirror image: hand-delete a baseline entry's JSON line +without touching the source file it describes, and the gate answered OK — +``added_entries`` only looks at what was added, and ``new_violations`` +only rescans the PR's diffed files (the baseline file itself isn't a +``.py`` file, so a PR touching nothing else scans zero files). Closed by +``falsified_removals``: every entry present at the base ref but absent +from the working tree gets its file rescanned, and a removal whose +violation still reproduces is refused exactly like an addition is. + +This module supplies the parsing both paths share +(``parse_baseline_json``); the git plumbing lives in +``check_craftsmanship.py``, which already owns ``_run_git``. +""" + +from __future__ import annotations + +import datetime as _dt +import json +import sys +from collections import Counter +from pathlib import Path + +_SCRIPTS_DIR = str(Path(__file__).resolve().parent) +if _SCRIPTS_DIR not in sys.path: + sys.path.insert(0, _SCRIPTS_DIR) +from craftsmanship_rules import Violation # noqa: E402 + + +def parse_baseline_json(text: str) -> set[Violation]: + """Parse a baseline JSON document's ``violations`` array. + + Shared by ``load_baseline`` (working tree) and + ``check_craftsmanship.load_baseline_from_ref`` (``git show`` output) — + the two loaders differ only in how they obtain ``text``, never in how + they interpret it. + """ + data = json.loads(text) + return { + Violation(entry["file"], entry["kind"], entry["detail"]) + for entry in data.get("violations", []) + } + + +def load_baseline(path: Path) -> set[Violation]: + """Return the baselined violations, or an empty set if none exists yet.""" + if not path.exists(): + return set() + return parse_baseline_json(path.read_text(encoding="utf-8")) + + +def save_baseline(path: Path, violations: set[Violation]) -> None: + """Write ``violations`` as the new baseline, sorted for a stable diff.""" + entries = sorted( + ({"file": v.file, "kind": v.kind, "detail": v.detail} for v in violations), + key=lambda entry: (entry["file"], entry["kind"], entry["detail"]), + ) + payload = { + "$comment": ( + "Generated by `python scripts/check_craftsmanship.py --write-baseline`. " + "Do NOT add an entry by hand to silence a new violation — that is " + "gaming the ratchet, not maintaining it (CLAUDE.md § Code Style). " + "Regenerate only after confirming every new entry is pre-existing " + "debt, not something this PR just introduced. Every entry removed " + "here must correspond to a violation actually fixed in the code." + ), + "generated": _dt.datetime.now(tz=_dt.timezone.utc).date().isoformat(), + "violations": entries, + } + path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") + + +def new_violations( + current: set[Violation], baseline: set[Violation] +) -> list[Violation]: + """Violations present now but absent from the baseline — these block.""" + return sorted(current - baseline, key=lambda v: (v.file, v.kind, v.detail)) + + +def stale_entries( + baseline: set[Violation], rescanned: dict[str, set[Violation]] +) -> list[Violation]: + """Baseline entries whose violation no longer exists in the current code. + + ``rescanned`` maps every file referenced anywhere in the baseline to the + violations a fresh scan of that file finds today (an empty set, or a + missing key, for a file that no longer exists or no longer offends). + """ + stale = [ + entry for entry in baseline if entry not in rescanned.get(entry.file, set()) + ] + return sorted(stale, key=lambda v: (v.file, v.kind, v.detail)) + + +def added_entries( + working_baseline: set[Violation], base_baseline: set[Violation] +) -> list[Violation]: + """Entries present in this PR's baseline file but absent from the base + ref's — the ratchet-file check. The baseline file may only SHRINK + within a PR (a violation genuinely fixed, then pruned); an addition, + with or without a matching removal elsewhere, is refused. Debt + discovered mid-PR gets fixed at the source, not grandfathered — this + is what makes ``.craftsmanship-baseline.json`` a ratchet and not a + second, self-service allowlist. + """ + return sorted( + working_baseline - base_baseline, key=lambda v: (v.file, v.kind, v.detail) + ) + + +def falsified_removals( + base_baseline: set[Violation], + working_baseline: set[Violation], + rescanned: dict[str, set[Violation]], +) -> list[Violation]: + """The mirror image of ``added_entries``, flagged in review: closing + "add a violation, then run --write-baseline in the same tree" without + also closing "delete the JSON line by hand, leave the violation in + place" left the ratchet open on the removal side — any of the ~1400 + grandfathered entries could be hand-deleted from + ``.craftsmanship-baseline.json`` for free, since neither + ``added_entries`` (only looks at additions) nor ``new_violations`` + (only rescans this PR's diffed files, not every baselined file) would + ever see it. + + An entry present in ``base_baseline`` but absent from + ``working_baseline`` is a REMOVAL. Legitimate ("I fixed it, then + pruned") stays silent here — ``rescanned`` (a fresh scan of exactly + the files those removed entries reference) simply will not reproduce + it. Illegitimate ("I deleted the JSON line, the code is untouched") + is caught: the violation still reproduces despite the entry being + gone, so the removal is refused. + """ + removed = base_baseline - working_baseline + falsified = [v for v in removed if v in rescanned.get(v.file, set())] + return sorted(falsified, key=lambda v: (v.file, v.kind, v.detail)) + + +def count_by_kind(violations: set[Violation] | list[Violation]) -> dict[str, int]: + """Debt breakdown by rule family — printed on ``--write-baseline`` so a + 1000+-entry baseline is a visible number per rule, not an opaque wall + of JSON no reviewer will actually read end to end. + """ + return dict(sorted(Counter(v.kind for v in violations).items())) diff --git a/scripts/craftsmanship_constants.py b/scripts/craftsmanship_constants.py new file mode 100644 index 00000000..2a2d071d --- /dev/null +++ b/scripts/craftsmanship_constants.py @@ -0,0 +1,128 @@ +"""Craftsmanship rule 4 — module-scope numeric literals without a +``# source:`` comment. + +Split out of ``craftsmanship_rules.py`` for the same reason +``craftsmanship_imports.py`` was (file-size self-application, see that +module's docstring). + +**Known, documented detection gaps** (flagged in review; each is pinned by +a test in ``tests_py/scripts/test_craftsmanship_constants.py`` asserting +the CURRENT non-detecting behavior, so silently "fixing" one is a reviewed +diff, not an accidental drift): + +1. **Computed expressions** — ``TIMEOUT = 60 * 60`` is an ``ast.BinOp``, + not the bare ``ast.Constant``/negated-constant this rule's + ``_numeric_literal`` matches, so it is never flagged. +2. **Class-scope constants** — ``_module_level_numeric_assignments`` only + walks ``tree.body`` (the module's own top-level statements); a class + attribute (``class C: TIMEOUT = 3600``) sits one level deeper and is + never visited. +3. **Default-argument values** — ``def f(timeout: int = 3600):`` is a + value inside a ``FunctionDef.args.defaults`` list, a different AST + surface this rule never inspects. + +None of these are exotic — they are exactly the forms most likely to +carry an accidental magic number. Extending detection to them is future +work, not silently promised by this module's name. +""" + +from __future__ import annotations + +import ast +import sys +from pathlib import Path + +# Sibling-module import, same idiom as check_doc_claims.py: resolves +# identically whether this runs as a script or is loaded via +# importlib.util.spec_from_file_location from a test. +_SCRIPTS_DIR = str(Path(__file__).resolve().parent) +if _SCRIPTS_DIR not in sys.path: + sys.path.insert(0, _SCRIPTS_DIR) +from craftsmanship_rules import Violation # noqa: E402 + +SOURCE_COMMENT_MARKER = "# source:" + +# NOT a "# source:"-backed constant (flagged in review: citing "task +# instruction" as a §8 source is not one — §8 wants a paper, a committed +# benchmark, or a dated measurement, none of which apply to an exemption +# list). This is a documented implementer DECISION, not a measurement: +# the exact values a human reviewer accepts without asking "where does +# that number come from" — 0/1/-1/2/100/1000 are load-bearing in every +# language's arithmetic idiom (empty/singleton/negation/pair/percent/ +# per-mille), and a power of two up to 2**16 (65536) is legible on sight +# as a bit-width or buffer size, not a business threshold that needs +# citing. Pinned by +# tests_py/scripts/test_craftsmanship_constants.py so a change to this +# list is a reviewed diff, not silent drift. +_POWERS_OF_TWO_USUELLES = frozenset(2**exp for exp in range(1, 17)) +TRIVIAL_LITERALS = frozenset({0, 1, -1, 2, 100, 1000}) | _POWERS_OF_TWO_USUELLES + + +def _numeric_literal(node: ast.expr) -> int | float | None: + if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): + if isinstance(node.value, bool): + return None + return node.value + if isinstance(node, ast.UnaryOp) and isinstance(node.op, (ast.USub, ast.UAdd)): + inner = _numeric_literal(node.operand) + if inner is None: + return None + return -inner if isinstance(node.op, ast.USub) else inner + return None + + +def _has_source_comment(lines: list[str], lineno: int) -> bool: + """True if a ``# source:`` comment sits on, or immediately above, line + ``lineno`` (1-indexed) — a contiguous run of comment lines, no blank + line in between. + """ + if 0 < lineno <= len(lines) and SOURCE_COMMENT_MARKER in lines[lineno - 1]: + return True + i = lineno - 2 + while i >= 0: + stripped = lines[i].strip() + if not stripped.startswith("#"): + return False + if SOURCE_COMMENT_MARKER in lines[i]: + return True + i -= 1 + return False + + +def _module_level_numeric_assignments( + tree: ast.Module, +) -> list[tuple[str, ast.expr, int]]: + """Return (name, value_node, lineno) for simple module-scope assignments.""" + found: list[tuple[str, ast.expr, int]] = [] + for node in tree.body: + if ( + isinstance(node, ast.Assign) + and len(node.targets) == 1 + and isinstance(node.targets[0], ast.Name) + ): + found.append((node.targets[0].id, node.value, node.lineno)) + elif ( + isinstance(node, ast.AnnAssign) + and isinstance(node.target, ast.Name) + and node.value is not None + ): + found.append((node.target.id, node.value, node.lineno)) + return found + + +def check_unsourced_constants( + rel_path: str, tree: ast.Module, source: str +) -> list[Violation]: + """Rule 4 — a non-trivial module-scope numeric literal with no nearby + ``# source:`` comment. ``detail`` is the constant's name: stable across + any edit that leaves the assignment (and its comment) untouched. + """ + lines = source.splitlines() + violations = [] + for name, value_node, lineno in _module_level_numeric_assignments(tree): + value = _numeric_literal(value_node) + if value is None or value in TRIVIAL_LITERALS: + continue + if not _has_source_comment(lines, lineno): + violations.append(Violation(rel_path, "unsourced-constant", name)) + return violations diff --git a/scripts/craftsmanship_git.py b/scripts/craftsmanship_git.py new file mode 100644 index 00000000..e8c99f5a --- /dev/null +++ b/scripts/craftsmanship_git.py @@ -0,0 +1,158 @@ +"""Git plumbing for the craftsmanship gate: resolving the diff base ref, +listing changed/tracked files, and reading the baseline exactly as +committed at a ref — the tamper-proof comparison source +``craftsmanship_baseline.py``'s module docstring explains the need for. + +Split out of ``check_craftsmanship.py`` to stay under the 300-line cap +this gate enforces on everything else (self-application). + +Every function takes ``repo_root`` explicitly rather than reading a +module-level constant: ``check_craftsmanship.py`` owns the one true +``REPO_ROOT`` (patched by name in tests — ``mock.patch.object(gate, +"REPO_ROOT", ...)``), and passing it through here means that patch keeps +working after the split instead of silently operating on a second, +un-patched copy. +""" + +from __future__ import annotations + +import os +import subprocess +import sys +from pathlib import Path + +_SCRIPTS_DIR = str(Path(__file__).resolve().parent) +if _SCRIPTS_DIR not in sys.path: + sys.path.insert(0, _SCRIPTS_DIR) +import craftsmanship_baseline as baseline_mod # noqa: E402 +from craftsmanship_rules import Violation # noqa: E402 + + +def _run_git(repo_root: Path, args: list[str]) -> str | None: + try: + result = subprocess.run( + ["git", *args], + cwd=repo_root, + capture_output=True, + text=True, + check=True, + ) + except (subprocess.CalledProcessError, FileNotFoundError): + return None + return result.stdout + + +def resolve_base_ref(repo_root: Path, explicit: str | None) -> str | None: + """Return the first ref that exists among the explicit ref, the PR's + base branch (if running under GitHub Actions), and ``origin/main``. + """ + pr_base = os.environ.get("GITHUB_BASE_REF") + candidates = [explicit, f"origin/{pr_base}" if pr_base else None, "origin/main"] + for candidate in candidates: + if ( + candidate + and _run_git(repo_root, ["rev-parse", "--verify", candidate]) is not None + ): + return candidate + return None + + +def changed_python_files(repo_root: Path, base_ref: str) -> list[str] | None: + """Files added/copied/modified/renamed on this branch since ``base_ref``.""" + output = _run_git( + repo_root, + [ + "diff", + "--name-only", + "--diff-filter=ACMR", + f"{base_ref}...HEAD", + "--", + "*.py", + ], + ) + if output is None: + return None + return sorted(line for line in output.splitlines() if line) + + +def all_tracked_python_files(repo_root: Path) -> list[str]: + """Every git-tracked ``.py`` file — the scope for ``--write-baseline``.""" + output = _run_git(repo_root, ["ls-files", "--", "*.py"]) or "" + return sorted(line for line in output.splitlines() if line) + + +def _relative_to_repo(repo_root: Path, path: Path) -> str | None: + try: + return path.resolve().relative_to(repo_root).as_posix() + except ValueError: + return None + + +# Substrings of the exact git stderr for "the ref resolves fine, but this +# path is not present in that tree" (verified against git 2.54.0: "fatal: +# path 'X' does not exist in 'Y'" for a path never committed, "fatal: path +# 'X' exists on disk, but not in 'Y'" for one present locally but untracked +# at that ref). Anything else — "invalid object name", a crash, permission +# denial — is a REAL git failure, not "doesn't exist yet", and must raise. +_PATH_ABSENT_MARKERS = ("does not exist in", "exists on disk, but not in") + + +def _git_path_exists_at_ref(repo_root: Path, ref: str, relative_path: str) -> bool: + """True if ``relative_path`` exists in the tree ``ref`` resolves to. + + Self-audited after the two review-round findings that shared one root + cause (a control that fails OPEN when a signal is ambiguous, instead + of failing closed): swallowing every ``cat-file -e`` failure into a + bare False previously read an unrelated git error (corrupt object, a + ref that stopped resolving between the caller's earlier + ``rev-parse --verify`` and this call, disk failure) the exact same way + it read a genuinely absent path — silently handing + ``load_baseline_from_ref`` a bootstrap fallback to the tamperable + working-tree baseline. Distinguishes the two via git's own stderr. + """ + result = subprocess.run( + ["git", "cat-file", "-e", f"{ref}:{relative_path}"], + cwd=repo_root, + capture_output=True, + text=True, + ) + if result.returncode == 0: + return True + if any(marker in result.stderr for marker in _PATH_ABSENT_MARKERS): + return False + raise RuntimeError( + f"git cat-file -e {ref}:{relative_path} failed unexpectedly " + f"(exit {result.returncode}): {result.stderr.strip()!r} — refusing " + f"to treat this as 'path absent, safe to bootstrap'" + ) + + +def load_baseline_from_ref( + repo_root: Path, ref: str | None, baseline_path: Path +) -> set[Violation] | None: + """The baseline exactly as committed at ``ref`` — immutable to the PR's + own commits, unlike ``baseline_mod.load_baseline`` (working tree). + + Returns None — "nothing to compare against" — when: ``ref`` is None; + ``baseline_path`` is not inside this repo (e.g. a test's temp dir); + or the file does not exist at ``ref`` yet (this PR is the one + introducing it — bootstrap, exempt from the ratchet checks). A + ``git show`` failure AFTER ``cat-file`` confirmed the path exists is a + different case entirely — a real git error, not "doesn't exist yet" — + and raises rather than returning None, so it can never be silently + read as permissive bootstrap. + """ + if ref is None: + return None + relative_path = _relative_to_repo(repo_root, baseline_path) + if relative_path is None: + return None + if not _git_path_exists_at_ref(repo_root, ref, relative_path): + return None + output = _run_git(repo_root, ["show", f"{ref}:{relative_path}"]) + if output is None: + raise RuntimeError( + f"git show {ref}:{relative_path} failed after cat-file confirmed " + "it exists — refusing to treat this as bootstrap" + ) + return baseline_mod.parse_baseline_json(output) diff --git a/scripts/craftsmanship_imports.py b/scripts/craftsmanship_imports.py new file mode 100644 index 00000000..83f230c6 --- /dev/null +++ b/scripts/craftsmanship_imports.py @@ -0,0 +1,124 @@ +"""Craftsmanship rule 3 — layer-boundary imports, a TRUE whitelist. + +Rewritten after review found the prior version was a blacklist wearing a +whitelist's name: it denied a short hardcoded list of specific imports +(``os``, ``pathlib``, a few ``mcp_server.`` prefixes) and silently +ALLOWED everything else — so `import numpy`, `import requests`, and +`import scripts.legacy_bridge` inside `core/` all passed uncaught. The +fix is structural, not a bigger blacklist: every import is now checked +against what a layer is explicitly PERMITTED to reference (derived from +``craftsmanship_layer_table.py``, itself parsed from +``docs/module-inventory.md`` § Dependency Rules — never a second +hardcoded copy of that table); anything not on the permitted list is a +violation, covering all eight documented layers, not four. +""" + +from __future__ import annotations + +import ast +import sys +from pathlib import Path + +_SCRIPTS_DIR = str(Path(__file__).resolve().parent) +if _SCRIPTS_DIR not in sys.path: + sys.path.insert(0, _SCRIPTS_DIR) +from craftsmanship_layer_table import LayerRule, is_stdlib, load_layer_rules # noqa: E402 +from craftsmanship_rules import Violation # noqa: E402 + +CHECKED_LAYERS = frozenset(load_layer_rules()) + + +def layer_of(rel_posix_path: str) -> str | None: + """Return the ``mcp_server//`` this file lives under, or None.""" + parts = rel_posix_path.split("/") + if "mcp_server" not in parts: + return None + idx = parts.index("mcp_server") + if idx + 1 >= len(parts): + return None + return parts[idx + 1] + + +def _is_type_checking_test(test: ast.expr) -> bool: + """True for ``if TYPE_CHECKING:`` / ``if typing.TYPE_CHECKING:``.""" + if isinstance(test, ast.Name): + return test.id == "TYPE_CHECKING" + if isinstance(test, ast.Attribute): + return test.attr == "TYPE_CHECKING" + return False + + +class _ImportCollector(ast.NodeVisitor): + """Collects absolute, runtime-reachable dotted import module names. + + Relative imports (``from . import x``, ``level > 0``) are skipped: they + resolve within the same package and cannot cross a layer boundary that + an absolute ``mcp_server.`` import would. Imports inside + ``if TYPE_CHECKING:`` are skipped too — a type-only forward reference + used for annotations, not a runtime dependency the layer rule polices. + """ + + def __init__(self) -> None: + self.modules: list[str] = [] + + def visit_If(self, node: ast.If) -> None: # noqa: N802 (ast.NodeVisitor API) + if _is_type_checking_test(node.test): + for stmt in node.orelse: + self.visit(stmt) + return + self.generic_visit(node) + + def visit_Import(self, node: ast.Import) -> None: # noqa: N802 + self.modules.extend(alias.name for alias in node.names) + + def visit_ImportFrom(self, node: ast.ImportFrom) -> None: # noqa: N802 + if node.level and node.level > 0: + return + if node.module: + self.modules.append(node.module) + + +def _import_violates_layer(rule: LayerRule, module: str) -> bool: + """True if ``module`` (dotted, absolute) breaks ``rule``'s whitelist. + + Every branch is a permission CHECK, not a denial check: an import that + matches none of them falls through to the final ``return True`` — the + fix for the review finding that a permitted set with an implicit + "everything else is fine" default is not a whitelist. + """ + parts = module.split(".") + top, second = parts[0], (parts[1] if len(parts) > 1 else None) + if top == "mcp_server" and second == rule.name: + return False # sibling import inside the same layer + if top == "mcp_server": + return second not in rule.allowed_layers + if is_stdlib(top): + if top in rule.stdlib_denied: + return True + return not rule.stdlib_allowed + # Third-party (neither `mcp_server.*` nor stdlib): permitted only for a + # "boundary" layer (infrastructure/validation/handlers/server/hooks) — + # Clean Architecture's adapter layers, where frameworks belong. A + # "pure" layer (shared/, core/, errors/) forbids it outright. + return rule.is_pure + + +def check_layer_violation( + rel_path: str, file_layer: str | None, tree: ast.Module +) -> list[Violation]: + """Rule 3 — an import that crosses a forbidden layer boundary. + + ``detail`` is the exact dotted module string as written, so two + different imports never collide, and the same import surviving an + otherwise-edited file still matches its baseline entry. + """ + rule = load_layer_rules().get(file_layer) if file_layer else None + if rule is None: + return [] + collector = _ImportCollector() + collector.visit(tree) + return [ + Violation(rel_path, "layer-violation", module) + for module in collector.modules + if _import_violates_layer(rule, module) + ] diff --git a/scripts/craftsmanship_layer_table.py b/scripts/craftsmanship_layer_table.py new file mode 100644 index 00000000..944aa191 --- /dev/null +++ b/scripts/craftsmanship_layer_table.py @@ -0,0 +1,217 @@ +"""Parses `docs/module-inventory.md` § Dependency Rules into layer rules. + +Fixes a DRY violation flagged in review: the previous implementation +hardcoded a Python copy of this table (`FORBIDDEN_SECOND_COMPONENT`), which +diverges from the documented table silently the moment either one changes. +This module makes the markdown table itself the single source of truth — +`craftsmanship_imports.py` derives its whitelist from what this module +parses, never from a second hand-maintained copy. + +Table shape (`docs/module-inventory.md`):: + + | Layer | May Import | Must NOT Import | + |---|---|---| + | **shared/** | Python stdlib only | core, infrastructure, handlers, server | + | **core/** | shared/ only | infrastructure, handlers, server, os/pathlib | + ... + +Each row becomes a `LayerRule`: + +- ``allowed_layers`` — the TRUE WHITELIST for ``mcp_server.`` imports, + taken from the "May Import" column's layer-name tokens. An import to any + ``mcp_server.*`` layer NOT in this set is a violation — this is the fix + for the review finding that the old blacklist let `scripts.legacy_bridge`- + style stray project imports straight through. +- ``is_pure`` — True when the "May Import" cell literally ends in the word + "only" (``"Python stdlib only"``, ``"shared/ only"``) — a textual signal + already present in the table, not an invented classification. A pure + layer forbids third-party packages entirely (Clean Architecture: `core/` + and `shared/` carry zero I/O and zero framework coupling); a non-pure + ("boundary") layer — infrastructure, validation, handlers, server, hooks + — is exactly where adapters and third-party drivers are expected to + live, so third-party imports there are not a craftsmanship violation. +- ``stdlib_allowed`` — False only for the "nothing" cell (`errors/`); + stdlib is the ambient default everywhere else, since no layer can avoid + using the language's own control-flow/typing primitives. +- ``stdlib_denied`` — specific stdlib modules named in "Must NOT Import" + as a slash-joined list (``"os/pathlib"`` for `core/`) — the one piece of + information the "May Import" column cannot express on its own. +""" + +from __future__ import annotations + +import re +import sys +from dataclasses import dataclass, field +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +MODULE_INVENTORY = REPO_ROOT / "docs" / "module-inventory.md" + +_TABLE_HEADER = "| Layer | May Import | Must NOT Import |" +_ROW_RE = re.compile(r"^\|\s*\*\*([a-z]+)/\*\*\s*\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*$") +_NOTHING = "nothing" + + +@dataclass(frozen=True) +class LayerRule: + name: str + is_pure: bool + stdlib_allowed: bool + allowed_layers: frozenset[str] = field(default_factory=frozenset) + stdlib_denied: frozenset[str] = field(default_factory=frozenset) + + +def _strip_parenthetical(token: str) -> str: + """Drop a trailing "(...)" note, e.g. "infrastructure (except via handlers)".""" + return re.sub(r"\s*\([^)]*\)\s*$", "", token).strip() + + +def _tokenize(cell: str) -> list[str]: + return [_strip_parenthetical(t).rstrip("/").strip() for t in cell.split(",")] + + +def _parse_may_import(cell: str) -> tuple[frozenset[str], bool, bool]: + """Return (allowed_layers, stdlib_allowed, is_pure) from a "May Import" cell.""" + if cell.strip().lower() == _NOTHING: + return frozenset(), False, True + is_pure = cell.strip().lower().endswith("only") + cell_body = re.sub(r"\s+only\s*$", "", cell, flags=re.IGNORECASE) + stdlib_allowed = False + layers: set[str] = set() + for token in _tokenize(cell_body): + if token.lower() == "python stdlib": + stdlib_allowed = True + elif token: + layers.add(token) + # Boundary layers never name "Python stdlib" explicitly (the table only + # calls it out for shared/ and infrastructure/) but obviously need it — + # every layer needs the language's own control-flow/typing primitives. + # Only a layer whose cell was literally "nothing" (handled above, with + # stdlib_allowed already False) denies stdlib. + if not is_pure: + stdlib_allowed = True + return frozenset(layers), stdlib_allowed, is_pure + + +def _parse_must_not_stdlib(cell: str) -> frozenset[str]: + """Extract stdlib module bans from a "Must NOT Import" cell. + + A token is a stdlib-module list (not a layer reference) when it + contains an internal "/" — e.g. "os/pathlib" — as opposed to a layer + reference, which never contains an internal slash in this column (only + "May Import" ever uses a trailing "layer/" spelling). + """ + denied: set[str] = set() + for raw_token in cell.split(","): + token = _strip_parenthetical(raw_token) + if "/" in token: + denied.update(part.strip() for part in token.split("/") if part.strip()) + return frozenset(denied) + + +def _parse_row(match: re.Match[str]) -> LayerRule: + name, may_cell, must_not_cell = match.groups() + allowed_layers, stdlib_allowed, is_pure = _parse_may_import(may_cell) + stdlib_denied = _parse_must_not_stdlib(must_not_cell) + # A pure layer whose "Must NOT Import" column names specific stdlib + # submodules (core/'s "os/pathlib") implicitly allows stdlib in + # general — banning particular submodules of an already-fully-banned + # category would otherwise be meaningless prose. Textual evidence from + # the table itself, not an override: `core/` never says "Python + # stdlib" the way `shared/` does, yet the Must-Not column only makes + # sense read this way (also matches coding-standards.md §2.2: "Core/ + # domain -> shared/common + standard library only"). + if stdlib_denied: + stdlib_allowed = True + return LayerRule( + name=name, + is_pure=is_pure, + stdlib_allowed=stdlib_allowed, + allowed_layers=allowed_layers, + stdlib_denied=stdlib_denied, + ) + + +def _check_header_present(markdown: str) -> None: + if _TABLE_HEADER not in markdown: + raise ValueError( + f"{MODULE_INVENTORY}: '§ Dependency Rules' table header not found " + f"— expected {_TABLE_HEADER!r}" + ) + + +def _extract_row_lines(markdown: str) -> list[str]: + """Return the table's raw data-row lines (header separator excluded). + + Only a line that ISN'T EVEN TRYING to be a table row (doesn't start + with ``|``) ends the table — the fix for the review finding that a + prior version treated ANY unmatched line, including a malformed row in + the MIDDLE of the table, as "the table ended", silently dropping that + row and every row after it (reproduced: one broken row after + ``validation/`` silently removed ``errors/``, ``handlers/``, + ``server/`` and ``hooks/`` from enforcement — four of eight layers, + zero signal, no error). + """ + rows: list[str] = [] + in_table = False + for line in markdown.splitlines(): + stripped = line.strip() + if stripped == _TABLE_HEADER: + in_table = True + continue + if not in_table: + continue + if not stripped.startswith("|"): + break # genuine end of table: not even attempting to be a row + if stripped.startswith("|---"): + continue # header separator row + rows.append(line) + return rows + + +def parse_layer_rules(markdown: str) -> dict[str, LayerRule]: + """Parse the § Dependency Rules table into layer name -> LayerRule. + + Fails loudly and completely, never partially — see + ``_extract_row_lines`` for why a malformed row raises instead of + silently truncating the table, and the row-count check below (a + structural self-check independent of that one) for why a duplicate + layer name silently overwriting an earlier dict entry also raises. + Also raises if the table header itself is missing (section renamed or + removed) or matched but produced zero rows. + """ + _check_header_present(markdown) + row_lines = _extract_row_lines(markdown) + rules: dict[str, LayerRule] = {} + for line in row_lines: + match = _ROW_RE.match(line) + if match is None: + raise ValueError( + f"{MODULE_INVENTORY}: table row does not match the expected " + f"'| **layer/** | ... | ... |' shape: {line!r} — refusing to " + f"silently drop this row and every row after it" + ) + rule = _parse_row(match) + rules[rule.name] = rule + if not rules: + raise ValueError( + f"{MODULE_INVENTORY}: matched the table header but parsed no rows" + ) + if len(rules) != len(row_lines): + raise ValueError( + f"{MODULE_INVENTORY}: parsed {len(rules)} layer rule(s) from " + f"{len(row_lines)} table row line(s) — a duplicate layer name " + f"silently overwrote an earlier row" + ) + return rules + + +def load_layer_rules( + module_inventory_path: Path = MODULE_INVENTORY, +) -> dict[str, LayerRule]: + return parse_layer_rules(module_inventory_path.read_text(encoding="utf-8")) + + +def is_stdlib(top_level: str) -> bool: + return top_level in sys.stdlib_module_names diff --git a/scripts/craftsmanship_rules.py b/scripts/craftsmanship_rules.py new file mode 100644 index 00000000..d4f195fa --- /dev/null +++ b/scripts/craftsmanship_rules.py @@ -0,0 +1,164 @@ +"""Craftsmanship detectors: file size, method size, layer imports, magic +numbers — the four rules ``CLAUDE.md`` § Code Style states but nothing +checks (issue: no automated pre-commit hook exists, admitted in that +section before this gate). + +Every detector returns a set of stable ``Violation`` identifiers — stable +meaning the identifier text does not change just because a line count +shifted elsewhere in the file (see each function's docstring). Stability is +what lets ``check_craftsmanship.py`` diff today's violations against a +baseline without every violation appearing "new" on every commit. + +This module owns rules 1-2 (file size, method size) plus the ``Violation`` +type and the ``scan_source`` aggregator; rules 3-4 (layer imports, magic +numbers) live in ``craftsmanship_imports.py`` / ``craftsmanship_constants.py`` +— split out because keeping all four here crossed the very 300-line cap +this gate enforces (a gate that exempted itself would not be credible). + +No I/O in this module — the caller reads the file; this module is pure AST +analysis, same discipline as ``core/`` (this file lives in ``scripts/`` +where that boundary is a convention, not an enforced layer rule). +""" + +from __future__ import annotations + +import ast +import sys +from dataclasses import dataclass +from pathlib import Path + +# source: CLAUDE.md § Code Style — "300 lines max per file" — a local +# tightening of coding-standards.md §4.1 (500). +FILE_LINE_LIMIT = 300 +# source: CLAUDE.md § Code Style — "40 lines max per method" — a local +# tightening of coding-standards.md §4.2 (50). +METHOD_LINE_LIMIT = 40 + +AUTO_GENERATED_MARKER = "auto-generated" + + +@dataclass(frozen=True) +class Violation: + """A single, stably-identified rule violation. + + ``detail`` deliberately excludes anything that drifts without the + violation itself changing (a line count, a byte offset) — see each + detector for what makes its ``detail`` stable. + """ + + file: str + kind: str + detail: str + + +def _leading_header_block(lines: list[str]) -> str: + """The file's leading run of comment/blank lines, joined. + + No fixed line count (a prior version hardcoded "scan the first 5 + lines", an arbitrary constant flagged in review): a header is + naturally delimited by the first line that is neither blank nor a + comment, so this handles a one-line marker or a multi-line license + block identically, with nothing to source or justify. + """ + header_lines: list[str] = [] + for line in lines: + stripped = line.strip() + if stripped == "" or stripped.startswith("#"): + header_lines.append(line) + continue + break + return "\n".join(header_lines) + + +def check_file_size(rel_path: str, source: str) -> list[Violation]: + """Rule 1 — file exceeds FILE_LINE_LIMIT lines. Exempt: auto-generated.""" + lines = source.splitlines() + if AUTO_GENERATED_MARKER in _leading_header_block(lines).lower(): + return [] + if len(lines) <= FILE_LINE_LIMIT: + return [] + # No line count in `detail`: the violation's identity is "this file is + # over the cap", not "this file is exactly N lines over the cap". + return [Violation(rel_path, "file-size", "exceeds 300-line cap")] + + +def _qualified_function_defs( + tree: ast.AST, +) -> list[tuple[str, ast.FunctionDef | ast.AsyncFunctionDef]]: + """Walk the tree, yielding (dotted qualified name, def node) pairs.""" + results: list[tuple[str, ast.FunctionDef | ast.AsyncFunctionDef]] = [] + _walk_defs(tree, [], results) + return results + + +def _walk_defs( + node: ast.AST, + stack: list[str], + results: list[tuple[str, ast.FunctionDef | ast.AsyncFunctionDef]], +) -> None: + for child in ast.iter_child_nodes(node): + if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)): + qualified = ".".join([*stack, child.name]) + results.append((qualified, child)) + _walk_defs(child, [*stack, child.name], results) + elif isinstance(child, ast.ClassDef): + _walk_defs(child, [*stack, child.name], results) + else: + _walk_defs(child, stack, results) + + +def check_method_size(rel_path: str, tree: ast.Module) -> list[Violation]: + """Rule 2 — a function/method body spans more than METHOD_LINE_LIMIT + lines, measured by AST (``end_lineno - lineno``) per the task + instruction, never by regex. + + ``node.lineno`` is the ``def`` line itself (decorators carry their own + ``lineno`` in the AST since Python 3.8), so a decorated function is + measured by its own body, not inflated by its decorator lines. + """ + violations = [] + for qualified, node in _qualified_function_defs(tree): + if node.end_lineno is None: + continue + span = node.end_lineno - node.lineno + if span > METHOD_LINE_LIMIT: + violations.append(Violation(rel_path, "method-size", qualified)) + return violations + + +# Sibling modules, imported at module level (not function-local — ruff +# PLC0415) as bare ``import X`` rather than ``from X import Y``: each +# sibling's own top does ``from craftsmanship_rules import Violation``, +# which only needs ``Violation`` to already exist in THIS module's +# namespace — true from this point on, since it is defined above. A bare +# ``import X`` here binds the module object without touching any of its +# attributes yet, so it is safe regardless of which of the three modules +# Python loads first; only ``scan_source`` below, called later, actually +# dereferences into them. +_scripts_dir = str(Path(__file__).resolve().parent) +if _scripts_dir not in sys.path: + sys.path.insert(0, _scripts_dir) +import craftsmanship_constants # noqa: E402 +import craftsmanship_imports # noqa: E402 + + +def scan_source(rel_path: str, source: str) -> list[Violation]: + """Run all four detectors over one file's source text. + + Returns an empty list (never raises) for a file that fails to parse — + the caller is expected to have already selected ``.py`` files; a syntax + error here means the file is broken independent of this gate, and this + gate's job is craftsmanship, not "does it parse". + """ + try: + tree = ast.parse(source, filename=rel_path) + except SyntaxError: + return [] + + layer = craftsmanship_imports.layer_of(rel_path) + return [ + *check_file_size(rel_path, source), + *check_method_size(rel_path, tree), + *craftsmanship_imports.check_layer_violation(rel_path, layer, tree), + *craftsmanship_constants.check_unsourced_constants(rel_path, tree, source), + ] diff --git a/tests_py/scripts/_craftsmanship_support.py b/tests_py/scripts/_craftsmanship_support.py new file mode 100644 index 00000000..38aacde4 --- /dev/null +++ b/tests_py/scripts/_craftsmanship_support.py @@ -0,0 +1,67 @@ +"""Single-load helper for the four ``scripts/craftsmanship_*.py`` modules. + +Every ``test_craftsmanship_*.py`` file imports this module (a genuine +package import, cached by Python's normal import machinery — this file +lives under ``tests_py/scripts/``, a real package via its ``__init__.py``) +instead of each independently calling +``importlib.util.spec_from_file_location`` on ``craftsmanship_rules.py``. + +Why that independence was wrong: ``craftsmanship_rules.py`` defines the +frozen dataclass ``Violation``, and its own body imports its two siblings +(``craftsmanship_imports.py`` / ``craftsmanship_constants.py``), each of +which imports ``Violation`` back. Every separate +``spec_from_file_location`` call re-executes the file from scratch, +minting a NEW ``Violation`` class each time — dataclass equality compares +``__class__ is __class__``, so a baseline-diff test comparing a +``Violation`` from one load against one from another silently always +disagrees. Loading once, here, and sharing the result closes that. + +The dotted, path-derived module name (``scripts.craftsmanship_rules``, not +a bare name) is preserved for mutmut's trampoline — same idiom as +``check_doc_claims.py``'s sibling loads (see that file's test for the full +rationale). +""" + +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path + +_SCRIPTS = Path(__file__).resolve().parents[2] / "scripts" + + +def _load_once(dotted_name: str, filename: str): + bare_name = dotted_name.rsplit(".", 1)[-1] + if dotted_name in sys.modules: + return sys.modules[dotted_name] + if bare_name in sys.modules: + # craftsmanship_rules.py's own body already loaded this sibling + # under its bare name — reuse it instead of executing it again. + return sys.modules[bare_name] + spec = importlib.util.spec_from_file_location(dotted_name, _SCRIPTS / filename) + module = importlib.util.module_from_spec(spec) + # Registered under BOTH names before exec: the dotted name for + # mutmut/dataclasses (`_process_class` looks up + # `sys.modules[cls.__module__]`), the bare name because + # craftsmanship_rules.py's own body does `import craftsmanship_constants` + # / `import craftsmanship_imports`, each of which does `from + # craftsmanship_rules import Violation` — that must resolve to THIS + # in-progress module, not trigger a second recursive load. + sys.modules[dotted_name] = module + sys.modules[bare_name] = module + spec.loader.exec_module(module) + return module + + +rules = _load_once("scripts.craftsmanship_rules", "craftsmanship_rules.py") +craftsmanship_imports = _load_once( + "scripts.craftsmanship_imports", "craftsmanship_imports.py" +) +craftsmanship_constants = _load_once( + "scripts.craftsmanship_constants", "craftsmanship_constants.py" +) +craftsmanship_layer_table = _load_once( + "scripts.craftsmanship_layer_table", "craftsmanship_layer_table.py" +) +baseline_mod = _load_once("scripts.craftsmanship_baseline", "craftsmanship_baseline.py") diff --git a/tests_py/scripts/test_check_craftsmanship.py b/tests_py/scripts/test_check_craftsmanship.py new file mode 100644 index 00000000..7aa49ade --- /dev/null +++ b/tests_py/scripts/test_check_craftsmanship.py @@ -0,0 +1,261 @@ +"""Tests for scripts/check_craftsmanship.py — the CLI orchestrator. + +Git interaction is mocked throughout (``gate.craftsmanship_git._run_git``) +so these tests never depend on the real repository's history or network +access; the detectors themselves are covered by the sibling +``test_craftsmanship_*`` modules and are exercised here only through the +plumbing (file scanning, base-ref resolution, exit codes). The two +end-to-end exploit reproductions +(``test_check_craftsmanship_exploits.py::SneakyLimitExploitTests``, +``FalsifiedRemovalExploitTests``) live in their own file — real `git` +against a throwaway repo, no mocking, split out to stay under this +module's own 300-line cap. +""" + +from __future__ import annotations + +import contextlib +import importlib.util +import io +import tempfile +import unittest +from pathlib import Path +from unittest import mock + +# Import the shared support FIRST: it registers craftsmanship_rules (and +# its siblings) under their bare names in sys.modules before gate.py loads +# — gate.py's own `import craftsmanship_rules as rules` then reuses that +# single cached instance instead of executing a second, dataclass- +# incompatible copy (see _craftsmanship_support.py's docstring). +import tests_py.scripts._craftsmanship_support # noqa: F401 + +_SCRIPTS = Path(__file__).resolve().parents[2] / "scripts" +_spec = importlib.util.spec_from_file_location( + "scripts.check_craftsmanship", _SCRIPTS / "check_craftsmanship.py" +) +gate = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(gate) + + +class ResolveBaseRefTests(unittest.TestCase): + def test_explicit_ref_wins_when_it_exists(self) -> None: + with mock.patch.object(gate.craftsmanship_git, "_run_git", return_value="ok"): + self.assertEqual( + gate.craftsmanship_git.resolve_base_ref(gate.REPO_ROOT, "mybranch"), + "mybranch", + ) + + def test_falls_back_to_origin_main(self) -> None: + def fake(repo_root: Path, args: list[str]): + return "ok" if args == ["rev-parse", "--verify", "origin/main"] else None + + with mock.patch.object(gate.craftsmanship_git, "_run_git", side_effect=fake): + self.assertEqual( + gate.craftsmanship_git.resolve_base_ref(gate.REPO_ROOT, None), + "origin/main", + ) + + def test_none_when_nothing_resolves(self) -> None: + with mock.patch.object(gate.craftsmanship_git, "_run_git", return_value=None): + self.assertIsNone( + gate.craftsmanship_git.resolve_base_ref(gate.REPO_ROOT, None) + ) + + +class ChangedPythonFilesTests(unittest.TestCase): + def test_filters_blank_lines_and_sorts(self) -> None: + with mock.patch.object( + gate.craftsmanship_git, "_run_git", return_value="b.py\n\na.py\n" + ): + self.assertEqual( + gate.craftsmanship_git.changed_python_files( + gate.REPO_ROOT, "origin/main" + ), + ["a.py", "b.py"], + ) + + def test_none_when_git_diff_fails(self) -> None: + with mock.patch.object(gate.craftsmanship_git, "_run_git", return_value=None): + self.assertIsNone( + gate.craftsmanship_git.changed_python_files( + gate.REPO_ROOT, "origin/main" + ) + ) + + +class ScanFilesTests(unittest.TestCase): + def test_missing_file_yields_nothing(self) -> None: + self.assertEqual(gate.scan_files(["does/not/exist.py"]), set()) + + def test_reads_and_scans_a_real_file(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + target = Path(tmp) / "clean.py" + target.write_text("x = 1\n") + with mock.patch.object(gate, "REPO_ROOT", Path(tmp)): + self.assertEqual(gate.scan_files(["clean.py"]), set()) + + +class MainExitCodeTests(unittest.TestCase): + def _run(self, argv: list[str]) -> tuple[int, str]: + buf = io.StringIO() + with contextlib.redirect_stderr(buf), contextlib.redirect_stdout(buf): + code = gate.main(argv) + return code, buf.getvalue() + + def test_explicit_clean_file_passes(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + target = Path(tmp) / "clean.py" + target.write_text("x = 1\n") + with mock.patch.object(gate, "REPO_ROOT", Path(tmp)): + code, out = self._run( + ["--baseline", str(Path(tmp) / "baseline.json"), "clean.py"] + ) + self.assertEqual(code, 0) + self.assertIn("OK", out) + + def test_explicit_dirty_file_fails_with_no_baseline(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + target = Path(tmp) / "dirty.py" + target.write_text("SENTINEL = 37\n") + with mock.patch.object(gate, "REPO_ROOT", Path(tmp)): + code, out = self._run( + ["--baseline", str(Path(tmp) / "baseline.json"), "dirty.py"] + ) + self.assertEqual(code, 1) + self.assertIn("SENTINEL", out) + + def test_baselined_violation_passes(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + target = Path(tmp) / "dirty.py" + target.write_text("SENTINEL = 37\n") + baseline_path = Path(tmp) / "baseline.json" + gate.baseline_mod.save_baseline( + baseline_path, + {gate.rules.Violation("dirty.py", "unsourced-constant", "SENTINEL")}, + ) + with mock.patch.object(gate, "REPO_ROOT", Path(tmp)): + code, out = self._run(["--baseline", str(baseline_path), "dirty.py"]) + self.assertEqual(code, 0) + self.assertIn("OK", out) + + def test_no_base_ref_and_no_files_exits_two(self) -> None: + with mock.patch.object( + gate.craftsmanship_git, "resolve_base_ref", return_value=None + ): + code, out = self._run([]) + self.assertEqual(code, 2) + + def test_write_baseline_regenerates_from_tracked_files(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + baseline_path = Path(tmp) / "baseline.json" + with ( + mock.patch.object(gate, "REPO_ROOT", Path(tmp)), + mock.patch.object( + gate.craftsmanship_git, "all_tracked_python_files", return_value=[] + ), + ): + code, out = self._run( + ["--write-baseline", "--baseline", str(baseline_path)] + ) + self.assertEqual(code, 0) + self.assertTrue(baseline_path.exists()) + + +class GitPathExistsAtRefTests(unittest.TestCase): + """Self-audited third instance of the "ambiguous failure read as a + negative" pattern the review round's other two findings shared: a bare + ``except: return False`` here would let ANY git failure — not just a + genuinely absent path — silently trigger ``load_baseline_from_ref``'s + bootstrap fallback to the tamperable working-tree baseline. + """ + + def test_real_absent_path_returns_false(self) -> None: + # Live integration: a path that has never existed at a real, + # resolvable ref in THIS repository. + result = gate.craftsmanship_git._git_path_exists_at_ref( + gate.REPO_ROOT, "HEAD", "this/path/has/never/existed.json" + ) + self.assertFalse(result) + + def test_real_existing_path_returns_true(self) -> None: + result = gate.craftsmanship_git._git_path_exists_at_ref( + gate.REPO_ROOT, "HEAD", "CLAUDE.md" + ) + self.assertTrue(result) + + def test_invalid_ref_raises_not_returns_false(self) -> None: + # A ref that does not resolve at all must never be silently read + # as "the path just doesn't exist yet" — resolve_base_ref should + # already have filtered this out, but this function must not + # compound a caller's bug into a security-relevant fail-open. + with self.assertRaises(RuntimeError): + gate.craftsmanship_git._git_path_exists_at_ref( + gate.REPO_ROOT, "totally-bogus-ref-xyz", "CLAUDE.md" + ) + + def test_unexpected_git_failure_raises(self) -> None: + fake_result = mock.Mock(returncode=128, stderr="fatal: something unexpected") + with mock.patch("subprocess.run", return_value=fake_result): + with self.assertRaises(RuntimeError): + gate.craftsmanship_git._git_path_exists_at_ref( + gate.REPO_ROOT, "main", "x.json" + ) + + +class LoadBaselineFromRefTests(unittest.TestCase): + """Unit-level coverage of the git-show-based base-ref loader; the + end-to-end exploit reproductions live in test_check_craftsmanship_exploits.py. + """ + + def test_none_ref_gives_none(self) -> None: + self.assertIsNone( + gate.craftsmanship_git.load_baseline_from_ref( + gate.REPO_ROOT, None, Path("/tmp/x.json") + ) + ) + + def test_path_outside_repo_gives_none(self) -> None: + result = gate.craftsmanship_git.load_baseline_from_ref( + Path("/repo"), "main", Path("/elsewhere/x.json") + ) + self.assertIsNone(result) + + def test_path_absent_at_ref_gives_none_bootstrap(self) -> None: + with mock.patch.object( + gate.craftsmanship_git, "_git_path_exists_at_ref", return_value=False + ): + result = gate.craftsmanship_git.load_baseline_from_ref( + gate.REPO_ROOT, "main", gate.REPO_ROOT / "x.json" + ) + self.assertIsNone(result) + + def test_show_failure_after_confirmed_existence_raises(self) -> None: + with ( + mock.patch.object( + gate.craftsmanship_git, "_git_path_exists_at_ref", return_value=True + ), + mock.patch.object(gate.craftsmanship_git, "_run_git", return_value=None), + ): + with self.assertRaises(RuntimeError): + gate.craftsmanship_git.load_baseline_from_ref( + gate.REPO_ROOT, "main", gate.REPO_ROOT / "x.json" + ) + + def test_parses_show_output(self) -> None: + payload = ( + '{"violations": [{"file": "a.py", "kind": "file-size", "detail": "d"}]}' + ) + with ( + mock.patch.object( + gate.craftsmanship_git, "_git_path_exists_at_ref", return_value=True + ), + mock.patch.object(gate.craftsmanship_git, "_run_git", return_value=payload), + ): + result = gate.craftsmanship_git.load_baseline_from_ref( + gate.REPO_ROOT, "main", gate.REPO_ROOT / "x.json" + ) + self.assertEqual(result, {gate.rules.Violation("a.py", "file-size", "d")}) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests_py/scripts/test_check_craftsmanship_exploits.py b/tests_py/scripts/test_check_craftsmanship_exploits.py new file mode 100644 index 00000000..fa6db14b --- /dev/null +++ b/tests_py/scripts/test_check_craftsmanship_exploits.py @@ -0,0 +1,210 @@ +"""End-to-end exploit reproductions for scripts/check_craftsmanship.py's +ratchet, each against a REAL throwaway git repository — no mocking, +because both exploits are specifically about what a real ``git show`` +against an immutable base ref sees versus what the mutable working tree +says. Split out of test_check_craftsmanship.py to stay under the +300-line cap the gate itself enforces (self-application). + +- ``SneakyLimitExploitTests`` — round 1: add a violation, then run + ``--write-baseline`` in the same tree to launder it via an ADDITION. +- ``FalsifiedRemovalExploitTests`` — round 2, the mirror image: hand- + delete a baseline entry's JSON line without touching the violating + source file, laundering it via a REMOVAL. +""" + +from __future__ import annotations + +import importlib.util +import subprocess +import tempfile +import unittest +from pathlib import Path + +import tests_py.scripts._craftsmanship_support # noqa: F401 + +_SCRIPTS = Path(__file__).resolve().parents[2] / "scripts" +_spec = importlib.util.spec_from_file_location( + "scripts.check_craftsmanship", _SCRIPTS / "check_craftsmanship.py" +) +gate = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(gate) + +_CRAFTSMANSHIP_MODULES = ( + "craftsmanship_rules.py", + "craftsmanship_imports.py", + "craftsmanship_constants.py", + "craftsmanship_baseline.py", + "craftsmanship_layer_table.py", + "craftsmanship_git.py", + "check_craftsmanship.py", +) + + +class _ThrowawayRepoTestCase(unittest.TestCase): + """Shared plumbing for the two end-to-end exploit-reproduction suites.""" + + def _git(self, repo: Path, *args: str) -> None: + subprocess.run( + ["git", *args], cwd=repo, check=True, capture_output=True, text=True + ) + + def _init_repo(self, repo: Path) -> None: + repo.mkdir(parents=True, exist_ok=True) + (repo / "scripts").mkdir() + (repo / "docs").mkdir() + (repo / "mcp_server" / "core").mkdir(parents=True) + for name in _CRAFTSMANSHIP_MODULES: + (repo / "scripts" / name).write_text((_SCRIPTS / name).read_text()) + (repo / "docs" / "module-inventory.md").write_text( + (_SCRIPTS.parent / "docs" / "module-inventory.md").read_text() + ) + # `-b main` pins the initial branch name explicitly: this repo's + # own default is "main" locally, but git's `init.defaultBranch` + # is a per-installation config (CI runners are not guaranteed to + # match), and every test here hardcodes "main" as the base ref. + self._git(repo, "init", "-q", "-b", "main") + self._git(repo, "config", "user.email", "demo@example.com") + self._git(repo, "config", "user.name", "demo") + + def _run_gate_in(self, repo: Path, *args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + ["python3", str(repo / "scripts" / "check_craftsmanship.py"), *args], + cwd=repo, + capture_output=True, + text=True, + ) + + +class SneakyLimitExploitTests(_ThrowawayRepoTestCase): + """Reproduces the review-round exploit and its close: a working-tree- + only baseline can be self-regenerated (``--write-baseline`` in the + same tree) to launder a violation the gate had just refused. + """ + + def test_exploit_is_closed(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + repo = Path(tmp) / "repo" + self._init_repo(repo) + (repo / "mcp_server" / "core" / "sample.py").write_text("SAFE = 1\n") + self._git(repo, "add", "-A") + self._git(repo, "commit", "-q", "-m", "main v1") + self._run_gate_in(repo, "--write-baseline") + self._git(repo, "add", ".craftsmanship-baseline.json") + self._git(repo, "commit", "-q", "-m", "main v2: baseline committed") + + self._git(repo, "checkout", "-q", "-b", "feature") + sample = repo / "mcp_server" / "core" / "sample.py" + sample.write_text(sample.read_text() + "SNEAKY_LIMIT = 12345\n") + self._git(repo, "add", "-A") + self._git(repo, "commit", "-q", "-m", "feature: sneaky const") + + # Step 1: the gate blocks the new violation. + blocked = self._run_gate_in(repo, "--base", "main") + self.assertEqual(blocked.returncode, 1, blocked.stdout + blocked.stderr) + self.assertIn("SNEAKY_LIMIT", blocked.stdout + blocked.stderr) + + # Step 2: the attack — launder it via a same-tree regenerate. + self._run_gate_in(repo, "--write-baseline") + + # Step 3: must STILL block — this is the exploit's close. + after_attack = self._run_gate_in(repo, "--base", "main") + self.assertEqual( + after_attack.returncode, 1, after_attack.stdout + after_attack.stderr + ) + self.assertIn("SNEAKY_LIMIT", after_attack.stdout + after_attack.stderr) + self.assertIn("ADDED", after_attack.stdout + after_attack.stderr) + + def test_legitimate_shrink_only_baseline_update_passes(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + repo = Path(tmp) / "repo" + self._init_repo(repo) + stale_file = repo / "mcp_server" / "core" / "todelete.py" + stale_file.write_text("STALE_ONE = 999999\n") + self._git(repo, "add", "-A") + self._git(repo, "commit", "-q", "-m", "main v1: has a violation") + self._run_gate_in(repo, "--write-baseline") + self._git(repo, "add", ".craftsmanship-baseline.json") + self._git(repo, "commit", "-q", "-m", "main v2: baseline committed") + + self._git(repo, "checkout", "-q", "-b", "fixer") + stale_file.unlink() + self._git(repo, "add", "-A") + + # Fixed the code but forgot to prune -> STALE, still blocks. + forgot_prune = self._run_gate_in(repo, "--base", "main") + self.assertEqual(forgot_prune.returncode, 1) + self.assertIn("STALE", forgot_prune.stdout + forgot_prune.stderr) + + # Regenerating is a pure shrink (one entry removed, none + # added) -> passes. + self._run_gate_in(repo, "--write-baseline") + self._git(repo, "add", "-A") + clean = self._run_gate_in(repo, "--base", "main") + self.assertEqual(clean.returncode, 0, clean.stdout + clean.stderr) + + +class FalsifiedRemovalExploitTests(_ThrowawayRepoTestCase): + """Reproduces the SECOND review-round exploit — the mirror image of + the first — and its close: hand-deleting a baseline entry's JSON line + without touching the violating source file it describes. Neither + ``added_entries`` (only looks at additions) nor ``new_violations`` + (only rescans this PR's diffed files, and the baseline JSON isn't a + ``.py`` file) saw this before ``falsified_removals`` existed. + """ + + def test_exploit_is_closed(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + repo = Path(tmp) / "repo" + self._init_repo(repo) + (repo / "mcp_server" / "core" / "sample.py").write_text("SNEAKY = 12345\n") + self._git(repo, "add", "-A") + self._git(repo, "commit", "-q", "-m", "main v1") + self._run_gate_in(repo, "--write-baseline") + self._git(repo, "add", ".craftsmanship-baseline.json") + self._git( + repo, "commit", "-q", "-m", "main v2: baseline committed with SNEAKY" + ) + + # main is clean at this point (SNEAKY is grandfathered). + self.assertEqual(self._run_gate_in(repo, "--base", "main").returncode, 0) + + self._git(repo, "checkout", "-q", "-b", "feature") + baseline_path = repo / ".craftsmanship-baseline.json" + # The attack: hand-delete the entry, touch NOTHING else. The + # source file (still carrying SNEAKY = 12345) is untouched, so + # it never even enters the diff-scanned file set. + baseline_path.write_text('{"violations": []}\n') + self._git(repo, "add", ".craftsmanship-baseline.json") + self._git(repo, "commit", "-q", "-m", "feature: quietly drop the entry") + + result = self._run_gate_in(repo, "--base", "main") + self.assertEqual(result.returncode, 1, result.stdout + result.stderr) + self.assertIn("REMOVED", result.stdout + result.stderr) + self.assertIn("SNEAKY", result.stdout + result.stderr) + + def test_legitimate_fix_and_prune_passes(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + repo = Path(tmp) / "repo" + self._init_repo(repo) + target = repo / "mcp_server" / "core" / "sample.py" + target.write_text("SNEAKY = 12345\n") + self._git(repo, "add", "-A") + self._git(repo, "commit", "-q", "-m", "main v1") + self._run_gate_in(repo, "--write-baseline") + self._git(repo, "add", ".craftsmanship-baseline.json") + self._git( + repo, "commit", "-q", "-m", "main v2: baseline committed with SNEAKY" + ) + + self._git(repo, "checkout", "-q", "-b", "fixer") + # Legitimate: actually fix the code, THEN regenerate (which + # naturally prunes the now-nonexistent violation). + target.write_text("SNEAKY = 12345 # source: measured 2026-08-10\n") + self._run_gate_in(repo, "--write-baseline") + self._git(repo, "add", "-A") + result = self._run_gate_in(repo, "--base", "main") + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests_py/scripts/test_craftsmanship_baseline.py b/tests_py/scripts/test_craftsmanship_baseline.py new file mode 100644 index 00000000..abaf984f --- /dev/null +++ b/tests_py/scripts/test_craftsmanship_baseline.py @@ -0,0 +1,162 @@ +"""Tests for scripts/craftsmanship_baseline.py — the ratchet. + +Pins the four failure directions the ratchet enforces: a violation absent +from the base-ref baseline is NEW (blocks); a baseline entry whose +violation no longer reproduces is STALE (also blocks, forcing the +baseline to be pruned rather than silently drifting from reality); a +baseline-file entry present in the working tree but absent from the base +ref's committed baseline is ADDED (blocks — the file may only shrink +within a PR, see ``added_entries``); and an entry present at the base ref +but absent from the working tree whose violation STILL reproduces is a +FALSIFIED removal (blocks — the mirror image of ADDED, see +``falsified_removals``). Both end-to-end exploits these last two checks +close — a working-tree-only baseline can be self-regenerated to launder a +new violation, or hand-edited to launder a removal — are reproduced +against real throwaway git repos in ``test_check_craftsmanship.py`` +(``SneakyLimitExploitTests``, ``FalsifiedRemovalExploitTests``). +""" + +from __future__ import annotations + +import json +import tempfile +import unittest +from pathlib import Path + +from tests_py.scripts._craftsmanship_support import baseline_mod, rules + +V1 = rules.Violation("a.py", "file-size", "exceeds 300-line cap") +V2 = rules.Violation("b.py", "method-size", "C.m") + + +class LoadSaveTests(unittest.TestCase): + def test_load_missing_file_returns_empty_set(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + self.assertEqual(baseline_mod.load_baseline(Path(tmp) / "none.json"), set()) + + def test_round_trip(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "baseline.json" + baseline_mod.save_baseline(path, {V1, V2}) + loaded = baseline_mod.load_baseline(path) + self.assertEqual(loaded, {V1, V2}) + + def test_saved_file_is_sorted_json(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "baseline.json" + baseline_mod.save_baseline(path, {V2, V1}) + data = json.loads(path.read_text()) + files = [entry["file"] for entry in data["violations"]] + self.assertEqual(files, sorted(files)) + + +class DiffTests(unittest.TestCase): + def test_new_violation_not_in_baseline(self) -> None: + current = {V1, V2} + result = baseline_mod.new_violations(current, {V1}) + self.assertEqual(result, [V2]) + + def test_no_new_violations_when_subset_of_baseline(self) -> None: + self.assertEqual(baseline_mod.new_violations({V1}, {V1, V2}), []) + + def test_stale_entry_when_violation_no_longer_reproduces(self) -> None: + # V2 is baselined for b.py, but a fresh scan of b.py finds nothing. + stale = baseline_mod.stale_entries({V1, V2}, {"a.py": {V1}, "b.py": set()}) + self.assertEqual(stale, [V2]) + + def test_no_stale_entries_when_everything_still_reproduces(self) -> None: + stale = baseline_mod.stale_entries({V1, V2}, {"a.py": {V1}, "b.py": {V2}}) + self.assertEqual(stale, []) + + def test_file_missing_from_rescan_map_is_stale(self) -> None: + # A baselined file that no longer exists (deleted/renamed) rescans + # to nothing — every entry for it is stale. + stale = baseline_mod.stale_entries({V1}, {}) + self.assertEqual(stale, [V1]) + + +class AddedEntriesTests(unittest.TestCase): + """The ratchet-file check: the baseline file may only shrink.""" + + def test_entry_added_beyond_base_is_reported(self) -> None: + working = {V1, V2} + base = {V1} + self.assertEqual(baseline_mod.added_entries(working, base), [V2]) + + def test_pure_shrink_reports_nothing(self) -> None: + working = {V1} + base = {V1, V2} + self.assertEqual(baseline_mod.added_entries(working, base), []) + + def test_identical_baseline_reports_nothing(self) -> None: + self.assertEqual(baseline_mod.added_entries({V1, V2}, {V1, V2}), []) + + +class FalsifiedRemovalsTests(unittest.TestCase): + """The mirror-image ratchet check: an entry present at the base ref + but absent from the working tree is only a legitimate prune if its + violation genuinely no longer reproduces. + """ + + def test_removed_entry_whose_violation_still_reproduces_is_falsified(self) -> None: + # V1 was in base, is absent from working (hand-deleted), but a.py + # STILL contains it per the fresh rescan — falsified. + base = {V1, V2} + working = {V2} + rescanned = {"a.py": {V1}} + self.assertEqual( + baseline_mod.falsified_removals(base, working, rescanned), [V1] + ) + + def test_removed_entry_whose_violation_is_genuinely_gone_is_not_falsified( + self, + ) -> None: + # Legitimate fix-and-prune: V1 removed from working, and a fresh + # scan of a.py no longer finds it. + base = {V1, V2} + working = {V2} + rescanned = {"a.py": set()} + self.assertEqual(baseline_mod.falsified_removals(base, working, rescanned), []) + + def test_entry_still_present_in_working_is_never_falsified(self) -> None: + # Not a removal at all — untouched entries are added_entries'/ + # stale_entries' concern, not this one's. + base = {V1} + working = {V1} + rescanned = {"a.py": {V1}} + self.assertEqual(baseline_mod.falsified_removals(base, working, rescanned), []) + + def test_file_missing_from_rescan_map_means_not_falsified(self) -> None: + # Deleted file: nothing to rescan, so nothing reproduces -> a + # legitimate removal (the file, and everything in it, is gone). + base = {V1} + working: set[rules.Violation] = set() + self.assertEqual(baseline_mod.falsified_removals(base, working, {}), []) + + +class CountByKindTests(unittest.TestCase): + def test_counts_grouped_and_sorted_by_kind(self) -> None: + v3 = rules.Violation("c.py", "file-size", "exceeds 300-line cap") + counts = baseline_mod.count_by_kind({V1, V2, v3}) + self.assertEqual(counts, {"file-size": 2, "method-size": 1}) + + def test_empty_input_gives_empty_dict(self) -> None: + self.assertEqual(baseline_mod.count_by_kind(set()), {}) + + +class ParseBaselineJsonTests(unittest.TestCase): + def test_parses_violations_array(self) -> None: + text = json.dumps( + {"violations": [{"file": "a.py", "kind": "file-size", "detail": "d"}]} + ) + self.assertEqual( + baseline_mod.parse_baseline_json(text), + {rules.Violation("a.py", "file-size", "d")}, + ) + + def test_missing_violations_key_gives_empty_set(self) -> None: + self.assertEqual(baseline_mod.parse_baseline_json("{}"), set()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests_py/scripts/test_craftsmanship_constants.py b/tests_py/scripts/test_craftsmanship_constants.py new file mode 100644 index 00000000..04adaf39 --- /dev/null +++ b/tests_py/scripts/test_craftsmanship_constants.py @@ -0,0 +1,70 @@ +"""Tests for scripts/craftsmanship_constants.py — rule 4 (unsourced +module-scope numeric literals). +""" + +from __future__ import annotations + +import ast +import unittest + +from tests_py.scripts._craftsmanship_support import ( + craftsmanship_constants as constants_mod, +) + + +def _find(source: str): + tree = ast.parse(source) + return constants_mod.check_unsourced_constants("f.py", tree, source) + + +class UnsourcedConstantTests(unittest.TestCase): + def test_unsourced_constant_fails(self) -> None: + violations = _find("TIMEOUT_SECONDS = 37\n") + self.assertEqual([v.detail for v in violations], ["TIMEOUT_SECONDS"]) + + def test_preceding_source_comment_passes(self) -> None: + source = ( + "# source: measured 2026-08-10, benchmarks/x.json\nTIMEOUT_SECONDS = 37\n" + ) + self.assertEqual(_find(source), []) + + def test_trailing_inline_source_comment_passes(self) -> None: + source = "TIMEOUT_SECONDS = 37 # source: RFC 9110 timeout guidance\n" + self.assertEqual(_find(source), []) + + def test_blank_line_breaks_the_comment_chain(self) -> None: + source = "# source: measured\n\nTIMEOUT_SECONDS = 37\n" + violations = _find(source) + self.assertEqual([v.detail for v in violations], ["TIMEOUT_SECONDS"]) + + def test_annassign_target_is_checked(self) -> None: + violations = _find("TIMEOUT_SECONDS: int = 37\n") + self.assertEqual([v.detail for v in violations], ["TIMEOUT_SECONDS"]) + + def test_negative_one_is_trivial(self) -> None: + self.assertEqual(_find("SENTINEL = -1\n"), []) + + def test_zero_one_two_hundred_thousand_are_trivial(self) -> None: + source = "A = 0\nB = 1\nC = 2\nD = 100\nE = 1000\n" + self.assertEqual(_find(source), []) + + def test_usual_power_of_two_is_trivial(self) -> None: + self.assertEqual(_find("BUFFER_SIZE = 4096\n"), []) + + def test_non_trivial_negative_number_still_needs_a_source(self) -> None: + violations = _find("OFFSET = -37\n") + self.assertEqual([v.detail for v in violations], ["OFFSET"]) + + def test_function_local_constant_is_not_checked(self) -> None: + source = "def f():\n TIMEOUT_SECONDS = 37\n return TIMEOUT_SECONDS\n" + self.assertEqual(_find(source), []) + + def test_string_constant_is_not_checked(self) -> None: + self.assertEqual(_find('GREETING = "hello"\n'), []) + + def test_bool_constant_is_not_checked(self) -> None: + self.assertEqual(_find("ENABLED = True\n"), []) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests_py/scripts/test_craftsmanship_imports.py b/tests_py/scripts/test_craftsmanship_imports.py new file mode 100644 index 00000000..cee50306 --- /dev/null +++ b/tests_py/scripts/test_craftsmanship_imports.py @@ -0,0 +1,183 @@ +"""Tests for scripts/craftsmanship_imports.py — rule 3 (layer boundaries), +a TRUE whitelist over all eight documented layers. + +Pins the exact review-round counter-examples that broke the prior +blacklist implementation (`import numpy`, `import requests`, `import +scripts.legacy_bridge` inside `core/`, all previously silent), plus the +boundary-layer symmetry (third-party IS permitted in infrastructure/, +since that is Clean Architecture's adapter layer) and the two AST edge +cases: a conditional import under ``if TYPE_CHECKING:`` is exempt, and a +relative import (``from . import x``) never counts as a boundary crossing. +""" + +from __future__ import annotations + +import ast +import unittest + +from tests_py.scripts._craftsmanship_support import craftsmanship_imports as imports_mod + + +def _find(source: str, file_layer: str): + tree = ast.parse(source) + return imports_mod.check_layer_violation("f.py", file_layer, tree) + + +class ReviewCounterExampleTests(unittest.TestCase): + """The exact three imports review confirmed passed silently in core/.""" + + def test_numpy_in_core_is_now_caught(self) -> None: + violations = _find("import numpy\n", "core") + self.assertEqual([v.detail for v in violations], ["numpy"]) + + def test_requests_in_core_is_now_caught(self) -> None: + violations = _find("import requests\n", "core") + self.assertEqual([v.detail for v in violations], ["requests"]) + + def test_stray_project_package_in_core_is_now_caught(self) -> None: + violations = _find("import scripts.legacy_bridge\n", "core") + self.assertEqual([v.detail for v in violations], ["scripts.legacy_bridge"]) + + +class PureLayerTests(unittest.TestCase): + def test_shared_forbids_third_party_import(self) -> None: + violations = _find("import numpy\n", "shared") + self.assertEqual([v.detail for v in violations], ["numpy"]) + + def test_shared_allows_stdlib(self) -> None: + self.assertEqual(_find("import os\nimport re\n", "shared"), []) + + def test_shared_allows_sibling_shared_import(self) -> None: + self.assertEqual(_find("import mcp_server.shared.text\n", "shared"), []) + + def test_core_forbids_pathlib(self) -> None: + violations = _find("from pathlib import Path\n", "core") + self.assertEqual([v.detail for v in violations], ["pathlib"]) + + def test_core_forbids_os(self) -> None: + violations = _find("import os\n", "core") + self.assertEqual([v.detail for v in violations], ["os"]) + + def test_core_forbids_infrastructure_import(self) -> None: + violations = _find("from mcp_server.infrastructure.pg_store import X\n", "core") + self.assertEqual(len(violations), 1) + + def test_core_forbids_validation_and_errors_too(self) -> None: + # Not in core/'s whitelist ({"shared"}) even though neither was in + # the old hardcoded blacklist — the true-whitelist fix's whole point. + self.assertEqual( + len(_find("from mcp_server.validation.schemas import X\n", "core")), 1 + ) + self.assertEqual( + len(_find("from mcp_server.errors import ValidationError\n", "core")), 1 + ) + + def test_core_allows_shared_import_and_general_stdlib(self) -> None: + self.assertEqual(_find("import re\nimport typing\n", "core"), []) + self.assertEqual(_find("from mcp_server.shared.text import f\n", "core"), []) + + def test_errors_forbids_everything_but_stdlib_ambient(self) -> None: + self.assertEqual(len(_find("import re\n", "errors")), 1) + self.assertEqual( + len(_find("from mcp_server.shared.text import f\n", "errors")), 1 + ) + self.assertEqual(len(_find("import numpy\n", "errors")), 1) + + +class BoundaryLayerTests(unittest.TestCase): + """Infrastructure/validation/handlers/server/hooks: adapter layers — + third-party is the point, not a violation; mcp_server.* cross-refs are + still a true whitelist. + """ + + def test_infrastructure_allows_third_party(self) -> None: + self.assertEqual(_find("import numpy\n", "infrastructure"), []) + self.assertEqual(_find("import psycopg2\n", "infrastructure"), []) + + def test_infrastructure_forbids_core_and_handlers(self) -> None: + self.assertEqual( + len(_find("from mcp_server.core.x import y\n", "infrastructure")), 1 + ) + self.assertEqual( + len(_find("from mcp_server.handlers.x import y\n", "infrastructure")), 1 + ) + + def test_validation_allows_shared_and_errors_only(self) -> None: + self.assertEqual( + _find("from mcp_server.shared.text import f\n", "validation"), [] + ) + self.assertEqual( + _find("from mcp_server.errors import ValidationError\n", "validation"), [] + ) + self.assertEqual( + len(_find("from mcp_server.core.x import y\n", "validation")), 1 + ) + + def test_handlers_allows_five_named_layers(self) -> None: + for layer in ("core", "infrastructure", "shared", "validation", "errors"): + self.assertEqual( + _find(f"from mcp_server.{layer}.x import y\n", "handlers"), [] + ) + self.assertEqual( + len(_find("from mcp_server.server.x import y\n", "handlers")), 1 + ) + + def test_server_forbids_core_and_infrastructure(self) -> None: + self.assertEqual(len(_find("from mcp_server.core.x import y\n", "server")), 1) + self.assertEqual( + len(_find("from mcp_server.infrastructure.x import y\n", "server")), 1 + ) + + def test_server_allows_handlers(self) -> None: + self.assertEqual(_find("from mcp_server.handlers.x import y\n", "server"), []) + + def test_hooks_allows_infrastructure_core_shared(self) -> None: + for layer in ("infrastructure", "core", "shared"): + self.assertEqual( + _find(f"from mcp_server.{layer}.x import y\n", "hooks"), [] + ) + self.assertEqual(len(_find("from mcp_server.server.x import y\n", "hooks")), 1) + + +class AstEdgeCaseTests(unittest.TestCase): + def test_type_checking_import_is_exempt(self) -> None: + source = ( + "from typing import TYPE_CHECKING\n" + "if TYPE_CHECKING:\n" + " from mcp_server.infrastructure.pg_store import X\n" + ) + self.assertEqual(_find(source, "core"), []) + + def test_relative_import_is_exempt(self) -> None: + self.assertEqual(_find("from . import sibling\n", "core"), []) + + +class LayerOfTests(unittest.TestCase): + def test_layer_of_resolves_from_repo_relative_path(self) -> None: + self.assertEqual( + imports_mod.layer_of("mcp_server/core/wiki_classifier.py"), "core" + ) + + def test_layer_of_none_outside_mcp_server(self) -> None: + self.assertIsNone(imports_mod.layer_of("scripts/check_craftsmanship.py")) + + def test_checked_layers_covers_all_eight(self) -> None: + self.assertEqual( + imports_mod.CHECKED_LAYERS, + frozenset( + { + "shared", + "core", + "infrastructure", + "validation", + "errors", + "handlers", + "server", + "hooks", + } + ), + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests_py/scripts/test_craftsmanship_layer_table.py b/tests_py/scripts/test_craftsmanship_layer_table.py new file mode 100644 index 00000000..15489d9a --- /dev/null +++ b/tests_py/scripts/test_craftsmanship_layer_table.py @@ -0,0 +1,137 @@ +"""Tests for scripts/craftsmanship_layer_table.py — the § Dependency Rules +table parser (DRY fix: this is now the single source of truth, never a +second hardcoded copy in craftsmanship_imports.py). +""" + +from __future__ import annotations + +import unittest + +from tests_py.scripts._craftsmanship_support import craftsmanship_layer_table as lt + +_TABLE = """# Module Inventory + +## Dependency Rules + +| Layer | May Import | Must NOT Import | +|---|---|---| +| **shared/** | Python stdlib only | core, infrastructure, handlers, server | +| **core/** | shared/ only | infrastructure, handlers, server, os/pathlib | +| **infrastructure/** | shared/, Python stdlib | core, handlers, server | +| **validation/** | shared/, errors/ | core, infrastructure, handlers | +| **errors/** | nothing | everything | +| **handlers/** | core, infrastructure, shared, validation, errors | server | +| **server/** | handlers, errors | core, infrastructure (except via handlers) | +| **hooks/** | infrastructure, core, shared | server | + +## Next section +not a table row +""" + + +class ParseLayerRulesTests(unittest.TestCase): + def test_parses_all_eight_layers(self) -> None: + rules = lt.parse_layer_rules(_TABLE) + self.assertEqual( + set(rules), + { + "shared", + "core", + "infrastructure", + "validation", + "errors", + "handlers", + "server", + "hooks", + }, + ) + + def test_shared_is_pure_stdlib_only(self) -> None: + rule = lt.parse_layer_rules(_TABLE)["shared"] + self.assertTrue(rule.is_pure) + self.assertTrue(rule.stdlib_allowed) + self.assertEqual(rule.allowed_layers, frozenset()) + + def test_core_allows_stdlib_except_os_pathlib(self) -> None: + rule = lt.parse_layer_rules(_TABLE)["core"] + self.assertTrue(rule.is_pure) + self.assertTrue(rule.stdlib_allowed) + self.assertEqual(rule.stdlib_denied, frozenset({"os", "pathlib"})) + self.assertEqual(rule.allowed_layers, frozenset({"shared"})) + + def test_infrastructure_is_a_boundary_layer(self) -> None: + rule = lt.parse_layer_rules(_TABLE)["infrastructure"] + self.assertFalse(rule.is_pure) + self.assertTrue(rule.stdlib_allowed) + self.assertEqual(rule.allowed_layers, frozenset({"shared"})) + + def test_errors_allows_nothing(self) -> None: + rule = lt.parse_layer_rules(_TABLE)["errors"] + self.assertTrue(rule.is_pure) + self.assertFalse(rule.stdlib_allowed) + self.assertEqual(rule.allowed_layers, frozenset()) + + def test_server_strips_parenthetical_note(self) -> None: + # "core, infrastructure (except via handlers)" in Must NOT Import + # must not leak "(except via handlers)" into a layer name — it is + # not consulted by this parser (allowed_layers comes from May + # Import only), but must not crash or corrupt stdlib_denied either. + rule = lt.parse_layer_rules(_TABLE)["server"] + self.assertEqual(rule.stdlib_denied, frozenset()) + self.assertEqual(rule.allowed_layers, frozenset({"handlers", "errors"})) + + def test_missing_header_raises(self) -> None: + with self.assertRaises(ValueError): + lt.parse_layer_rules("no table here") + + def test_header_with_no_rows_raises(self) -> None: + broken = "| Layer | May Import | Must NOT Import |\n|---|---|---|\nnot a row\n" + with self.assertRaises(ValueError): + lt.parse_layer_rules(broken) + + def test_malformed_row_in_the_middle_raises_not_truncates(self) -> None: + # Reproduces the review-round finding exactly: a broken row after + # validation/ used to silently drop errors/, handlers/, server/, + # and hooks/ (the `if match is None: break` treated it as "table + # ended") — four of eight layers, zero signal. It must now raise. + broken = _TABLE.replace( + "| **errors/** | nothing | everything |", + "| this row is not shaped like a table row at all |", + ) + with self.assertRaises(ValueError) as ctx: + lt.parse_layer_rules(broken) + self.assertIn("does not match", str(ctx.exception)) + + def test_malformed_row_error_names_the_offending_line(self) -> None: + broken = _TABLE.replace( + "| **errors/** | nothing | everything |", + "| this row is not shaped like a table row at all |", + ) + with self.assertRaises(ValueError) as ctx: + lt.parse_layer_rules(broken) + self.assertIn( + "this row is not shaped like a table row at all", str(ctx.exception) + ) + + def test_duplicate_layer_name_raises_via_row_count_mismatch(self) -> None: + # Two "core/" rows: the dict silently collapses to one entry + # (7 rules from 8 row lines) — caught by the row-count invariant, + # independent of the row-shape check above. + marker = "| **core/** | shared/ only |" + core_row = next(line for line in _TABLE.splitlines() if line.startswith(marker)) + duplicated = _TABLE.replace(core_row, f"{core_row}\n{core_row}") + with self.assertRaises(ValueError) as ctx: + lt.parse_layer_rules(duplicated) + self.assertIn("duplicate layer name", str(ctx.exception)) + + def test_the_real_repository_table_parses(self) -> None: + # Live integration check: the actual docs/module-inventory.md must + # parse without error — a malformed table must fail LOUDLY (an + # exception at import time), never silently under-enforce. + rules = lt.load_layer_rules() + self.assertIn("core", rules) + self.assertIn("shared", rules) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests_py/scripts/test_craftsmanship_rules.py b/tests_py/scripts/test_craftsmanship_rules.py new file mode 100644 index 00000000..a325582b --- /dev/null +++ b/tests_py/scripts/test_craftsmanship_rules.py @@ -0,0 +1,92 @@ +"""Tests for scripts/craftsmanship_rules.py — rules 1-2 (file/method size) +plus the ``scan_source`` aggregator. + +Boundary cases pinned explicitly (task acceptance criteria): a file at +exactly the 300-line cap passes, one line over fails; a decorated, a +nested, and an ``async def`` method are all measured correctly; an +auto-generated file is exempt from the file-size rule. +""" + +from __future__ import annotations + +import unittest + +from tests_py.scripts._craftsmanship_support import rules + + +def _lines(n: int) -> str: + """A syntactically valid module body of exactly n lines.""" + return "\n".join(f"x{i} = {i}" for i in range(n)) + + +class FileSizeTests(unittest.TestCase): + def test_exactly_at_cap_passes(self) -> None: + source = _lines(rules.FILE_LINE_LIMIT) + self.assertEqual(rules.check_file_size("f.py", source), []) + + def test_one_over_cap_fails(self) -> None: + source = _lines(rules.FILE_LINE_LIMIT + 1) + violations = rules.check_file_size("f.py", source) + self.assertEqual(len(violations), 1) + self.assertEqual(violations[0].kind, "file-size") + + def test_auto_generated_file_is_exempt(self) -> None: + source = "# auto-generated, do not edit\n" + _lines(rules.FILE_LINE_LIMIT + 50) + self.assertEqual(rules.check_file_size("f.py", source), []) + + def test_auto_generated_marker_case_insensitive(self) -> None: + source = "# Auto-Generated\n" + _lines(rules.FILE_LINE_LIMIT + 1) + self.assertEqual(rules.check_file_size("f.py", source), []) + + +class MethodSizeTests(unittest.TestCase): + def _violations(self, source: str) -> list: + import ast + + tree = ast.parse(source) + return rules.check_method_size("f.py", tree) + + def test_short_function_passes(self) -> None: + source = "def f():\n" + "\n".join(" pass" for _ in range(5)) + self.assertEqual(self._violations(source), []) + + def test_long_function_fails(self) -> None: + body = "\n".join(" x = 1" for _ in range(rules.METHOD_LINE_LIMIT + 5)) + source = f"def f():\n{body}" + violations = self._violations(source) + self.assertEqual(len(violations), 1) + self.assertEqual(violations[0].detail, "f") + + def test_decorated_method_measured_by_body_not_decorator(self) -> None: + body = "\n".join(" x = 1" for _ in range(10)) + source = f"class C:\n @staticmethod\n def m():\n{body}" + self.assertEqual(self._violations(source), []) + + def test_nested_function_gets_dotted_qualified_name(self) -> None: + # `inner`'s oversized body is lexically part of `outer` too, so + # both are legitimately over the cap — this pins the qualified + # name, not the count. + body = "\n".join(" y = 1" for _ in range(rules.METHOD_LINE_LIMIT + 3)) + source = f"def outer():\n def inner():\n{body}\n return inner" + violations = self._violations(source) + names = sorted(v.detail for v in violations) + self.assertIn("outer.inner", names) + + def test_async_method_in_class_gets_qualified_name(self) -> None: + body = "\n".join(" x = 1" for _ in range(rules.METHOD_LINE_LIMIT + 3)) + source = f"class C:\n async def m(self):\n{body}" + violations = self._violations(source) + self.assertEqual(len(violations), 1) + self.assertEqual(violations[0].detail, "C.m") + + +class ScanSourceTests(unittest.TestCase): + def test_syntax_error_returns_empty_not_raises(self) -> None: + self.assertEqual(rules.scan_source("f.py", "def f(:\n"), []) + + def test_clean_source_has_no_violations(self) -> None: + self.assertEqual(rules.scan_source("f.py", "x = 1\n"), []) + + +if __name__ == "__main__": + unittest.main()