graphite-code 0.5.3__tar.gz → 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {graphite_code-0.5.3 → graphite_code-1.0.1}/ARCHITECTURE.md +6 -5
- {graphite_code-0.5.3 → graphite_code-1.0.1}/CHANGELOG.md +269 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/CONTRIBUTING.md +20 -6
- graphite_code-0.5.3/README.md → graphite_code-1.0.1/PKG-INFO +800 -701
- graphite_code-0.5.3/PKG-INFO → graphite_code-1.0.1/README.md +72 -61
- {graphite_code-0.5.3 → graphite_code-1.0.1}/RELEASING.md +448 -379
- {graphite_code-0.5.3 → graphite_code-1.0.1}/pyproject.toml +262 -218
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/__init__.py +41 -41
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/_cleanup_worker.py +31 -14
- graphite_code-1.0.1/src/graphite/_win32_ctypes.py +36 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/agent_settings.py +226 -226
- graphite_code-1.0.1/src/graphite/analyze.py +234 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/answer_contract.py +2 -2
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/cache.py +93 -3
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/cli.py +373 -62
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/config.py +209 -209
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/daemon.py +9 -2
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/daemon_health.py +39 -9
- graphite_code-1.0.1/src/graphite/daemon_launch.py +75 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/dependency_install.py +15 -12
- graphite_code-1.0.1/src/graphite/detach.py +37 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/doctor.py +687 -678
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/doctor_probes.py +2147 -2134
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/export/json.py +1 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/extract/ast.py +4 -2
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/git.py +6 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/graph.py +1 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/hook_entry.py +68 -68
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/hookshim.py +86 -86
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/init.py +2 -2
- graphite_code-1.0.1/src/graphite/launchd_agent.py +139 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/mcp_server.py +463 -450
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/probe_process.py +953 -879
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/probe_workspace.py +31 -26
- graphite_code-1.0.1/src/graphite/py.typed +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/query.py +1 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/replacement_audit.py +42 -37
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/resolve.py +663 -660
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/approval.py +362 -362
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/claude_executor.py +7 -2
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/contracts.py +2 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/edit_apply.py +5 -2
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/lifecycle.py +2 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/lifecycle_service.py +6 -2
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/lifecycle_storage.py +992 -991
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/openrouter_probe.py +5 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/policy.py +3 -3
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/probe_runner.py +572 -543
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/process_runner.py +17 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/profiles.py +1 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/route_pool.py +8 -5
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/schema_validation.py +2 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/service.py +28 -12
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/storage.py +4025 -4019
- graphite_code-1.0.1/src/graphite/systemd_unit.py +129 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/ts_bridge.py +5 -2
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/ts_resolver.mjs +4 -4
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/typescript_activation.py +18 -6
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/validation.py +1 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/windows_job.py +27 -9
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/windows_task.py +27 -37
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_agent_settings.py +276 -276
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_answer_contract.py +487 -487
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_arrow_and_constructor_binding.py +172 -172
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_bootstrap.py +304 -304
- graphite_code-1.0.1/tests/test_cache_entry_pruning.py +245 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cache_file_set.py +110 -110
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_class_field_arrow_binding.py +130 -130
- graphite_code-1.0.1/tests/test_cli_reference.py +182 -0
- graphite_code-1.0.1/tests/test_compare_dist.py +75 -0
- graphite_code-1.0.1/tests/test_configuration_reference.py +100 -0
- graphite_code-1.0.1/tests/test_cycle_search.py +128 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_health.py +1040 -1040
- graphite_code-1.0.1/tests/test_daemon_launch.py +42 -0
- graphite_code-1.0.1/tests/test_daemon_service_cli.py +102 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_destructured_binding.py +183 -183
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_dispatch_evidence.py +277 -277
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_doctor.py +81 -15
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_documentation.py +45 -1
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_engine_identity.py +262 -262
- graphite_code-1.0.1/tests/test_exit_codes_reference.py +36 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_external_calls.py +548 -548
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_init.py +446 -446
- graphite_code-1.0.1/tests/test_launchd_agent.py +122 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_lifecycle_operator.py +253 -253
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_mcp.py +3 -3
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_method_dispatch_scope.py +294 -294
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_monorepo.py +199 -199
- graphite_code-1.0.1/tests/test_mutation_tests.py +365 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_probe_workspace.py +443 -443
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_lifecycle_storage.py +576 -542
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_reliability.py +153 -153
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_replacement_audit.py +6 -6
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_cli_recovery.py +283 -283
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_contracts.py +246 -246
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_profiles.py +685 -685
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_service.py +469 -469
- graphite_code-1.0.1/tests/test_synthetic_repo.py +96 -0
- graphite_code-1.0.1/tests/test_systemd_unit.py +156 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_typescript_resolver.py +274 -274
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_verify_artifact.py +13 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_verify_published_release.py +68 -3
- graphite_code-0.5.3/src/graphite/analyze.py +0 -146
- graphite_code-0.5.3/src/graphite/detach.py +0 -33
- {graphite_code-0.5.3 → graphite_code-1.0.1}/.gitignore +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/LICENSE +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/__main__.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/activation.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/agent_hooks.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/bootstrap.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/buildlock.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/channel.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/cluster.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/context.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/debt.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/engine_identity.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/export/__init__.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/export/html.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/export/md.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/extract/__init__.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/freshness.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/graph_io.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/health.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/hookinstall.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/incident_ledger.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/ingest.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/io.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/listing.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/llm.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/llm_probe.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/mcp.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/natural_query.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/overlays.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/process_contracts.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/provider_observer.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/query_plan.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/review.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/__init__.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/classifier.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/claude_probe.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/cli_identity.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/codex_executor.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/codex_probe.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/context_builder.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/diff_policy.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/effort.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/lifecycle_operator.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/ollama_executor.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/ollama_probe.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/openrouter_executor.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/prompt.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/registry.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/route_pool_execution.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/settings.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/shadow.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/telemetry.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/worktree.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/zai_edit.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/zai_executor.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/zai_probe.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/savings.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/usage_ledger.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/watch.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/windows_startup.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/conftest.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/data/sqloracle/blindspots.py.txt +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/data/sqloracle/round69.py.txt +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/fake_clis/fake_cli.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/fixtures/provider_lifecycle_schema_v1.sql +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/fixtures/routing_schema_v2_ca77600.sql +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/fixtures/routing_schema_v3_94eb333.sql +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/fixtures/routing_schema_v4_lifecycle_migration.sql +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_activation.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_activation_backstop.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_agent_hook_activation.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_agent_hooks.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_atomic_write.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_build_detach.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_buildlock.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cache_engine_identity.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cache_pruning.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_call_graph.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_cli.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_core.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_lock.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_mcp.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_register.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_report.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_status.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cli_channel.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cli_version.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_context.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cwd_relative_config.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_activation.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_build_lock.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_child_lock.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_engine_staleness.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_health_activation.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_debt.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_determinism.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_doctor_agent_hooks.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_doctor_hooks.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_doctor_managed_docs.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_edit_apply.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_git_security.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_go_rust.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_graph_io.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_graph_provider_isolation.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_hardening.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_health.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_hook_entry.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_hook_template.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_hookinstall.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_hookshim.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_html_security.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_incident_ledger.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_init_activation_doctrine.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_init_hooks.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_init_validation_exit.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_listing.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_listing_surfaces.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_llm.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_natural_query.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_node_identity.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_overlays.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_path_leak_fixture.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_probe_diagnostics.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_claude_probe.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_codex_probe.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_lifecycle.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_lifecycle_service.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_observer.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_ollama_probe.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_openrouter_probe.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_probe_runner.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_public_surface.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_published_schemas.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_python_resolver.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_query_plan.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_resolve.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_review.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_route_pool.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_approval.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_benchmark.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_classifier.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_claude_executor.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_cli.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_cli_contracts.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_codex_executor.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_context.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_diff_policy.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_executor.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_openrouter_executor.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_policy.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_process_runner.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_registry.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_schema_validation.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_security.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_shadow.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_storage.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_telemetry.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_worktree.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_zai_executor.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_zai_probe.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_savings.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_search.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_smoke.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_sqloracle.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_typescript_activation.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_usage_ledger.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_watch.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_windows_startup.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_windows_task.py +0 -0
- {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_zai_edit.py +0 -0
|
@@ -24,10 +24,10 @@ The implementation stages are:
|
|
|
24
24
|
1. **Collection — `ingest.py`, `config.py`, `git.py`.** Configuration establishes file-count and file-size limits and the cache and output locations. Ingestion normalizes project-relative paths, resolves candidates beneath the repository root, skips unsafe or ineligible files, and sorts accepted entries. Git-backed enumeration uses `GitRunner` with a trusted executable outside the repository, an argument vector, no shell, a filtered Git environment, a timeout, and a stdout cap. The filesystem fallback is also capped by configured file limits, but it does not provide every process control because no child process is involved.
|
|
25
25
|
2. **Extraction — `extract/ast.py`, `cache.py`, `ts_bridge.py`, `ts_resolver.mjs`.** Per-language Tree-sitter extractors handle JavaScript/TypeScript, Python, Go, and Rust, while unsupported or unavailable parsers degrade to a file-level record. Extraction emits symbols, imports, calls, and containment edges in deterministic order. The content-addressed cache avoids repeated AST work. TypeScript/JavaScript may additionally use the Node compiler bridge for compiler-backed module resolution; bridge failure falls back to heuristic resolution rather than making Node mandatory.
|
|
26
26
|
3. **Resolution — `resolve.py`.** `SourceIndex` builds the set of normalized, project-relative source identities, reads TypeScript aliases and workspace package entry points, prefers compiler-backed TypeScript results when available, and otherwise applies bounded heuristics. Global merge logic also resolves supported call identities and deterministically removes duplicates.
|
|
27
|
-
4. **Graph and analysis — `graph.py`, `cluster.py`, `analyze.py`, `query.py`.** `graph.py` constructs a NetworkX `DiGraph` after sorting nodes and edges and normalizing IDs. Analysis filters to project nodes where appropriate and returns bounded, ordered results. Community detection constructs a deterministic undirected view and uses the configured seed (42 by default). JSON conversion preserves the graph's deterministic insertion order.
|
|
27
|
+
4. **Graph and analysis — `graph.py`, `cluster.py`, `analyze.py`, `query.py`.** `graph.py` constructs a NetworkX `DiGraph` after sorting nodes and edges and normalizing IDs. Analysis filters to project nodes where appropriate and returns bounded, ordered results; the cycle report is a bounded search (per strongly connected component, one length level at a time up to length 8, under a 10 000-cycle budget) and `analysis.cycle_search` records whether the enumeration completed exactly or was truncated, so a repository with dense import cycles cannot make a build exhaust memory. Community detection constructs a deterministic undirected view and uses the configured seed (42 by default). JSON conversion preserves the graph's deterministic insertion order.
|
|
28
28
|
5. **Validation and review — `validation.py`, `context.py`, `review.py`.** Bundle validation checks structural types, node and edge identity, referential integrity, metadata counts, and unsafe `source_file` paths. Review validates a supplied bundle before deriving graph impact evidence; its Git discovery also normalizes status records and rejects malformed or unsafe paths. By contrast, `cmd_query`, `cmd_impact`, `cmd_context`, and `GraphiteMCPServer._load` currently read JSON and call `graph_from_json` without `validate_graph_bundle` or a bounded-read helper. Those direct consumers therefore rely on callers to supply an already validated, reasonably sized artifact.
|
|
29
29
|
6. **Export — `export/json.py`, `export/md.py`, `export/html.py`, `io.py`.** Exporters consume only canonical graph, cluster, analysis, and manifest data. JSON serialization provides the data encoding; HTML separately JSON-encodes script data, escapes `<`, `>`, and `&`, HTML-escapes the title, and uses text DOM APIs for runtime labels. Text and JSON outputs use temporary files, `fsync`, and `os.replace` for atomic replacement of each file.
|
|
30
|
-
7. **Operations — `watch.py`, `daemon.py`, `daemon_health.py`, `bootstrap.py`, `init.py`, `windows_task.py`, `windows_startup.py`.** These modules watch for changes, maintain multi-project daemon status, evaluate health, generate integration instructions, and manage platform
|
|
30
|
+
7. **Operations — `watch.py`, `daemon.py`, `daemon_health.py`, `bootstrap.py`, `init.py`, `daemon_launch.py`, `windows_task.py`, `windows_startup.py`, `systemd_unit.py`, `launchd_agent.py`.** These modules watch for changes, maintain multi-project daemon status, evaluate health, generate integration instructions, and manage platform supervision. `daemon_launch.py` holds the one daemon argument vector (`python -P -m graphite daemon <base> …`); the Windows scheduled task, the Windows Startup-folder launcher, the systemd user unit and the launchd agent are each rendered from it, so every supervisor starts the same bounded, zero-LLM daemon under `-P`. Watch and daemon canonicalize inherited configuration; daemon child builds use fixed `--llm none` argv and a provider-scrubbed environment.
|
|
31
31
|
8. **Provider lifecycle — `routing/lifecycle.py`, `routing/lifecycle_storage.py`, `routing/lifecycle_service.py`, and `routing/lifecycle_operator.py`.** Runtime observations, compatibility decisions, lifecycle authority, and invalidations live in an isolated database. Operator reads use an existing-database, query-only connection and bounded public records. Policy and verification preparation create non-activating content-hashed candidates; neither surface invokes a provider.
|
|
32
32
|
9. **Optional overlays — `overlays.py` and `llm.py`.** Provider adapters may consume an already-built, fresh, validated graph only through `graphite overlay build`. The overlay manifest binds the canonical bundle fingerprint, exact lifecycle/model/routing identities, limits, creation time, outcome, and schema. Identity-derived contained paths, restrictive permissions, content-addressed payloads, and manifest-last atomic replacement isolate the output below `graph-out/overlays/`. Overlay staleness is independent of canonical freshness and cannot grant graph or routing authority.
|
|
33
33
|
|
|
@@ -42,9 +42,10 @@ The implementation stages are:
|
|
|
42
42
|
| Validation and evidence | `validation.py`, `context.py`, `review.py` | Validate bundles and derive review/context evidence | Graph structures; review may use the Git adapter |
|
|
43
43
|
| Export | `export/json.py`, `export/md.py`, `export/html.py`, `io.py` | Encode and atomically replace individual artifacts | Validated graph, analysis, clusters, manifest |
|
|
44
44
|
| Optional model overlays | `overlays.py`, `llm.py` | Validate identity and canonical freshness; produce fingerprint-bound, non-authoritative annotations outside canonical artifacts | Existing canonical graph, explicit provider configuration, network only when separately authorized |
|
|
45
|
-
| Operations and integration | `watch.py`, `daemon.py`, `daemon_health.py`, `bootstrap.py`, `init.py`, `windows_task.py`, `windows_startup.py` | Freshness, daemon health, generated instructions, and OS
|
|
45
|
+
| Operations and integration | `watch.py`, `daemon.py`, `daemon_health.py`, `bootstrap.py`, `init.py`, `daemon_launch.py`, `windows_task.py`, `windows_startup.py`, `systemd_unit.py`, `launchd_agent.py` | Freshness, daemon health, generated instructions, and OS supervision on Windows, Linux and macOS | Public core operations and explicit platform/process boundaries |
|
|
46
|
+
| Platform shims | `_win32_ctypes.py`, `_cleanup_worker.py` | Typed call-time views of platform-only stdlib surfaces (Win32 ctypes, POSIX-only `os` members) so the portable core type-checks on every OS without `sys.platform` guards | Standard library only |
|
|
46
47
|
|
|
47
|
-
The intended dependency direction is observable in current imports but is not enforced by a dedicated architecture linter. CLI and MCP adapt inputs; portable core modules do not import those UI entry points. Export consumes canonical graph and analysis data; extraction does not depend on exporters. Canonical graph construction, validation, and read operations do not depend on model providers. Windows
|
|
48
|
+
The intended dependency direction is observable in current imports but is not enforced by a dedicated architecture linter. CLI and MCP adapt inputs; portable core modules do not import those UI entry points. Export consumes canonical graph and analysis data; extraction does not depend on exporters. Canonical graph construction, validation, and read operations do not depend on model providers. Platform supervision — the Windows scheduled task and Startup launcher, the systemd user unit, the launchd agent — remains outside the portable core. External execution crosses explicit subprocess boundaries; Git uses the hardened `GitRunner`, while the TypeScript and platform-supervisor adapters have their own, not necessarily identical, controls. Contributors must preserve these directions and add tests if a boundary becomes mechanically important.
|
|
48
49
|
|
|
49
50
|
## Trust boundaries
|
|
50
51
|
|
|
@@ -86,7 +87,7 @@ Treat paths, bytes, symlinks, encodings, file counts, and file sizes as hostile.
|
|
|
86
87
|
|
|
87
88
|
**Process boundary.**
|
|
88
89
|
|
|
89
|
-
Git, Node/TypeScript, and
|
|
90
|
+
Git, Node/TypeScript, and the platform supervisors (`schtasks`, `systemctl --user`, `launchctl`) execute outside Python. `GitRunner` is the strongest current adapter: fixed executable discovery outside the repository, argv execution with `shell=False`, disabled stdin/stderr, filtered `GIT_*` environment, timeout, stdout cap, and typed sanitized failures. The TypeScript bridge uses argv, `subprocess.run`, captured output, a configured timeout, and a 500-character diagnostic cap, but it currently inherits the environment and does not impose an explicit output-byte cap. The supervisor adapters are platform-specific, take an injected runner in tests, and must be reviewed on their own controls. Do not generalize Git's isolation guarantees to every subprocess.
|
|
90
91
|
|
|
91
92
|
**Artifact and browser boundary.**
|
|
92
93
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
Notable changes to graphite. Format follows [Keep a Changelog]; versioning is
|
|
4
|
-
|
|
4
|
+
semantic. From 1.0.0 the surfaces listed in `docs/compatibility.md` are
|
|
5
|
+
stable: a breaking change to any of them is a major release.
|
|
5
6
|
|
|
6
7
|
**A version number here is a coarse release label, not a fix marker.** Several
|
|
7
8
|
behavioural fixes have shipped with no version change at all — the `-P`
|
|
@@ -12,6 +13,273 @@ machine-checkable identity; the version is for humans.
|
|
|
12
13
|
|
|
13
14
|
[Keep a Changelog]: https://keepachangelog.com/en/1.1.0/
|
|
14
15
|
|
|
16
|
+
## [1.0.1] — 2026-09-05
|
|
17
|
+
|
|
18
|
+
A patch release. Measured against the deployed 1.0.0 engine on this repository,
|
|
19
|
+
the tree produces the same graph: 7260 of 7260 node ids survive, 20700 of 20700
|
|
20
|
+
edge triples unchanged, every relation count equal, `analysis.cycles` identical.
|
|
21
|
+
Only the engine fingerprint moves, because graphite's own bytes did.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
**Vendor-neutral wording throughout the replacement audit and docs.** The
|
|
26
|
+
README opening, the `audit-replacement` help and reference text,
|
|
27
|
+
`skill/SKILL.md` and the audit's own messages no longer name the legacy
|
|
28
|
+
graph tool, and the README's opening comparative against it — an unmeasured
|
|
29
|
+
claim dating to the initial commit (`fd58583`) — is removed rather than
|
|
30
|
+
defended. In the audit's report, which is not a `schema_version`-stable
|
|
31
|
+
surface, the `graphify` block is renamed `legacy` and the warning codes
|
|
32
|
+
`graphify_paths_exist`, `graphify_text_references` and
|
|
33
|
+
`graphify_gitignore_entries` become `legacy_paths_exist`,
|
|
34
|
+
`legacy_text_references` and `legacy_gitignore_entries`. Detection is
|
|
35
|
+
unchanged: the on-disk path patterns and the text-scan needle still match
|
|
36
|
+
the legacy tool's literal artifact names — the one place the name must
|
|
37
|
+
remain for the audit to find anything.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
**The extraction cache grew without bound inside a partition (#66).** #23
|
|
42
|
+
reclaims whole partitions a build can never read again and left the one it
|
|
43
|
+
uses alone. Entries are keyed on the file's content hash, so every edit that
|
|
44
|
+
triggers a rebuild wrote a new entry and orphaned the previous one, and
|
|
45
|
+
nothing ever removed an orphan: a consumer checkout rebuilt by the daemon on
|
|
46
|
+
every change reached 15,406 entries / 419 MB in a single partition, 11,592 of
|
|
47
|
+
them from four days, against 1,780 / 62 MB for graphite's own tree on the
|
|
48
|
+
same engine. Same rule as #23, one level down -- reachability, not age or
|
|
49
|
+
size: a `Cache` now records every key it read (and found) or wrote, and after
|
|
50
|
+
a build has extracted the tree and built its graph, `prune_unreachable_entries`
|
|
51
|
+
deletes every other entry in the partition, reporting
|
|
52
|
+
`[graphite] reclaimed N unreachable cache entries (X MB)` when it removed any.
|
|
53
|
+
Older content checked out later re-extracts once (performance, never
|
|
54
|
+
correctness). Safe without the build lock: `read` already treats a vanished
|
|
55
|
+
file as a miss, so a concurrent reader re-extracts rather than fails. A build
|
|
56
|
+
that touched nothing prunes nothing, only the exact `<2 hex>/<64 hex>.json`
|
|
57
|
+
shape the cache writes is eligible, and a per-entry failure (a Windows handle
|
|
58
|
+
race) is swallowed.
|
|
59
|
+
|
|
60
|
+
**The deferred stdin close never fired on Linux or macOS.** `run_bounded_process`
|
|
61
|
+
reads the child's pipes with a fixed-size read, and its two transports hand
|
|
62
|
+
that reader different objects: on Windows a raw `io.FileIO`, whose `read(n)`
|
|
63
|
+
returns whatever the child has written, and on POSIX a default `Popen` pipe,
|
|
64
|
+
a `BufferedReader` whose `read(n)` keeps reading until it has n bytes or EOF.
|
|
65
|
+
Off Windows the reader therefore saw nothing until the child exited, the
|
|
66
|
+
`stdin_close_when` predicate never saw its marker, and every deferred close
|
|
67
|
+
was the budget fallback -- so the #29 fix was inert there and the doctor's MCP
|
|
68
|
+
deep probe spent its whole budget on every POSIX run. The reader now uses
|
|
69
|
+
`read1` on buffered pipes; the Windows path is unchanged. The tests that
|
|
70
|
+
pinned the deferral could not fail (one matched a marker that text-mode
|
|
71
|
+
stdout writes as CRLF on Windows, the other's lower bound was satisfied by
|
|
72
|
+
the child's own sleep) and now kill a never-defer mutant; the interval test
|
|
73
|
+
that blocked the pre-push gate measures from the close instead of from
|
|
74
|
+
launch, against the child's own life rather than a 0.25s constant.
|
|
75
|
+
|
|
76
|
+
**The post-publication verifier's provenance arm asked an endpoint that
|
|
77
|
+
never carries provenance.** `scripts/verify_published_release.py` read
|
|
78
|
+
`urls[].provenance` from PyPI's legacy `/pypi/<project>/<version>/json`
|
|
79
|
+
API, which has no such key, so the arm reported "no provenance" for 1.0.0
|
|
80
|
+
while the Integrity API was already serving both GitHub attestation bundles
|
|
81
|
+
(`jared0565/graphite`, `publish.yml`). The arm now reads the PEP 691 Simple
|
|
82
|
+
JSON index (`Accept: application/vnd.pypi.simple.v1+json`), where PEP 740
|
|
83
|
+
publishes a `provenance` URL per file; a test pins the endpoint so a
|
|
84
|
+
regression to the legacy API fails. Re-run against 1.0.0: 6 of 6 arms.
|
|
85
|
+
|
|
86
|
+
**Three findings from aramid's `llm-review` tier on the CI workflow and the
|
|
87
|
+
benchmark, all confirmed and closed:**
|
|
88
|
+
|
|
89
|
+
- The `coverage_floor` dispatch input could *lower* the floor for one run,
|
|
90
|
+
so a dispatched run could report green against a weaker gate than the
|
|
91
|
+
committed one. The enforcing step now refuses any requested value that is
|
|
92
|
+
not an integer at or above the committed `COVERAGE_FLOOR`; a dispatch can
|
|
93
|
+
only raise it (the negative control is unchanged). Proven with a fake
|
|
94
|
+
coverage across six arms: empty, 84 and 83 pass; 80, `abc` and -5 fail
|
|
95
|
+
before coverage is read.
|
|
96
|
+
- `test_core_probe_cleanup_timeout_uses_single_global_slot_and_recovers`
|
|
97
|
+
raced its 0.5 s probe budget against runner load (#65): more than 0.4 s of
|
|
98
|
+
scheduling before the workspace lease is acquired yields a pure `timeout`,
|
|
99
|
+
after it a `cleanup_timeout` carrying `masked_*` keys — two CI failures in
|
|
100
|
+
three runs on one day, both shapes reproduced with a paced fake clock. The
|
|
101
|
+
test now freezes its injected `_clock` like the sibling probe tests (the
|
|
102
|
+
subject is the classification of a hung cleanup, not phase pacing; the
|
|
103
|
+
cleanup join still runs on real time), and the frozen-clock test still
|
|
104
|
+
kills both guarding mutants: a hung cleanup reported as `None`, and a
|
|
105
|
+
probe slot that never releases.
|
|
106
|
+
- The `security` job's self-check asserted only that gitleaks and semgrep
|
|
107
|
+
ran, while the step's name claimed typecheck and a dependency audit. The
|
|
108
|
+
labels were measured from the job's own `prepush.json` artifact
|
|
109
|
+
(`gitleaks, mypy, python.exe, semgrep, shadow`): the assertion now covers
|
|
110
|
+
every configured scanner, and the step name, `SECURITY.md` and
|
|
111
|
+
`CONTRIBUTING.md` no longer claim a dependency audit — no gate run on
|
|
112
|
+
either machine has ever emitted one, although `aramid doctor` lists
|
|
113
|
+
pip-audit as present (reported to aramid). Measuring this exposed two
|
|
114
|
+
worse things: under aramid 0.6.0 the CI typecheck slot had **never run**
|
|
115
|
+
in `--all` mode (no `mypy` in `tools_ran`, nothing degraded) and the old
|
|
116
|
+
assertion called that clean; under 0.6.1 it runs over every `.py`, and
|
|
117
|
+
the job passed with exit 0 while its own report carried 786 block-tier
|
|
118
|
+
findings that the same report fails with on the maintainer's machine
|
|
119
|
+
(exit 1). Discriminated on the first run under the 0.7.0 pin (33286250634):
|
|
120
|
+
still 786 block-tier findings and exit 0 with CI's empty ledger, so
|
|
121
|
+
aramid's exit depends on ledger presence — `.aramid/` is gitignored, so
|
|
122
|
+
a fresh checkout cannot be gated by exit status at all. aramid confirmed
|
|
123
|
+
the mechanism from its code (channel round 151): its **fresh-ledger
|
|
124
|
+
rule** — the first pre-push run on a ledger with no baseline writes one
|
|
125
|
+
and downgrades findings the ratchet alone escalated to exit 0, so every
|
|
126
|
+
CI checkout is "the first run" — and from 0.7.1 the report says so
|
|
127
|
+
(`fresh_ledger_baseline`, `grandfathered`; both gate steps print them).
|
|
128
|
+
Both CI gate steps now
|
|
129
|
+
read the verdict from the report itself: any block-tier finding from a
|
|
130
|
+
non-mypy scanner, or from mypy inside the project's type-gate scope
|
|
131
|
+
(`src/graphite`), fails the step regardless of the exit status; mypy
|
|
132
|
+
findings on the deliberately untyped `tests/`, `scripts/` and
|
|
133
|
+
`benchmarks/` trees are counted and printed.
|
|
134
|
+
- `benchmarks/build_benchmark.py` prepended the repository root to
|
|
135
|
+
`sys.path` to import its sibling — the shape the `-P` launch contract
|
|
136
|
+
forbids. It loads `synthetic_repo.py` by location now, CI runs it under
|
|
137
|
+
`-P`, and two tests pin the shape (no `sys.path` access in the module; the
|
|
138
|
+
script starts under `-P`).
|
|
139
|
+
|
|
140
|
+
### Changed
|
|
141
|
+
|
|
142
|
+
- CI pins `aramid==0.7.2`, the tool this machine runs (channel round 157;
|
|
143
|
+
0.7.0 and 0.7.1 were pinned briefly at rounds 148 and 152). 0.7.2 fixes
|
|
144
|
+
the tests-slot skip-streak false alarm this repository reported — the
|
|
145
|
+
registry key `tests` rode into `run_started.expected` beside the slot's
|
|
146
|
+
label, so `status` counted every pre-push run as a skipped suite (channel
|
|
147
|
+
rounds 155–157) — and stamps `run_id`/`recorded` in the check report, so
|
|
148
|
+
a `--no-record` measurement is distinguishable from a recorded one.
|
|
149
|
+
0.7.1's typecheck slot honours
|
|
150
|
+
`[tool.mypy] files`, so the 683 mypy rows that a whole-tree measurement
|
|
151
|
+
under 0.7.0 had written into the maintainer's ledger for `tests/` (675)
|
|
152
|
+
and `scripts/` (8) are retired with `ledger resolve --out-of-scope`, each
|
|
153
|
+
carrying the reason.
|
|
154
|
+
The two `mypy:syntax` ledger rows that 0.6.1 left unresolvable were NOT
|
|
155
|
+
retired with `ledger resolve --out-of-scope` as first written here: it
|
|
156
|
+
refused them (`is not open (status=fixed)`), because aramid's gate had
|
|
157
|
+
already marked them `fixed` on a push that ran mypy over other files
|
|
158
|
+
without opening `ci.yml` — a defect in aramid 0.6.1's examined-set
|
|
159
|
+
stamping, fixed on aramid's `main` (`e65f296`) for 0.7.1. The rows stay
|
|
160
|
+
`fixed`, the log being append-only; channel round 151 is their record.
|
|
161
|
+
- mypy's configuration is back under `[tool.mypy]` in `pyproject.toml` and
|
|
162
|
+
`setup.cfg` is gone. It had moved there only because aramid 0.6.0's
|
|
163
|
+
typecheck runner armed on `[tool.mypy]` and then fed every changed file to
|
|
164
|
+
mypy; aramid 0.6.1 hands mypy only `.py`/`.pyi` (channel round 144), so
|
|
165
|
+
the CI pin is now `aramid==0.6.1` and the two `mypy:syntax` suppression
|
|
166
|
+
entries that bridged the defect are deleted rather than kept — a
|
|
167
|
+
workaround that outlives its root cause misdirects the next reader.
|
|
168
|
+
|
|
169
|
+
### Documentation
|
|
170
|
+
|
|
171
|
+
- **The generated CLI reference now renders nested subcommands.**
|
|
172
|
+
`scripts/gen_cli_reference.py` walked only the top level of the parser,
|
|
173
|
+
so the twenty-four commands that live one level down — under `route`,
|
|
174
|
+
`lifecycle`, `channel`, `incidents` and `overlay` — and every option they
|
|
175
|
+
own (`route reconcile --attempt-id`, for one) were absent from
|
|
176
|
+
`docs/reference/cli.md`, and the lockstep test could not see them drift.
|
|
177
|
+
Each nested command now has its own section under its group, and
|
|
178
|
+
`tests/test_cli_reference.py` requires every nested command and every
|
|
179
|
+
option it declares to appear there.
|
|
180
|
+
- **Every CLI argument now declares help text.** Rendering the nested
|
|
181
|
+
commands exposed sixty-two arguments with none — every nested `--json`,
|
|
182
|
+
the `lifecycle policy prepare` and `verification prepare` inputs,
|
|
183
|
+
`route accept|reject|cleanup|review --task-id`, `record-outcome`'s
|
|
184
|
+
flags, `reconcile --attempt-id`, `policy --promote/--rollback`, the
|
|
185
|
+
`incidents ack|resolve` positionals and options — so both `--help` and
|
|
186
|
+
the reference were silent about them. Each now says what it takes and
|
|
187
|
+
what it binds to; a test walks the whole parser and fails on any
|
|
188
|
+
argument without help, so none can be added back.
|
|
189
|
+
- New [user guide](docs/user-guide.md) and [knowledge base](docs/knowledge-base.md).
|
|
190
|
+
- README: installation is `pip install graphite-code` (it had still said
|
|
191
|
+
`git clone` + `pip install -e .`, wrong since the 0.3.0 wheel), the `-P`
|
|
192
|
+
launch rule is explained where the command is introduced, the MCP
|
|
193
|
+
examples launch with `-P`, and a documentation index points at every
|
|
194
|
+
support page. CONTRIBUTING puts the dev environment outside the clone
|
|
195
|
+
and lists what CI enforces. ARCHITECTURE covers the platform
|
|
196
|
+
supervisors and the bounded cycle search. The Claude Code skill is
|
|
197
|
+
machine-neutral and versioned 1.0.0.
|
|
198
|
+
|
|
199
|
+
## [1.0.0] — 2026-08-29
|
|
200
|
+
|
|
201
|
+
The production-grade release. Nothing here is a claim CI cannot turn red:
|
|
202
|
+
each item names the gate or test that enforces it. The declaration criteria
|
|
203
|
+
and their evidence are in `docs/superpowers/specs/2026-08-29-production-grade-design.md`.
|
|
204
|
+
|
|
205
|
+
### Fixed
|
|
206
|
+
|
|
207
|
+
**The cycle report could not finish on a repository with dense import
|
|
208
|
+
cycles** (#64). `analyze()` enumerated every simple cycle of the project
|
|
209
|
+
graph before sorting and keeping twenty; on Django 5.2 (2 930 source files,
|
|
210
|
+
`tests/` and `django/` importing each other) that is exponential — 13 GB and
|
|
211
|
+
unfinished after thirty minutes, while each half alone reported cycles in
|
|
212
|
+
seconds. The search is now bounded: cycles are enumerated inside strongly
|
|
213
|
+
connected components one length level at a time up to length 8, under a
|
|
214
|
+
10 000-cycle budget, and stop once a completed level holds the twenty
|
|
215
|
+
shortest — exact whenever it stops that way, and deterministic across
|
|
216
|
+
processes even when the budget bites. `analysis.cycle_search` records
|
|
217
|
+
`length_bound`, `budget`, `examined`, `budget_exhausted`,
|
|
218
|
+
`complete_through_length`, `cyclic_components` and `largest_component`, so a
|
|
219
|
+
truncated answer says so. Django 5.2 now builds in 82 s (45 620 nodes,
|
|
220
|
+
109 373 edges, 53 MB). Found by the real-repository benchmark; the synthetic
|
|
221
|
+
corpus alone would never have shown it.
|
|
222
|
+
|
|
223
|
+
**A lifecycle event claiming "no prior observation" over an UNAVAILABLE row
|
|
224
|
+
raised `AttributeError`** instead of `lifecycle_transition_stale`
|
|
225
|
+
(`routing/lifecycle_storage.py`); found while making the type gate clean.
|
|
226
|
+
|
|
227
|
+
### Added
|
|
228
|
+
|
|
229
|
+
- **Every supported cell gates merges.** The CI matrix is windows/ubuntu/macos
|
|
230
|
+
× CPython 3.11–3.14, all twelve cells blocking; the advisory `portability`
|
|
231
|
+
job is gone. Ubuntu and macOS 3.13 ran for the first time and pass.
|
|
232
|
+
- **A type gate.** `python -m mypy` over `src/graphite` runs in the lint
|
|
233
|
+
job; 142 findings were fixed with zero `type: ignore` comments added, and
|
|
234
|
+
the gate is clean under `--platform` win32, linux and darwin.
|
|
235
|
+
`graphite/py.typed` ships (the artifact verifier refuses a wheel without
|
|
236
|
+
it) and the distribution carries `Typing :: Typed`.
|
|
237
|
+
- **A coverage floor.** Branch coverage is collected on the 3.12 leg of each
|
|
238
|
+
OS, combined (83.04 % on the first measurement), and enforced at
|
|
239
|
+
`COVERAGE_FLOOR = 83`; a `workflow_dispatch` input overrides the floor for
|
|
240
|
+
one run as the negative control (floor 84 turns the job red).
|
|
241
|
+
- **The security gates run in CI, unmodified.** A `security` job creates the
|
|
242
|
+
dev venv `aramid.toml` names and runs `aramid check --gate pre-commit|pre-push
|
|
243
|
+
--all --strict` against the committed configuration, asserting the tools
|
|
244
|
+
that ran (gitleaks, ruff, shadow; gitleaks, mypy, the suite, semgrep,
|
|
245
|
+
shadow) and refusing any degraded tool. A planted repo-root `graphite.py`
|
|
246
|
+
fails it at the pre-commit gate — and, under `python -m pytest`, every test
|
|
247
|
+
leg with it, which is the hazard the `-P` launch shape exists for.
|
|
248
|
+
- `SECURITY.md` (GitHub private vulnerability reporting), `CODEOWNERS`, issue
|
|
249
|
+
templates, dependabot for actions and pip, `authors` metadata.
|
|
250
|
+
- `daemon-install-linux` (systemd user unit), `daemon-install-macos`
|
|
251
|
+
(launchd agent), `daemon-uninstall-*` and `daemon-service-status`; all
|
|
252
|
+
three supervisors build their launch argv from one place, so `-P` is
|
|
253
|
+
present by construction. `daemon-health` reports the platform's supervisor.
|
|
254
|
+
- `benchmarks/`: a deterministic synthetic corpus and a build benchmark; a CI
|
|
255
|
+
`benchmark` job records a 3 000-file build as an artifact and fails only
|
|
256
|
+
past a catastrophic budget. `capabilities` declares
|
|
257
|
+
`supported_repo_files: 7000` with its basis; `docs/benchmarks.md` holds the
|
|
258
|
+
measurements, including Django 5.2.
|
|
259
|
+
- Reference documentation that cannot drift: `docs/reference/cli.md`
|
|
260
|
+
(generated from the parser; a test fails when stale),
|
|
261
|
+
`docs/reference/configuration.md` (every `Config` field and every
|
|
262
|
+
`GRAPHITE_*` variable the code reads, both directions tested),
|
|
263
|
+
`docs/reference/exit-codes.md` (every subcommand has a row),
|
|
264
|
+
`docs/compatibility.md` (what 1.x promises, the deprecation policy, the
|
|
265
|
+
support matrix).
|
|
266
|
+
- Releases are built in CI from the approved tag with pinned tools
|
|
267
|
+
(`release-build-constraints.txt`), refused unless both digests equal the
|
|
268
|
+
reviewed ones, attached to the GitHub Release, and published with PEP 740
|
|
269
|
+
attestations that are true because this workflow built the bytes.
|
|
270
|
+
|
|
271
|
+
### Changed
|
|
272
|
+
|
|
273
|
+
- `Development Status :: 5 - Production/Stable`. From 1.0.0 the surfaces in
|
|
274
|
+
`docs/compatibility.md` are stable; a breaking change to any of them is a
|
|
275
|
+
major release.
|
|
276
|
+
- The CI security job pins aramid 0.6.0 (the version the maintainer's
|
|
277
|
+
machine runs). mypy's configuration lives in `setup.cfg` for now: aramid
|
|
278
|
+
0.6.0's typecheck runner arms on `[tool.mypy]` and hands non-Python files
|
|
279
|
+
to mypy (accepted in channel round 140, fixed in aramid 0.6.1); both
|
|
280
|
+
files say when to move it back. Two reasoned entries in
|
|
281
|
+
`.aramid-suppressions.toml` from the same defect retire themselves.
|
|
282
|
+
|
|
15
283
|
## [0.5.3] — 2026-08-28
|
|
16
284
|
|
|
17
285
|
### Fixed
|
|
@@ -4,17 +4,19 @@ Contributions should be focused, reviewable changes that preserve Graphite's det
|
|
|
4
4
|
|
|
5
5
|
## Development setup
|
|
6
6
|
|
|
7
|
-
Graphite requires Python 3.11
|
|
7
|
+
Graphite requires Python 3.11–3.14, Git, and an isolated virtual environment **outside the clone**. Clone and enter the repository using paths appropriate for your system, then create the environment beside it rather than inside it:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
git clone https://github.com/jared0565/graphite.git
|
|
11
11
|
cd graphite
|
|
12
|
-
python -m venv
|
|
12
|
+
python -m venv ../.venvs/graphite-dev
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
Two reasons for the location. An environment inside the checkout is read by the MCP trusted-source probe as an import shadow of the repository it was asked to trust, so `doctor --deep` reports it. And the release checks (`aramid.toml`, the CI `security` job) run the test suite through exactly that interpreter, `../.venvs/graphite-dev`, so that the suite tests the editable clone rather than whatever `graphite-code` wheel the machine interpreter has installed — a green run against the wrong distribution proves nothing.
|
|
16
|
+
|
|
15
17
|
If you contribute from a fork, clone the fork's verified URL instead. Keep URLs and other environment-specific values outside shell metacharacters.
|
|
16
18
|
|
|
17
|
-
Activate
|
|
19
|
+
Activate the environment using the command for your shell. Before installing already-declared extras, inspect `pyproject.toml` and confirm the exact extra and dependency names. Then install the declared development extra:
|
|
18
20
|
|
|
19
21
|
```bash
|
|
20
22
|
python -m pip install -e ".[dev]"
|
|
@@ -87,20 +89,32 @@ Run a focused test while iterating. Replace the placeholder path with the test m
|
|
|
87
89
|
python -m pytest tests/test_relevant_area.py -q
|
|
88
90
|
```
|
|
89
91
|
|
|
90
|
-
Before requesting review, run all required checks:
|
|
92
|
+
Before requesting review, run all required checks through the dev environment's interpreter:
|
|
91
93
|
|
|
92
94
|
```bash
|
|
93
95
|
python -m ruff check .
|
|
94
|
-
python -m
|
|
96
|
+
python -m mypy # [tool.mypy] in pyproject.toml; 0 errors is the gate
|
|
97
|
+
python -m pytest -q -rs # -rs: a skip is visible, not a silent pass
|
|
95
98
|
python -m graphite --help
|
|
96
99
|
```
|
|
97
100
|
|
|
98
|
-
Focused tests shorten the feedback loop; they do not replace the full Ruff, pytest, and CLI smoke checks. Add behavior-level tests for new or changed contracts and ensure failures are meaningful rather than dependent on local machine state.
|
|
101
|
+
Focused tests shorten the feedback loop; they do not replace the full Ruff, mypy, pytest, and CLI smoke checks. Add behavior-level tests for new or changed contracts and ensure failures are meaningful rather than dependent on local machine state.
|
|
102
|
+
|
|
103
|
+
What CI enforces on every push and pull request (`.github/workflows/ci.yml`): a `lint` job (ruff, mypy, the generated CLI reference in sync); a `test` matrix of Windows, Linux and macOS × Python 3.11–3.14 in which **every cell gates**; a `security` job that runs the same aramid pre-commit and pre-push gates as the local hooks (gitleaks, semgrep, ruff security rules, the repo-root shadow check, mypy, the suite) and asserts from the gate's JSON report that each scanner actually ran; the `coverage` floor; a `benchmark` job against the synthetic corpus; and an `artifact` job that builds the distribution twice and requires byte-identical results. The local git hooks are installed by `aramid` from the committed `aramid.toml`; do not push with `--no-verify`.
|
|
99
104
|
|
|
100
105
|
Doctor changes require focused tests for stable doctor JSON, deterministic ordering, redaction, end-to-end deadlines, process cleanup, missing tools, and optional semantics. Cover both successful and adversarial outcomes. Cross-platform behavior must have Windows and POSIX coverage; use narrowly scoped platform skips only when an operating-system primitive truly has no counterpart, and test the portable contract on every platform.
|
|
101
106
|
|
|
102
107
|
No live provider calls in automated tests are permitted. Exercise network and subprocess behavior through a fake worker, fake provider, and fake process boundary with controlled time, output, failures, and descendants. Tests must remain offline, deterministic, and safe to run without credentials.
|
|
103
108
|
|
|
109
|
+
### Coverage floor
|
|
110
|
+
|
|
111
|
+
CI collects branch coverage on the 3.12 leg of every OS, combines the three
|
|
112
|
+
data files in the `coverage` job, and fails under `COVERAGE_FLOOR` in
|
|
113
|
+
`.github/workflows/ci.yml`. The floor is the integer part of the measured
|
|
114
|
+
total: raise it when a release measures higher, and never lower it without a
|
|
115
|
+
CHANGELOG entry saying what was removed and why. No single leg's figure is
|
|
116
|
+
the measurement -- platform-only code is covered on one OS each.
|
|
117
|
+
|
|
104
118
|
## Security expectations
|
|
105
119
|
|
|
106
120
|
Treat repository files, file names, symlinks, Git metadata, generated graph data, and model output as untrusted input. Pass subprocess arguments as an argument vector and never interpolate untrusted values into a shell command. Preserve path containment, bounded I/O, explicit timeouts, atomic writes, output encoding, and safe error redaction at every relevant boundary.
|