theurian 0.1.0.dev7__tar.gz → 0.1.0.dev9__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.
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/.gitignore +11 -11
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/CHANGELOG.md +355 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/PKG-INFO +2 -1
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/pyproject.toml +6 -1
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/__init__.py +1 -1
- theurian-0.1.0.dev9/src/theurian/application/proposal_service.py +1949 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/cli/commands.py +30 -9
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/cli/main.py +5 -1
- theurian-0.1.0.dev9/src/theurian/cli/output.py +61 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/cli/propose_commands.py +63 -22
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/errors.py +220 -8
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/migration.py +6 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/proposal.py +36 -6
- theurian-0.1.0.dev9/src/theurian/infrastructure/filesystem/migration_loader.py +1859 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/security/paths.py +8 -4
- theurian-0.1.0.dev9/tests/command_population.py +542 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_cli_commands.py +217 -0
- theurian-0.1.0.dev9/tests/integration/test_proposal_service.py +2752 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_propose_cli.py +428 -0
- theurian-0.1.0.dev9/tests/unit/test_command_population.py +763 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_documented_commands.py +51 -51
- theurian-0.1.0.dev9/tests/unit/test_dogfood_corpus_governance.py +1464 -0
- theurian-0.1.0.dev9/tests/unit/test_migration_loader_errors.py +3936 -0
- theurian-0.1.0.dev9/tests/unit/test_path_security.py +383 -0
- theurian-0.1.0.dev7/src/theurian/application/proposal_service.py +0 -948
- theurian-0.1.0.dev7/src/theurian/infrastructure/filesystem/migration_loader.py +0 -818
- theurian-0.1.0.dev7/tests/command_population.py +0 -210
- theurian-0.1.0.dev7/tests/integration/test_proposal_service.py +0 -1091
- theurian-0.1.0.dev7/tests/unit/test_migration_loader_errors.py +0 -1757
- theurian-0.1.0.dev7/tests/unit/test_path_security.py +0 -198
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/LICENSE +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/README.md +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/hatch_build.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/forest_builder.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/index_builder.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/ingestion_service.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/migration_alias_guards.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/migration_body_guards.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/migration_engine.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/project_service.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/retrieval_service.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/setup_context.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/setup_service.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/setup_steps.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/setup_withholding.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/visibility.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/application/withdrawal_purge.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/cli/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/cli/auth_commands.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/cli/context.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/cli/index_commands.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/cli/setup_commands.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/daemon/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/daemon/instance.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/daemon/runner.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/daemon/server.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/chunking.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/compatibility.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/context.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/enums.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/extras.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/identifiers.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ingestion.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/knowledge.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/authorization.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/canonical_store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/daemon_manager.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/determinism.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/embedding.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/index_store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/mcp_client_config.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/object_store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/reranking.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/review_provider.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/secret_store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/source_parser.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/specification_provider.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/summarization.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ports/vector_store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/project.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/ranking.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/raptor.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/retrieval.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/review.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/setup.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/specification.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/state.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/domain/values.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/indexing/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/claude/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/claude/mcp_config.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/determinism.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/embedding/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/embedding/hashing.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/filesystem/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/filesystem/parsers/markdown.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/filesystem/parsers/openapi.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/filesystem/parsers/registry.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/filesystem/parsers/structured.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/git/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/github/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/raptor/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/raptor/extractive.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/secrets/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/secrets/file_store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/services/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/services/launchagent.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/services/runner.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/services/systemd_user.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/sqlite/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/sqlite/connection.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/sqlite/index_forest.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/sqlite/index_purge.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/sqlite/index_query.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/sqlite/index_scan.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/sqlite/index_schema.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/sqlite/index_store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/sqlite/schema.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/sqlite/store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/infrastructure/vector/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/ingestion/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/mcp/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/mcp/results.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/mcp/search.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/mcp/tools.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/migrations/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/normalization/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/normalization/projection.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/observability/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/retrieval/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/review/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/security/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/security/env_file.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/security/tokens.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/security/yaml_loading.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/specification/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/src/theurian/traceability/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/command_extraction.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/conftest.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/fakes/__init__.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/fakes/clock.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/fakes/ids.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/fakes/pages.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/fakes/setup.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/fakes/store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_absence_proof.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_alias_item_id_collision.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_auth_rotate.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_bare_install.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_canonical_store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_canonical_store_corruption.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_claude_mcp_config.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_cli_help_without_rich.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_daemon.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_forest_builder.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_forest_builder_scale.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_forest_node_scope.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_forest_purge_equality.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_forest_purge_recompute.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_forest_retrieval.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_forest_store_retrieval.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_gc_during_a_search.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_index_fallback.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_index_gc_cli.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_index_purge.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_index_purge_differential.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_index_purge_nodes.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_index_scan_fold.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_index_schema_v4.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_index_store.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_ingestion.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_init_gitignore_block.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_mcp_tools.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_project_registry.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_retrieval_service.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_revision_id_reuse.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_scan_exhaustion.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_service_adapters.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_session_start_hook.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_setup_cli.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_setup_env_file.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_setup_journal.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_setup_partial_failure.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_setup_probe_assertions.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_setup_report_withholding.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_setup_service.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_short_query_retrieval.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_state_provenance.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_unreadable_registry_surface.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_wire_contract.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_withdrawal_purge.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/integration/test_writer_contract.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_artifact_integrity_claim.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_candidate_cut.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_candidate_depth.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_chunking.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_cli_help_rendering.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_command_extraction.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_compatibility.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_config_key_call_sites.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_daemon_extra.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_domain_invariants.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_env_file_merge.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_examples.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_extractive_summarizer.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_forest_derivation.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_forest_fanout.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_gate_call_sites.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_hashing_embedding.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_ingest_manifest_path.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_install_claims.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_layering.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_migration_alias_guards.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_migration_engine.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_migration_loader_identity.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_network_call_sites.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_parsers.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_plugin_boundary.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_ports.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_project_and_traceability.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_project_registry_errors.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_projection.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_proposal.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_ranking.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_raptor_scope.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_ref_recording.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_resolve_context_call_sites.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_result_gate_session.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_result_payload.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_retrieval_depth.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_schemas.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_scope_isolation.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_secret_detector.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_setup_changed_paths.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_setup_claims.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_setup_domain.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_state_hash.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_test_fixtures.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_tokens.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/tests/unit/test_yaml_loading.py +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/README.md +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/cli/version.schema.json +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/config/project-config.schema.json +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/knowledge/retrieval-result.schema.json +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/mcp/knowledge-search-response.schema.json +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/mcp/knowledge-status-response.schema.json +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/mcp/project-list-response.schema.json +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/mcp/retrieval-metadata.schema.json +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/mcp/tool-context.schema.json +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/migrations/migration.schema.json +0 -0
- {theurian-0.1.0.dev7 → theurian-0.1.0.dev9}/theurian/schemas/protocol/compatibility.schema.json +0 -0
|
@@ -19,17 +19,6 @@ dist/
|
|
|
19
19
|
coverage.xml
|
|
20
20
|
htmlcov/
|
|
21
21
|
|
|
22
|
-
# --- Theurian derived artifacts -------------------------------------------
|
|
23
|
-
# Canonical state is rebuilt from Git-tracked migrations and content.
|
|
24
|
-
# Never commit any of these. See docs/adr/0004-sqlite-is-a-derived-artifact.md
|
|
25
|
-
.theurian/state/
|
|
26
|
-
.theurian/cache/
|
|
27
|
-
.theurian/runtime/
|
|
28
|
-
.theurian/generated/
|
|
29
|
-
*.sqlite
|
|
30
|
-
*.sqlite-wal
|
|
31
|
-
*.sqlite-shm
|
|
32
|
-
|
|
33
22
|
# --- Documentation site (derived) ---
|
|
34
23
|
# `mkdocs build` writes site/. The Pages workflow copies the logo into
|
|
35
24
|
# docs/assets/ before building, so anyone replicating that build locally
|
|
@@ -58,3 +47,14 @@ htmlcov/
|
|
|
58
47
|
.env.*
|
|
59
48
|
!.env.example
|
|
60
49
|
*.token
|
|
50
|
+
|
|
51
|
+
# >>> theurian >>>
|
|
52
|
+
# Derived artifacts. Rebuilt from Git-tracked migrations (ADR-0004).
|
|
53
|
+
.theurian/state/
|
|
54
|
+
.theurian/cache/
|
|
55
|
+
.theurian/runtime/
|
|
56
|
+
.theurian/generated/
|
|
57
|
+
*.sqlite
|
|
58
|
+
*.sqlite-wal
|
|
59
|
+
*.sqlite-shm
|
|
60
|
+
# <<< theurian <<<
|
|
@@ -12,6 +12,361 @@ Pre-1.0, a MINOR bump may change the protocol. Post-1.0, only a MAJOR may.
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
## [0.1.0.dev9] - 2026-08-22
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **`propose accept` translates every accept-path filesystem or path fault into
|
|
20
|
+
an `{error, remedy}` document instead of escaping a raw traceback**
|
|
21
|
+
([#227](https://github.com/theurian/theurian/issues/227)). A proposal directory
|
|
22
|
+
this process cannot list, stat or read; a migration file it cannot open; a
|
|
23
|
+
`contentFile` whose path `resolve()` refuses (a NUL byte or an unpaired
|
|
24
|
+
surrogate); an unwritable `.theurian/migrations/` — each used to leave `accept`
|
|
25
|
+
as a bare `OSError`/`ValueError`, so `--json` published nothing where it
|
|
26
|
+
promises a document. (An unreadable `evidence.json` is not in this set: it was
|
|
27
|
+
already translated to an indeterminate document in 0.1.0.dev8,
|
|
28
|
+
[#253](https://github.com/theurian/theurian/issues/253).) Each is now a `ProposalError` naming the offending path
|
|
29
|
+
relative to the project root — never the absolute path, which is the machine's
|
|
30
|
+
home directory (SEC-7) — whose remedy sends the reader to
|
|
31
|
+
`.theurian/migrations/` before re-drafting: a refused read establishes nothing
|
|
32
|
+
about whether the migration landed, and re-drafting an accepted proposal mints a
|
|
33
|
+
duplicate migration (#89). A move that landed but whose trailing cleanup of the
|
|
34
|
+
proposal's own copied files could not run (a read-only `0o555` proposal
|
|
35
|
+
directory) now degrades to success with a `remedy` naming the leftover, rather
|
|
36
|
+
than reporting a non-landing that would send the caller to re-draft.
|
|
37
|
+
- **`propose accept`'s replacement guard reads the loaded migration set and keys
|
|
38
|
+
on the body's filesystem identity, closing a disagreement with the loader**
|
|
39
|
+
([#234](https://github.com/theurian/theurian/issues/234)). The guard that
|
|
40
|
+
refuses a body replacement which would leave the migration set unable to
|
|
41
|
+
validate used to enumerate `.theurian/migrations/*.yaml` itself and skip the
|
|
42
|
+
symlinked entries the loader follows, so a pin held by a relocated (symlinked)
|
|
43
|
+
migration was invisible to it: the replacement was allowed, and the set then
|
|
44
|
+
stopped loading at exit 4 with no undo. It now reads the same `MigrationSet` the
|
|
45
|
+
loader produced, and compares on the body's `(st_dev, st_ino)` rather than a
|
|
46
|
+
path string.
|
|
47
|
+
|
|
48
|
+
**Behaviour change:** `accept` now refuses four inputs it previously let
|
|
49
|
+
through, each of which then broke the loaded set at exit 4:
|
|
50
|
+
|
|
51
|
+
- a *different* revision replacing an **unpinned** landed body — the old guard
|
|
52
|
+
filtered on the pin and waved this through, yet the set then holds two
|
|
53
|
+
revisions on one physical file (`refuse_duplicate_content_files`, exit 4);
|
|
54
|
+
- a byte-identical replacement of a **pinned** landed body under a *different*
|
|
55
|
+
revision id — the old guard treated "byte-identical changes nothing" as true
|
|
56
|
+
of the *set*, but it is the same two-revisions-on-one-file break;
|
|
57
|
+
- a **hardlink** to a landed body — the identity key `(st_dev, st_ino)` reaches
|
|
58
|
+
the same inode by every alias, where the old path-string compare saw none;
|
|
59
|
+
- a **case variant** of a landed body's path (`RETRY-POLICY.md` against a landed
|
|
60
|
+
`retry-policy.md`) — likewise one inode by every spelling. A Unicode
|
|
61
|
+
normalisation variant reaches it the same way, though no test exercises that
|
|
62
|
+
face.
|
|
63
|
+
|
|
64
|
+
The one in-place re-declare `accept` still allows — this proposal's own revision
|
|
65
|
+
re-declared byte-for-byte **on the same item**, the in-place status change of
|
|
66
|
+
ADR-0024 decision 5 — is an explicit conjunction of equal item id, revision id
|
|
67
|
+
and bytes. A byte-*different* re-declare of a *pinned* landed revision was
|
|
68
|
+
already refused before this branch and is unchanged. There are no users yet, but
|
|
69
|
+
the refusals change what `accept` exits on, so they are named here.
|
|
70
|
+
- **`propose accept` refuses a cross-item re-declare of a landed revision id**
|
|
71
|
+
(round-one review finding). The replacement-guard skip that admits the one
|
|
72
|
+
legitimate in-place re-declare (ADR-0024 decision 5) fired on two conjuncts —
|
|
73
|
+
equal revision id and equal bytes — so a byte-identical body re-declared under a
|
|
74
|
+
*different* item's id passed it. `accept` returned 0, and `migrate validate`
|
|
75
|
+
passed too (it does not check cross-item revision ownership); then `migrate
|
|
76
|
+
apply` refused the whole set at exit 4 ("a revision id belongs to one item",
|
|
77
|
+
INV-1/SEC-13) after the pull request had merged and the proposal was already
|
|
78
|
+
consumed, with no undo. The skip now carries the item id as a third conjunct
|
|
79
|
+
(item id, revision id, bytes), moving that refusal to the accept door. No
|
|
80
|
+
rejected-item content is disclosed either way — `store.py` refuses a cross-item
|
|
81
|
+
revision-id reuse before it reads content (SEC-13) — so this spares the operator
|
|
82
|
+
a consumed proposal; it does not add a disclosure control.
|
|
83
|
+
- **`propose accept` refuses a migration whose id the loaded set already holds
|
|
84
|
+
under another filename** (round-one review finding). The "already in place"
|
|
85
|
+
refusal keyed only on the destination filename (`<id>-<slug>.yaml`), but the
|
|
86
|
+
loader keys migrations by their *inner* `id`. A hand-authored proposal named
|
|
87
|
+
`<landed-id>-other-slug.yaml` carrying `id: <landed-id>` collided on the inner
|
|
88
|
+
id while its filename was free: the name check waved it through, the
|
|
89
|
+
filename/id agreement check was satisfied (the prefix equals the id), and
|
|
90
|
+
`accept` landed a *duplicate* migration id, on which `migrate
|
|
91
|
+
validate`/`status`/`apply` then all exit 4. The refusal now also answers the id
|
|
92
|
+
against the same loaded migration set the loader, `migrate validate` and
|
|
93
|
+
`migrate apply` read (#234/#253/#254). This is the third and last accept-path
|
|
94
|
+
procedure moved off a filesystem enumeration and onto the loaded set; no method
|
|
95
|
+
on the accept path enumerates `.theurian/migrations/` any more.
|
|
96
|
+
- **The accept-path read-failure remedy is chosen by `errno`, not a blanket
|
|
97
|
+
`chmod`** (round-one review finding, extending
|
|
98
|
+
[#227](https://github.com/theurian/theurian/issues/227)). The remedy prescribed
|
|
99
|
+
"Make _path_ readable — chmod u+rX on it" for every accept-path read failure —
|
|
100
|
+
the same over-claim [#233](https://github.com/theurian/theurian/issues/233)
|
|
101
|
+
corrected for `PathEscapeError`, reopened here. A `contentFile` naming a
|
|
102
|
+
directory raises `EISDIR`, which no `chmod` cures: the fault is the authored
|
|
103
|
+
input, so the remedy now names the `contentFile` to correct and says nothing
|
|
104
|
+
about permissions. A `stat`/`open` refused for a *child* is the parent directory
|
|
105
|
+
lacking its search bit, so `chmod u+rX` on the child cures the wrong file — the
|
|
106
|
+
remedy now points `chmod u+x` at the unsearchable directory instead.
|
|
107
|
+
`EACCES`/`EPERM` still earn a `chmod`; a `None` errno is treated as
|
|
108
|
+
non-permission, since a `chmod` prescribed for an unknown cause is the very
|
|
109
|
+
over-claim this avoids. Every branch still points at `.theurian/migrations/`
|
|
110
|
+
before any re-draft (#89).
|
|
111
|
+
- **Scope note, not a behaviour change: `propose accept` does not self-validate
|
|
112
|
+
the proposal it is accepting**
|
|
113
|
+
([#307](https://github.com/theurian/theurian/issues/307)). The replacement
|
|
114
|
+
guard's docstring claimed it holds the invariant that `accept` "never leaves the
|
|
115
|
+
set unable to validate." That is false as a global invariant: `accept` does not
|
|
116
|
+
schema-validate the incoming migration and does not check it against itself, so
|
|
117
|
+
a self-contained breakage in a single proposal — two operations naming one
|
|
118
|
+
`contentFile`, a self-inconsistent pin, an empty `contentFile` — lands and is
|
|
119
|
+
caught by `migrate validate` in CI, which is the check by design (ADR-0013 §4).
|
|
120
|
+
The docstring is narrowed to what the guard actually holds: it refuses a
|
|
121
|
+
replacement that would break a pin *already landed* in the approved set, the one
|
|
122
|
+
fault it can judge from the landed set alone. No behaviour changed here;
|
|
123
|
+
hardening `accept` itself against a self-contained breakage is deferred to #307.
|
|
124
|
+
- **A migration-schema rejection echoed `jsonschema`'s raw `{instance!r}` and
|
|
125
|
+
had dropped the schema-side fact that names the defect**
|
|
126
|
+
([#289](https://github.com/theurian/theurian/issues/289)). When a migration
|
|
127
|
+
failed validation, the message interpolated the whole failing instance
|
|
128
|
+
verbatim — unbounded, and with control characters unescaped — while no longer
|
|
129
|
+
saying *what the schema expected*: the `const` a value must equal, the
|
|
130
|
+
`pattern` it must match, or the unexpected keys an `additionalProperties:
|
|
131
|
+
false` rejected. The rejection is now built at Theurian's own seam
|
|
132
|
+
(`migration_loader.py`'s `_schema_rejection`), keeping that schema-side fact
|
|
133
|
+
and bounding every author-written fragment it echoes to `MAX_ECHOED_VALUE`
|
|
134
|
+
(1,000 characters) through a `reprlib`-based renderer that escapes control
|
|
135
|
+
characters. This restores the diagnostic parity the pre-change wording had
|
|
136
|
+
while removing the unbounded echo — a 100 KB value no longer renders a
|
|
137
|
+
100,198-character refusal into a terminal.
|
|
138
|
+
|
|
139
|
+
- **An oversized scalar nested in a rejected operation reported its
|
|
140
|
+
post-truncation length, not its true length**
|
|
141
|
+
([#289](https://github.com/theurian/theurian/issues/289)). When a rejected
|
|
142
|
+
operation carried an oversized string, the echo truncated the container's
|
|
143
|
+
render first, so the "N characters in all" count reported the *container
|
|
144
|
+
repr's* length (~1,100), not the offending value's. The renderer now records
|
|
145
|
+
the longest scalar's pre-truncation length and the rejection names that true
|
|
146
|
+
length — the one number that is the diagnosis for a value refused for being
|
|
147
|
+
large.
|
|
148
|
+
|
|
149
|
+
- **A hostile migration document could exhaust CPU or memory at validate time,
|
|
150
|
+
and a giant-integer scalar escaped `--json` as a raw traceback**
|
|
151
|
+
([#291](https://github.com/theurian/theurian/issues/291)). Several shapes of a
|
|
152
|
+
parsed migration document defeated `jsonschema`'s own message building at
|
|
153
|
+
validate time. The loader now applies three bounds ahead of `validate` in its
|
|
154
|
+
own un-memoised walk — nesting depth, node count, and total rendered magnitude
|
|
155
|
+
— and translates a single giant integer by type after `validate` raises:
|
|
156
|
+
- **Nesting** is refused ahead of `validate` at `MAX_DOCUMENT_NESTING` (64).
|
|
157
|
+
Past the interpreter's C recursion budget `jsonschema` cannot build its own
|
|
158
|
+
refusal message, and the `RecursionError` that follows is indistinguishable
|
|
159
|
+
from a corrupt schema — so a deep *document* was answered "reinstall
|
|
160
|
+
theurian". A schema-valid migration nests at most 7 levels.
|
|
161
|
+
- **Branching alias expansion** is refused ahead of `validate` at
|
|
162
|
+
`MAX_DOCUMENT_NODES` (100,000). A YAML anchor aliased into a doubling chain
|
|
163
|
+
is a ~500-byte file whose expansion is 2^N nodes; `jsonschema`'s
|
|
164
|
+
`{instance!r}` re-expands it the same way, building a 46 MB message from a
|
|
165
|
+
500-byte file at alias level 22 (measured 2026-08-21, `jsonschema` 4.26.0).
|
|
166
|
+
The walk deliberately does not collapse shared references — a collapsed
|
|
167
|
+
count would wave the bomb through. Same un-memoised-walk shape as the OpenAPI
|
|
168
|
+
`$ref` ref-walk in
|
|
169
|
+
[#245](https://github.com/theurian/theurian/issues/245), in another seam.
|
|
170
|
+
- **Total rendered magnitude** is refused ahead of `validate` at
|
|
171
|
+
`MAX_DOCUMENT_RENDERED_CHARS` (1,000,000) — a new bound in the same walk that
|
|
172
|
+
the node count could not stand in for. One large scalar aliased into many
|
|
173
|
+
slots is only a handful of nodes, well under `MAX_DOCUMENT_NODES`, but
|
|
174
|
+
`{instance!r}` re-expands it to N times its rendered width. The walk charges
|
|
175
|
+
every leaf's width via a new O(1) `_rendered_width` per un-memoised reference
|
|
176
|
+
— `len` for a `str`/`bytes`, a `bit_length`-derived decimal-digit estimate
|
|
177
|
+
for an `int` (never `str(int)`, which is quadratic and raises past CPython's
|
|
178
|
+
int→str limit), and the `repr` length of a bounded `bool`/`float`/`None`.
|
|
179
|
+
This closes two aliased-scalar faces. The first is a large *string* aliased
|
|
180
|
+
into N slots: a hundreds-of-gigabytes transient that raises `MemoryError`,
|
|
181
|
+
which is neither a `ValueError` nor an `ArithmeticError` and so escaped the
|
|
182
|
+
scalar catch below as a raw traceback. The second was found by the round-two
|
|
183
|
+
review and missed by the round-one bound, which charged only `str`/`bytes`: a
|
|
184
|
+
*medium integer* — a few thousand digits, so its own `repr` does not raise —
|
|
185
|
+
aliased into N slots is O(N) nodes under `MAX_DOCUMENT_NODES` and not a
|
|
186
|
+
`ValueError`, so it defeated every round-one control while `{instance!r}`
|
|
187
|
+
re-emitted its digits once per slot, a `digits × slots`-character message.
|
|
188
|
+
Charging every leaf per reference refuses both faces before `validate`. The
|
|
189
|
+
refusal message still says "characters of string content", but the bound now
|
|
190
|
+
covers every leaf type, integers and bytes included.
|
|
191
|
+
- **A single giant integer** — one whose own `{instance!r}` render raises,
|
|
192
|
+
past CPython's int→str limit (4300 digits by default) — is one node whose
|
|
193
|
+
lone width passes the aggregate budget above, so it reaches `validate` and is
|
|
194
|
+
*translated by type* rather than refused ahead. `jsonschema` renders it past
|
|
195
|
+
that limit and raises `ValueError` (reachable today), or a float `multipleOf`
|
|
196
|
+
coerces it and raises `OverflowError` (latent — the bundled schema carries
|
|
197
|
+
only `minimum`/`maximum`). `_validate_document` catches the whole
|
|
198
|
+
`(ValueError, ArithmeticError)` class as a `MigrationError` so a future
|
|
199
|
+
numeric keyword cannot reopen the escape. This catch and the rendered budget
|
|
200
|
+
are complementary, not redundant: the budget refuses the *aliased* integer
|
|
201
|
+
whose aggregate render is large, and this catch handles the *single* integer
|
|
202
|
+
whose lone render raises. The file-load path closes the same single-value
|
|
203
|
+
face: a YAML integer literal past CPython's limit is now translated to the
|
|
204
|
+
same bounded "reduce it" wording instead of forwarding CPython's message,
|
|
205
|
+
which named `sys.set_int_max_str_digits()` — an interpreter tuning knob no
|
|
206
|
+
migration author should reach for. As defence in depth, the rejection
|
|
207
|
+
builder's `_echo` refuses an integer wider than `_MAX_ECHOED_INT_BITS`
|
|
208
|
+
(2,000 bits) as a placeholder.
|
|
209
|
+
|
|
210
|
+
## [0.1.0.dev8] - 2026-08-20
|
|
211
|
+
|
|
212
|
+
### Added
|
|
213
|
+
|
|
214
|
+
- **`evidence.json` now records the `migrationId` and `itemId` its proposal
|
|
215
|
+
drafted** ([#253](https://github.com/theurian/theurian/issues/253)). These are
|
|
216
|
+
the two fields of that file Core reads back: together they let `propose accept`
|
|
217
|
+
ask whether a migration with that id, operating on that item, is in
|
|
218
|
+
`.theurian/migrations/`. Both are a contributor's claim, not authority —
|
|
219
|
+
`evidence.json` is committed and untrusted (ADR-0013 point 7) — so `itemId` is
|
|
220
|
+
the cross-check that stops a forged `migrationId` (one pointing at another
|
|
221
|
+
proposal's landed migration) from reading as accepted. Optional on read: the 26
|
|
222
|
+
proposals committed in this repository predate both fields and are diagnosed by
|
|
223
|
+
best-effort inference, which their message says.
|
|
224
|
+
|
|
225
|
+
### Fixed
|
|
226
|
+
|
|
227
|
+
- **The installed migration schema resolved `$ref`s over the network, and its
|
|
228
|
+
resolution failures escaped as raw tracebacks**
|
|
229
|
+
([#235](https://github.com/theurian/theurian/issues/235)). The migration
|
|
230
|
+
schema was validated with `jsonschema`'s default registry, whose retrieve
|
|
231
|
+
callable fetches an external `$ref` over the network
|
|
232
|
+
(`urllib.request.urlopen`) at validate time — and reads a `file://` ref off
|
|
233
|
+
disk the same way. An SSRF-shaped read gated only on the installed schema
|
|
234
|
+
being corrupted or replaced (an operator-side precondition; `schema_root()`
|
|
235
|
+
never reads user-project paths), and covered by no existing claim:
|
|
236
|
+
`parsers/openapi.py`'s "external `$ref` targets are recorded, never fetched"
|
|
237
|
+
governs *ingested* documents, not the schema this build ships. Every
|
|
238
|
+
validator is now built with `referencing`'s `EMPTY_REGISTRY` — no retrieve
|
|
239
|
+
callable — so an external `$ref` fails closed; internal `#/$defs/…` refs
|
|
240
|
+
still resolve from the schema's own root resource, so the bundled schema
|
|
241
|
+
validates unchanged. Separately, the resulting resolution failures — an
|
|
242
|
+
unresolvable `$ref`/`$dynamicRef` raises `referencing.exceptions.Unresolvable`
|
|
243
|
+
and a self-recursive or empty `$ref` raises `RecursionError`, neither a
|
|
244
|
+
`ValidationError` — used to slip past every `except ValidationError` seam and
|
|
245
|
+
reach `resolve_context` as a raw traceback under `--json`. Both are now
|
|
246
|
+
translated to `SchemaUnreadableError`, the type every other
|
|
247
|
+
installed-schema-corruption failure already carries, at both validate seams
|
|
248
|
+
(`migrate validate` / `propose`, and any command that loads migrations from
|
|
249
|
+
disk).
|
|
250
|
+
- **A path-containment refusal told the user to go somewhere they already were,
|
|
251
|
+
and could instruct them to delete their own work**
|
|
252
|
+
([#233](https://github.com/theurian/theurian/issues/233)). With
|
|
253
|
+
`.theurian/migrations` — or one `*.yaml` file inside it — a symlink pointing
|
|
254
|
+
outside the project, `theurian migrate validate --json` printed
|
|
255
|
+
`{"error": "Path escapes the permitted root /<absolute>/<project>", "remedy":
|
|
256
|
+
"Run this inside an initialised Theurian project."}` at exit 1. The refusal
|
|
257
|
+
itself was correct and is unchanged; what it said about itself was not.
|
|
258
|
+
`PathEscapeError` set no `remedy` of its own, so the CLI's generic fallback
|
|
259
|
+
won — the "exception that does not describe itself" shape
|
|
260
|
+
[#205](https://github.com/theurian/theurian/issues/205) exists to end. It now
|
|
261
|
+
names, relative to the project root and with the `!r` quoting its siblings
|
|
262
|
+
use, *where* the problem is — and **no remedy names a file to delete.** That
|
|
263
|
+
is the substantive change: an escape happens through a symbolic link somewhere
|
|
264
|
+
on the entry's ancestor chain or its resolution chain, and which link it is
|
|
265
|
+
cannot be determined from the entry alone. Three successive attempts to
|
|
266
|
+
determine it anyway were each refuted by a deeper construction — the last by
|
|
267
|
+
`x.yaml → y.yaml → outside`, where following the remedy deleted two
|
|
268
|
+
Git-tracked files and ended at `valid: true` while the minimal cure was
|
|
269
|
+
repointing `y` alone. So the remedy now states only what `lstat` proves — that
|
|
270
|
+
the named entry is or is not itself a link — and then hands over the finite
|
|
271
|
+
checklist: this entry, the directories above it, the links it resolves
|
|
272
|
+
through. "Repoint that link, or remove that link" refers to whichever the
|
|
273
|
+
reader finds; Theurian never names it. Related: an outside-pointing
|
|
274
|
+
`.theurian` is reachable from a plain `git clone`
|
|
275
|
+
([#237](https://github.com/theurian/theurian/issues/237)). The offending path
|
|
276
|
+
is still never echoed (SEC-7), and neither is the absolute project root.
|
|
277
|
+
- **A `contentFile` refusal no longer denies the commonest cure.** The
|
|
278
|
+
path-escape remedy for a `contentFile` used to say that removing the `..` was
|
|
279
|
+
"not the cure" — wrong for a plain over-traversal typo, which involves no
|
|
280
|
+
symlink at all and is fixed by exactly that edit, and in conflict with the
|
|
281
|
+
sibling `MigrationContentUnreadableError`, which tells the author to fix the
|
|
282
|
+
path. It now offers both candidates: correct the path, or find the link
|
|
283
|
+
something it traverses goes through.
|
|
284
|
+
- **Exit code:** a `PathEscapeError` raised while resolving a project now exits
|
|
285
|
+
`EXIT_STATE_ERROR` (4) rather than 1 for the commands routed through
|
|
286
|
+
`_require_project` — measured, all nine: `migrate validate`, `migrate status`,
|
|
287
|
+
`migrate apply`, `index build`, `index status`, `index gc`, `ingest`,
|
|
288
|
+
`propose` and `propose accept` — matching every sibling refusal on the same
|
|
289
|
+
load path (`MigrationsDirectoryUnreadableError`,
|
|
290
|
+
`MigrationFileUnreadableError`, `MigrationError`). A script keying on exit 1
|
|
291
|
+
for those must be updated. `init` and `project register` call
|
|
292
|
+
`resolve_context` directly and still exit 1; `project status` reports at exit
|
|
293
|
+
0 as it always has. All three now carry the new remedy text.
|
|
294
|
+
- **A path refused only for nesting too deep no longer claims to have escaped**
|
|
295
|
+
([#233](https://github.com/theurian/theurian/issues/233)). `resolve_within_root`
|
|
296
|
+
refuses a path past `MAX_PATH_DEPTH` whether or not it ever leaves the root,
|
|
297
|
+
and reported it as `Path escapes the permitted root` — false for a path
|
|
298
|
+
sitting entirely inside. It now raises `PathDepthExceededError` with its own
|
|
299
|
+
message and a remedy about flattening the nesting. The new type is a
|
|
300
|
+
`PathEscapeError` subclass, so every existing `except` and every exit-code
|
|
301
|
+
route catches it unchanged; only what the caller is told differs.
|
|
302
|
+
- **An interrupted `theurian propose` was diagnosed as an accepted one**
|
|
303
|
+
([#253](https://github.com/theurian/theurian/issues/253)). `propose accept`
|
|
304
|
+
read the presence of `evidence.json` as "this proposal has already been
|
|
305
|
+
accepted", but `propose` writes the body, then the evidence, then the
|
|
306
|
+
migration — so a run killed before its last write left exactly that shape.
|
|
307
|
+
`accept` reported that the migration had been moved into
|
|
308
|
+
`.theurian/migrations/` and that no action was needed beyond opening a pull
|
|
309
|
+
request, while `.theurian/migrations/` held nothing and the drafted knowledge
|
|
310
|
+
existed nowhere. The remedy discarded the draft.
|
|
311
|
+
|
|
312
|
+
Acceptance is a **best-effort diagnosis over untrusted input, not a
|
|
313
|
+
tamper-proof fact**: the proposal directory is contributor-controlled, so the
|
|
314
|
+
recorded `migrationId` is a claim (cross-checked by `itemId` against the
|
|
315
|
+
migration it names), a `evidence.json` that is present but unreadable is
|
|
316
|
+
answered as *indeterminate* rather than collapsed into "no record", and every
|
|
317
|
+
fallible branch points the reader at `.theurian/migrations/` first — no branch
|
|
318
|
+
emits an unconditional "no action is needed" or "draft it again", so none can
|
|
319
|
+
tell the author to discard work that may exist or duplicate a change that
|
|
320
|
+
already landed. Whether a migration is in place under the recorded id is read
|
|
321
|
+
from the same approved `MigrationSet` `migrate validate`/`apply` read — keyed by
|
|
322
|
+
the migration's inner id, not by a filename match — so `propose accept` cannot
|
|
323
|
+
disagree with the loader about what has landed, whatever a migration file was
|
|
324
|
+
renamed to or whether it is a symlink. The three states are kept distinct by
|
|
325
|
+
their exit codes: a migration in place under the recorded id — whether or not
|
|
326
|
+
the item cross-checks — exits 4 ("read before acting"), and only a recorded id
|
|
327
|
+
the loaded set holds *no* migration for exits 1 ("nothing landed, re-draft"), so
|
|
328
|
+
following exit 1 can never mint a duplicate of a change on disk. Absent
|
|
329
|
+
`evidence.json` (a legacy proposal, or
|
|
330
|
+
one interrupted before its evidence write) falls to inference over the
|
|
331
|
+
directory, which reads only the body shape the generator produces, so a
|
|
332
|
+
reviewer's notes or a `Thumbs.db` left beside an accepted proposal no longer
|
|
333
|
+
flips the verdict.
|
|
334
|
+
|
|
335
|
+
- **A committed proposal could forge `theurian propose accept`'s own output**
|
|
336
|
+
([#253](https://github.com/theurian/theurian/issues/253)). A proposal directory
|
|
337
|
+
arrives through a contributor's pull request, and a file name or a migration's
|
|
338
|
+
`contentFile` carrying `ESC [ 2 K` and a carriage return erases the line the
|
|
339
|
+
terminal has drawn and prints its own in place of it — reproduced printing this
|
|
340
|
+
command's own output under its own name, on both the refusal path and the
|
|
341
|
+
exit-0 **success** payload (`bodyFiles`, `migrationFile`). The CLI now escapes
|
|
342
|
+
every terminal-control character — the whole C0 block, `DEL` and C1 — at one
|
|
343
|
+
shared sink that every text-mode emitter routes each value and key through, so
|
|
344
|
+
no value any command prints, from any source, reaches a terminal with a raw
|
|
345
|
+
control byte. A value's own newline is escaped too (the output's structural
|
|
346
|
+
newlines are the emitters' own), while printable Unicode (a Japanese title) is
|
|
347
|
+
kept. The `--json` output was never affected.
|
|
348
|
+
|
|
349
|
+
### Changed
|
|
350
|
+
|
|
351
|
+
- **BREAKING — re-accepting an already-accepted proposal now exits 4, not 1**
|
|
352
|
+
([#254](https://github.com/theurian/theurian/issues/254)). The published exit
|
|
353
|
+
code table documented 4 for "that migration is already in place", while the
|
|
354
|
+
natural route to that state — running `theurian propose accept` twice — exited
|
|
355
|
+
1 alongside "no such proposal", an interrupted draft, and a refused
|
|
356
|
+
`contentFile`. Exit 4 keeps its meaning and gains this case: *the knowledge
|
|
357
|
+
state refuses this move, so read it before acting*. It is not "already done" —
|
|
358
|
+
an approved migration set that cannot be read also exits 4, with the proposal
|
|
359
|
+
still waiting — and the published table now says so rather than promising that
|
|
360
|
+
1 always means nothing landed. Scripts that treat any non-zero exit as failure
|
|
361
|
+
are unaffected; one that special-cased 1 for "already accepted" must read 4.
|
|
362
|
+
|
|
363
|
+
Breaking by the compatibility table in
|
|
364
|
+
[`docs/protocol/plugin-core-compatibility.md`](../../docs/protocol/plugin-core-compatibility.md),
|
|
365
|
+
and `protocolVersion` is **not** bumped for it — a recorded, narrowly scoped
|
|
366
|
+
exemption on the same grounds as `system.capabilities.milestone`'s (#206),
|
|
367
|
+
written up under "Changing this contract" in
|
|
368
|
+
[`docs/protocol/mcp-tools.md`](../../docs/protocol/mcp-tools.md).
|
|
369
|
+
|
|
15
370
|
## [0.1.0.dev7] - 2026-08-19
|
|
16
371
|
|
|
17
372
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: theurian
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev9
|
|
4
4
|
Summary: Git-native engineering knowledge platform: versioned, traceable context for AI agents.
|
|
5
5
|
Project-URL: Homepage, https://github.com/theurian/theurian
|
|
6
6
|
Project-URL: Documentation, https://github.com/theurian/theurian/tree/main/docs
|
|
@@ -22,6 +22,7 @@ Requires-Dist: jsonschema==4.26.0
|
|
|
22
22
|
Requires-Dist: pydantic==2.13.4
|
|
23
23
|
Requires-Dist: python-ulid==4.0.1
|
|
24
24
|
Requires-Dist: pyyaml==6.0.3
|
|
25
|
+
Requires-Dist: referencing==0.37.0
|
|
25
26
|
Requires-Dist: typer==0.27.1
|
|
26
27
|
Provides-Extra: all
|
|
27
28
|
Requires-Dist: mcp==2.0.0; extra == 'all'
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "theurian"
|
|
7
|
-
version = "0.1.0.
|
|
7
|
+
version = "0.1.0.dev9"
|
|
8
8
|
description = "Git-native engineering knowledge platform: versioned, traceable context for AI agents."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -36,6 +36,11 @@ dependencies = [
|
|
|
36
36
|
"pydantic==2.13.4",
|
|
37
37
|
"python-ulid==4.0.1",
|
|
38
38
|
"pyyaml==6.0.3",
|
|
39
|
+
# jsonschema's public reference-resolution API, imported directly for the
|
|
40
|
+
# no-network registry that keeps the installed migration schema offline
|
|
41
|
+
# (issue #235). A transitive dep of jsonschema, declared here because we
|
|
42
|
+
# import it -- ADR-0014's "import what you declare".
|
|
43
|
+
"referencing==0.37.0",
|
|
39
44
|
"typer==0.27.1",
|
|
40
45
|
]
|
|
41
46
|
|
|
@@ -8,7 +8,7 @@ plugin must never import them (ADR-0001, CP-2).
|
|
|
8
8
|
|
|
9
9
|
from theurian.domain.compatibility import CURRENT_PROTOCOL_VERSION
|
|
10
10
|
|
|
11
|
-
__version__ = "0.1.0.
|
|
11
|
+
__version__ = "0.1.0.dev9"
|
|
12
12
|
__protocol_version__ = CURRENT_PROTOCOL_VERSION
|
|
13
13
|
|
|
14
14
|
__all__ = ["__protocol_version__", "__version__"]
|