atomadic-forge 0.5.2__tar.gz → 0.6.0__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.
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/CHANGELOG.md +112 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/PKG-INFO +15 -8
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/README.md +14 -7
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/AGENTS_GUIDE.md +2 -2
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/COMMANDS.md +2 -2
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/FIRST_10_MINUTES.md +1 -1
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/pyproject.toml +1 -1
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/__init__.py +1 -1
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/auth_constants.py +0 -1
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/agent_plan_emitter.py +2 -1
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/certify_checks.py +73 -3
- atomadic_forge-0.6.0/src/atomadic_forge/a1_at_functions/code_signature.py +197 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/exported_api_check.py +2 -2
- atomadic_forge-0.6.0/src/atomadic_forge/a1_at_functions/intent_similarity.py +113 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/llm_client.py +6 -5
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/mcp_protocol.py +5 -1
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/provider_resolver.py +3 -1
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/recipes.py +67 -0
- atomadic_forge-0.6.0/src/atomadic_forge/a1_at_functions/research_note_distiller.py +95 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/test_runner.py +23 -25
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/trust_gate_response.py +2 -2
- atomadic_forge-0.6.0/src/atomadic_forge/a2_mo_composites/cost_circuit_breaker.py +266 -0
- atomadic_forge-0.6.0/src/atomadic_forge/a2_mo_composites/cross_agent_intent_deduplicator.py +69 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a2_mo_composites/forge_auth_client.py +0 -1
- atomadic_forge-0.6.0/src/atomadic_forge/a2_mo_composites/hierarchical_memory.py +307 -0
- atomadic_forge-0.6.0/src/atomadic_forge/a3_og_features/agent_hire_protocol.py +228 -0
- atomadic_forge-0.6.0/src/atomadic_forge/a3_og_features/dedup_engine.py +220 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a4_sy_orchestration/copilots_cmd.py +5 -5
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge.egg-info/PKG-INFO +15 -8
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge.egg-info/SOURCES.txt +13 -0
- atomadic_forge-0.6.0/tests/test_agent_hire_protocol.py +99 -0
- atomadic_forge-0.6.0/tests/test_cost_circuit_breaker.py +95 -0
- atomadic_forge-0.6.0/tests/test_dedup_engine.py +155 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_exported_api_check.py +8 -4
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_forge_auth_a1.py +0 -1
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_forge_auth_a2.py +1 -2
- atomadic_forge-0.6.0/tests/test_hierarchical_memory.py +90 -0
- atomadic_forge-0.6.0/tests/test_ling_provider.py +36 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_trust_gate_response.py +4 -2
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/actions/forge-action/README.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/actions/forge-action/action.yml +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/dependabot.yml +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/pull_request_template.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/workflows/ci.yml +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/workflows/customer-refactor.yml +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/workflows/forge-self-certify.yml +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/workflows/forge-studio-ci.yml +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/.github/workflows/release.yml +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/ARCHITECTURE.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/CONTRIBUTING.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/LICENSE +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/MANIFEST.in +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/SECURITY.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/assets/Atomadic-Forge-01.png +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/01-getting-started.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/02-commands.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/03-tutorial.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/04-llm-loops.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/05-faq.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/AIR_GAPPED.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/CI_CD.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/CODEX_WALKTHROUGH.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/FORMALIZATION.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/LANDSCAPE.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/MARKET_POSITIONING.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/MULTI_REPO.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/README.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/RECEIPT.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/RELEASE_CHECKLIST.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/RELEASE_MESSAGING.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/ROADMAP.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/SHOWCASE.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/SIDECAR.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/STUDIO.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/WHY_NOW.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/INDEX.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/chat.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/commandsmith.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/config.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/demo.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/emergent-then-synergy.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/emergent.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/evolve-then-iterate.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/evolve.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/feature-then-emergent.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/iterate.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/synergy-then-emergent.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/commands/synergy.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/compliance/CMMC_AI_MAPPING.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/compliance/CS-1.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/compliance/EU_AI_ACT_ANNEX_IV.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/compliance/FDA_PCCP_MAPPING.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/compliance/SR_11-7_MAPPING.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/tutorials/01-quickstart.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/tutorials/02-your-first-package.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/tutorials/03-the-five-tier-law.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/tutorials/04-plug-in-llms.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/tutorials/05-multi-repo-absorb.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/docs/tutorials/06-javascript-quickstart.md +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/setup.cfg +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/__main__.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/__init__.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/agent_plan_schema.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/commandsmith_types.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/config_defaults.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/emergent_types.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/error_codes.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/forge_types.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/gen_language.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/lang_extensions.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/policy_schema.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/receipt_schema.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/roi_constants.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/semantic_types.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/sidecar_schema.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/synergy_types.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a0_qk_constants/tier_names.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/__init__.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/agent_context_pack.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/agent_memory.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/agent_summary.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/body_extractor.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/card_renderer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/chat_context.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/cherry_pick.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/classify_tier.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/commandsmith_discover.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/commandsmith_render.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/compiler_feedback.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/compliance_checker.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/config_io.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/cs1_renderer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/doc_synthesizer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/emergent_compose.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/emergent_rank.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/emergent_signature_extract.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/emergent_synthesize.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/enforce_planner.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/error_hints.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/evolution_log.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/forge_auth.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/forge_feedback.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/generation_quality.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/import_repair.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/import_smoke.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/js_parser.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/lineage_chain.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/lineage_reader.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/local_signer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/lsp_protocol.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/manifest_diff.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/patch_scorer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/plan_adapter.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/policy_loader.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/preflight_change.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/progress_reporter.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/provider_detect.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/receipt_emitter.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/repo_explainer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/roi_calculator.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/rollback_planner.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/sbom_emitter.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/scaffold_js.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/scaffold_pyproject.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/scaffold_starter.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/scout_walk.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/sidecar_parser.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/sidecar_validator.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/stub_detector.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/synergy_detect.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/synergy_render.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/synergy_surface_extract.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/test_selector.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/tier_init_rebuild.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/tool_composer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/transcript_log.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/validation_commands.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/wire_check.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a2_mo_composites/__init__.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a2_mo_composites/lineage_chain_store.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a2_mo_composites/manifest_store.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a2_mo_composites/plan_store.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a2_mo_composites/receipt_signer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/__init__.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/commandsmith_feature.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/__init__.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/a0_qk_constants/__init__.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/a1_at_functions/__init__.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/demo_packages/mixed_py_js/tests/conftest.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/demo_packages/mixed_py_js/tests/test_mixed.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/demo_runner.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/emergent_feature.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/emergent_pipeline_integration.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/forge_enforce.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/forge_evolve.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/forge_loop.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/forge_pipeline.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/forge_plan_apply.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/lsp_server.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/mcp_server.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/setup_wizard.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a3_og_features/synergy_feature.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a4_sy_orchestration/__init__.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a4_sy_orchestration/cli.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a4_sy_orchestration/login_cmd.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a4_sy_orchestration/whoami_cmd.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/__init__.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/_registry.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/audit.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/chat.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/commandsmith.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/config_cmd.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/demo.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/emergent.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/emergent_then_synergy.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/evolve.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/evolve_then_iterate.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/feature_then_emergent.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/iterate.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/synergy.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/commands/synergy_then_emergent.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge.egg-info/dependency_links.txt +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge.egg-info/entry_points.txt +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge.egg-info/requires.txt +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge.egg-info/top_level.txt +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_aaaa_nexus_client.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_agent_plan.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_agent_summary.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_audit_verb.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_badge_worker.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_body_extractor_repairs.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_card_renderer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_certify_operational_axis.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_chat.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_classify_tier.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_cli_smoke.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_codex_5_complete.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_codex_6_enforce_polyglot.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_commandsmith.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_compiler_feedback.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_compliance_checker.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_config.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_copilots_copilot.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_cs1_renderer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_demo.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_emergent_compose.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_emergent_signature_extract.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_error_codes.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_error_hints.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_evolve_js.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_forge_action.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_forge_enforce.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_generation_quality.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_ignore_and_docs.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_import_smoke.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_iterate_evolve.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_js_certify.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_js_parser.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_js_recon.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_js_wire.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_lineage_chain.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_local_signer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_lsp_protocol.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_manifest_diff.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_mcp_protocol.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_ollama_client.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_pipeline.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_plan_apply.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_pre_audit_smoke.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_precommit_hooks.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_progress_reporter.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_receipt_emitter.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_receipt_schema.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_receipt_signer.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_roi_calculator.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_sbom_emitter.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_scaffold.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_sidecar.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_sidecar_validate.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_stagnation.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_stub_detector.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_synergy.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_test_runner.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_tier_init_rebuild.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_vscode_extension_manifest.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_whoami_cmd.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_wire_certify.py +0 -0
- {atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/tests/test_wire_suggest_repairs.py +0 -0
|
@@ -1,5 +1,117 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 - Frontier features: dedup, budget, memory, swarm hiring, Ling-2.6-1T
|
|
4
|
+
|
|
5
|
+
Major minor — five new capabilities cherry-picked from forge-deluxe-seed
|
|
6
|
+
plus a frontier free-tier LLM, plus three MCP bug fixes and agent-optimized
|
|
7
|
+
certify output.
|
|
8
|
+
|
|
9
|
+
### Added — capabilities
|
|
10
|
+
|
|
11
|
+
- **`dedup_engine`** — orchestrates `intent_similarity` + `code_signature`
|
|
12
|
+
+ `research_note_distiller` to catch duplicate research notes AND
|
|
13
|
+
duplicate code logic at the gate. The "never reinvent the wheel"
|
|
14
|
+
primitive, ported with 13 tests.
|
|
15
|
+
|
|
16
|
+
- **`cost_circuit_breaker`** — multi-tier (per-task / per-session /
|
|
17
|
+
per-day) USD + token budget with hard-kill, soft-warn-at-80%, and
|
|
18
|
+
no-progress stuck detection. Defaults match OpenHands
|
|
19
|
+
(MAX_ITERATIONS=100, $100/day). Closes the production-incident gap
|
|
20
|
+
($47k loop, $30k loop) called out in cycle-14 SOTA research.
|
|
21
|
+
|
|
22
|
+
- **`hierarchical_memory`** — 4-tier MemGPT pattern (M0 working /
|
|
23
|
+
M1 core pinned / M2 episodic / M3 reflection) with Park-2023
|
|
24
|
+
recency × importance × relevance scoring. Pure stdlib sqlite3 —
|
|
25
|
+
air-gappable.
|
|
26
|
+
|
|
27
|
+
- **`agent_hire_protocol`** — 5-step swarm SOP (sealed-probe vetting
|
|
28
|
+
→ trust gate → similarity check → contract → signed receipt) with
|
|
29
|
+
D_max=3 (ChatDev empirical limit) for safe multi-agent fanout.
|
|
30
|
+
|
|
31
|
+
- **`ling` / `--provider ling`** — wires Ling-2.6-1T (1T-param MoE,
|
|
32
|
+
262K context, SOTA SWE-bench) via OpenRouter at the **free tier**.
|
|
33
|
+
Forge users now get a frontier model for iterate/evolve at zero
|
|
34
|
+
cost; OpenRouter default model also bumped from gemma-3-27b-it
|
|
35
|
+
to ling-2.6-1t.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **P0 (critical)** `recon` MCP tool no longer overflows LLM context
|
|
42
|
+
windows. `symbols[]` (771 K chars on forge itself) is stripped from
|
|
43
|
+
the default response; pass `verbose: true` to get the full walk.
|
|
44
|
+
`symbol_count` remains so agents still see cardinality inline.
|
|
45
|
+
|
|
46
|
+
- **P1 (high)** `certify` behavioral score no longer reports `ran:
|
|
47
|
+
false, pass_ratio: 0.0` on repos using `xfailed`/`xpassed` pytest
|
|
48
|
+
status words. The `_parse_pytest_summary` parser now uses independent
|
|
49
|
+
per-metric regex patterns instead of a single monolithic regex that
|
|
50
|
+
choked on unknown status words between "passed" and "in Xs".
|
|
51
|
+
Atomadic-Lang's certify score recovers from 70 to 100.
|
|
52
|
+
|
|
53
|
+
- **P2 (high)** `auto_plan` verdict no longer reports `PASS` for repos
|
|
54
|
+
with score < 75 and no action cards. `not cards` alone was treated as
|
|
55
|
+
PASS regardless of score; now requires `score >= 75`. No-input plans
|
|
56
|
+
(score defaults to 0.0) correctly return `FAIL`.
|
|
57
|
+
|
|
58
|
+
### Added — certify polish
|
|
59
|
+
|
|
60
|
+
- `certify` output now includes `health_summary` (score + verdict +
|
|
61
|
+
blockers + scan_duration_ms) — a single at-a-glance block for agent
|
|
62
|
+
loops that don't want to parse the full response.
|
|
63
|
+
|
|
64
|
+
- `certify` output now includes `axes` dict with per-axis `ok` bool and
|
|
65
|
+
`how_to_fix` string. When an axis fails, agents get an explicit action
|
|
66
|
+
("Add README.md at the project root." / "Run forge wire src
|
|
67
|
+
--suggest-repairs -- N violation(s).") instead of just a flag.
|
|
68
|
+
|
|
69
|
+
- `certify` now reports `scan_duration_ms` at top level and inside
|
|
70
|
+
`health_summary` — agents doing performance budgeting can use this
|
|
71
|
+
for timeout planning.
|
|
72
|
+
|
|
73
|
+
- New recipe `bump_version` — checklist for patch/minor/major bumps:
|
|
74
|
+
edit pyproject.toml, update __version__, write CHANGELOG entry,
|
|
75
|
+
certify, commit, tag.
|
|
76
|
+
|
|
77
|
+
- New recipe `fix_test_detection` — debugging guide for when certify
|
|
78
|
+
reports `ran=false` or `pass_ratio=0` despite pytest passing locally:
|
|
79
|
+
traces xfailed parse failure, wrong-package import filter, and import
|
|
80
|
+
smoke failure paths.
|
|
81
|
+
|
|
82
|
+
### Internal
|
|
83
|
+
|
|
84
|
+
- CI Ruff lint debt cleared: 50 errors → 0 across recent merges
|
|
85
|
+
(I001/F401/UP037/UP006/UP035 auto-fixed; UP038/B006/E701/E702/F841
|
|
86
|
+
manually). Lint now blocks regressions instead of being permanently red.
|
|
87
|
+
|
|
88
|
+
- Test count: 937 → 975 (+38) — every new capability shipped with
|
|
89
|
+
pinning tests; certify holds 100.0/100 across the lift.
|
|
90
|
+
|
|
91
|
+
- README provider matrix updated: Anthropic default → `claude-sonnet-4-6`,
|
|
92
|
+
OpenAI default → `gpt-4o-mini` (with override hints), `ling` row added.
|
|
93
|
+
|
|
94
|
+
- Live demo (`forge.atomadic.tech`) and invest links surfaced in README.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 0.5.3 - Documentation metadata sync
|
|
99
|
+
|
|
100
|
+
Small follow-up to `0.5.2` so the published package description and
|
|
101
|
+
current-facing docs no longer advertise stale `0.3.x` version strings,
|
|
102
|
+
old test counts, or the pre-`0.5.2` MCP tool count.
|
|
103
|
+
|
|
104
|
+
### Fixed
|
|
105
|
+
|
|
106
|
+
- README install and contributor snippets now show the current Forge
|
|
107
|
+
version family and `937 passed, 2 skipped`.
|
|
108
|
+
- Current ecosystem positioning now reflects the `0.5.3` PyPI line and
|
|
109
|
+
23-tool MCP surface.
|
|
110
|
+
- First-run and VS Code extension docs now reference the current MCP
|
|
111
|
+
surface/version expectations.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
3
115
|
## 0.5.2 - Agent ergonomics and language-aware guidance
|
|
4
116
|
|
|
5
117
|
Implements the first round of quality-of-life improvements from the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: atomadic-forge
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Atomadic Forge — absorb, enforce, emerge. Polyglot (Python + JavaScript/TypeScript) architecture guardian for AI-generated code.
|
|
5
5
|
Author: Atomadic
|
|
6
6
|
License-Expression: BUSL-1.1
|
|
@@ -45,6 +45,8 @@ Dynamic: license-file
|
|
|
45
45
|
|
|
46
46
|
> **Absorb. Enforce. Emerge.** The architecture substrate for AI-generated code — now polyglot (Python, JavaScript, TypeScript).
|
|
47
47
|
|
|
48
|
+
🔗 **Try it live:** [forge.atomadic.tech](https://forge.atomadic.tech) — paste any GitHub repo and watch the analysis in real time.
|
|
49
|
+
|
|
48
50
|
Forge is a monadic-architecture engine that does three things no existing
|
|
49
51
|
tool combines:
|
|
50
52
|
|
|
@@ -185,7 +187,7 @@ Each tier is a layer of **verified building blocks**. Higher tiers never invent
|
|
|
185
187
|
|
|
186
188
|
```bash
|
|
187
189
|
pip install atomadic-forge
|
|
188
|
-
forge --version # atomadic-forge 0.3
|
|
190
|
+
forge --version # atomadic-forge 0.5.3
|
|
189
191
|
forge doctor # environment check
|
|
190
192
|
```
|
|
191
193
|
|
|
@@ -198,7 +200,7 @@ generate).
|
|
|
198
200
|
```bash
|
|
199
201
|
git clone https://github.com/atomadictech/atomadic-forge && cd atomadic-forge
|
|
200
202
|
pip install -e ".[dev]"
|
|
201
|
-
python -m pytest #
|
|
203
|
+
python -m pytest # 937 passed, 2 skipped
|
|
202
204
|
```
|
|
203
205
|
|
|
204
206
|
## AI Agent integration (MCP)
|
|
@@ -320,9 +322,10 @@ forge chat ask "hello" --provider stub --no-cwd-context --json
|
|
|
320
322
|
|----------|------|---------|---------------|-------------|
|
|
321
323
|
| `gemini` | **free tier** | `GEMINI_API_KEY` / `GOOGLE_API_KEY` | `gemini-2.5-flash` | Best free cloud option; override with `FORGE_GEMINI_MODEL` |
|
|
322
324
|
| `nexus` / `aaaa-nexus` | paid | `AAAA_NEXUS_API_KEY` | (Nexus default) | AAAA-Nexus sovereign AI; most reliable for long runs |
|
|
323
|
-
| `anthropic` | paid | `ANTHROPIC_API_KEY` | `claude-
|
|
324
|
-
| `openai` | paid | `OPENAI_API_KEY` | `gpt-4o-mini` | Cheap GPT path |
|
|
325
|
-
| `openrouter` | **free tier available** | `OPENROUTER_API_KEY` | `
|
|
325
|
+
| `anthropic` | paid | `ANTHROPIC_API_KEY` | `claude-sonnet-4-6` | Highest code quality (Claude 4.x; override with `claude-opus-4-7` for max reasoning or `claude-haiku-4-5-20251001` for speed) |
|
|
326
|
+
| `openai` | paid | `OPENAI_API_KEY` | `gpt-4o-mini` | Cheap GPT path; override to `gpt-4.1` or `gpt-4o` for higher quality |
|
|
327
|
+
| `openrouter` | **free tier available** | `OPENROUTER_API_KEY` | `inclusionai/ling-2.6-1t:free` | Access 200+ models; good fallback when Gemini quota exhausted; override with `FORGE_OPENROUTER_MODEL` |
|
|
328
|
+
| `ling` | **free** | `OPENROUTER_API_KEY` | `inclusionai/ling-2.6-1t:free` | Shortcut for Ling-2.6-1T (1T-param MoE, 262K ctx, SOTA SWE-bench) — frontier model at zero cost via OpenRouter |
|
|
326
329
|
| `ollama` | free, local | `FORGE_OLLAMA=1` | `qwen2.5-coder:7b` | Offline; fully private |
|
|
327
330
|
| `stub` | free, offline | n/a | n/a | Tests, CI, dry-runs |
|
|
328
331
|
|
|
@@ -449,7 +452,7 @@ Forge ships with named limits. No overpromise.
|
|
|
449
452
|
| Product | What it is | Status |
|
|
450
453
|
|---------|------------|--------|
|
|
451
454
|
| **AAAA-Nexus** | Trust/safety/payments substrate for autonomous agents | Live at [atomadic.tech](https://atomadic.tech) |
|
|
452
|
-
| **Atomadic Forge** | Absorb-and-emerge engine for developers (this repo) | **0.3
|
|
455
|
+
| **Atomadic Forge** | Absorb-and-emerge engine for developers (this repo) | **0.5.3** — on PyPI, 937 tests, 100/100, 23-tool MCP server, desktop GUI |
|
|
453
456
|
| **Atomadic Assistant** | Sovereign AI assistant with cognitive loop on Cloudflare | In development |
|
|
454
457
|
|
|
455
458
|
## License
|
|
@@ -477,7 +480,7 @@ Apache 2.0.
|
|
|
477
480
|
**Forge itself is monadic.** Every source file belongs to one tier. The repo is a worked example:
|
|
478
481
|
|
|
479
482
|
```bash
|
|
480
|
-
python -m pytest #
|
|
483
|
+
python -m pytest # 937 passed, 2 skipped
|
|
481
484
|
forge doctor # Environment check
|
|
482
485
|
forge wire src/atomadic_forge # Scan for violations (PASS)
|
|
483
486
|
forge certify . --fail-under 100 # Score and gate the repo (100/100)
|
|
@@ -508,3 +511,7 @@ forge commandsmith smoke # Smoke-test all 36+ registered verbs
|
|
|
508
511
|
- ✓ **Cloudflare badge worker** — live certify score in any README
|
|
509
512
|
- ✗ Chain-of-custody notarization (future)
|
|
510
513
|
- ✗ Rust / Go tier classification (roadmap)
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
💰 **Interested in investing?** [invest.atomadic.tech](https://invest.atomadic.tech) — learn about the Atomadic Technologies ecosystem.
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
> **Absorb. Enforce. Emerge.** The architecture substrate for AI-generated code — now polyglot (Python, JavaScript, TypeScript).
|
|
15
15
|
|
|
16
|
+
🔗 **Try it live:** [forge.atomadic.tech](https://forge.atomadic.tech) — paste any GitHub repo and watch the analysis in real time.
|
|
17
|
+
|
|
16
18
|
Forge is a monadic-architecture engine that does three things no existing
|
|
17
19
|
tool combines:
|
|
18
20
|
|
|
@@ -153,7 +155,7 @@ Each tier is a layer of **verified building blocks**. Higher tiers never invent
|
|
|
153
155
|
|
|
154
156
|
```bash
|
|
155
157
|
pip install atomadic-forge
|
|
156
|
-
forge --version # atomadic-forge 0.3
|
|
158
|
+
forge --version # atomadic-forge 0.5.3
|
|
157
159
|
forge doctor # environment check
|
|
158
160
|
```
|
|
159
161
|
|
|
@@ -166,7 +168,7 @@ generate).
|
|
|
166
168
|
```bash
|
|
167
169
|
git clone https://github.com/atomadictech/atomadic-forge && cd atomadic-forge
|
|
168
170
|
pip install -e ".[dev]"
|
|
169
|
-
python -m pytest #
|
|
171
|
+
python -m pytest # 937 passed, 2 skipped
|
|
170
172
|
```
|
|
171
173
|
|
|
172
174
|
## AI Agent integration (MCP)
|
|
@@ -288,9 +290,10 @@ forge chat ask "hello" --provider stub --no-cwd-context --json
|
|
|
288
290
|
|----------|------|---------|---------------|-------------|
|
|
289
291
|
| `gemini` | **free tier** | `GEMINI_API_KEY` / `GOOGLE_API_KEY` | `gemini-2.5-flash` | Best free cloud option; override with `FORGE_GEMINI_MODEL` |
|
|
290
292
|
| `nexus` / `aaaa-nexus` | paid | `AAAA_NEXUS_API_KEY` | (Nexus default) | AAAA-Nexus sovereign AI; most reliable for long runs |
|
|
291
|
-
| `anthropic` | paid | `ANTHROPIC_API_KEY` | `claude-
|
|
292
|
-
| `openai` | paid | `OPENAI_API_KEY` | `gpt-4o-mini` | Cheap GPT path |
|
|
293
|
-
| `openrouter` | **free tier available** | `OPENROUTER_API_KEY` | `
|
|
293
|
+
| `anthropic` | paid | `ANTHROPIC_API_KEY` | `claude-sonnet-4-6` | Highest code quality (Claude 4.x; override with `claude-opus-4-7` for max reasoning or `claude-haiku-4-5-20251001` for speed) |
|
|
294
|
+
| `openai` | paid | `OPENAI_API_KEY` | `gpt-4o-mini` | Cheap GPT path; override to `gpt-4.1` or `gpt-4o` for higher quality |
|
|
295
|
+
| `openrouter` | **free tier available** | `OPENROUTER_API_KEY` | `inclusionai/ling-2.6-1t:free` | Access 200+ models; good fallback when Gemini quota exhausted; override with `FORGE_OPENROUTER_MODEL` |
|
|
296
|
+
| `ling` | **free** | `OPENROUTER_API_KEY` | `inclusionai/ling-2.6-1t:free` | Shortcut for Ling-2.6-1T (1T-param MoE, 262K ctx, SOTA SWE-bench) — frontier model at zero cost via OpenRouter |
|
|
294
297
|
| `ollama` | free, local | `FORGE_OLLAMA=1` | `qwen2.5-coder:7b` | Offline; fully private |
|
|
295
298
|
| `stub` | free, offline | n/a | n/a | Tests, CI, dry-runs |
|
|
296
299
|
|
|
@@ -417,7 +420,7 @@ Forge ships with named limits. No overpromise.
|
|
|
417
420
|
| Product | What it is | Status |
|
|
418
421
|
|---------|------------|--------|
|
|
419
422
|
| **AAAA-Nexus** | Trust/safety/payments substrate for autonomous agents | Live at [atomadic.tech](https://atomadic.tech) |
|
|
420
|
-
| **Atomadic Forge** | Absorb-and-emerge engine for developers (this repo) | **0.3
|
|
423
|
+
| **Atomadic Forge** | Absorb-and-emerge engine for developers (this repo) | **0.5.3** — on PyPI, 937 tests, 100/100, 23-tool MCP server, desktop GUI |
|
|
421
424
|
| **Atomadic Assistant** | Sovereign AI assistant with cognitive loop on Cloudflare | In development |
|
|
422
425
|
|
|
423
426
|
## License
|
|
@@ -445,7 +448,7 @@ Apache 2.0.
|
|
|
445
448
|
**Forge itself is monadic.** Every source file belongs to one tier. The repo is a worked example:
|
|
446
449
|
|
|
447
450
|
```bash
|
|
448
|
-
python -m pytest #
|
|
451
|
+
python -m pytest # 937 passed, 2 skipped
|
|
449
452
|
forge doctor # Environment check
|
|
450
453
|
forge wire src/atomadic_forge # Scan for violations (PASS)
|
|
451
454
|
forge certify . --fail-under 100 # Score and gate the repo (100/100)
|
|
@@ -476,3 +479,7 @@ forge commandsmith smoke # Smoke-test all 36+ registered verbs
|
|
|
476
479
|
- ✓ **Cloudflare badge worker** — live certify score in any README
|
|
477
480
|
- ✗ Chain-of-custody notarization (future)
|
|
478
481
|
- ✗ Rust / Go tier classification (roadmap)
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
💰 **Interested in investing?** [invest.atomadic.tech](https://invest.atomadic.tech) — learn about the Atomadic Technologies ecosystem.
|
|
@@ -58,7 +58,7 @@ Or against the installed CLI:
|
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
60
|
$ forge --version
|
|
61
|
-
atomadic-forge 0.5.
|
|
61
|
+
atomadic-forge 0.5.3
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
The `--version` flag (and `-V`) is the canonical "Forge is wired in
|
|
@@ -368,7 +368,7 @@ Forge's normative answer; only the *attestation chain* is missing.
|
|
|
368
368
|
| Symptom | Likely cause | Fix |
|
|
369
369
|
|---|---|---|
|
|
370
370
|
| Tools list returns 0 tools | MCP client didn't pass `--project .` | Check args in MCP config |
|
|
371
|
-
| Tools list returns < 23 (e.g. 21 or 10) | Old `forge` install | `pip install -U atomadic-forge` (or `pip install -e .` from the repo). Pin `forge --version >= 0.5.
|
|
371
|
+
| Tools list returns < 23 (e.g. 21 or 10) | Old `forge` install | `pip install -U atomadic-forge` (or `pip install -e .` from the repo). Pin `forge --version >= 0.5.3`. |
|
|
372
372
|
| `wire` says PASS but agent still hits import errors | Agent is running tests in a different working directory | `--project` should match the test cwd |
|
|
373
373
|
| `certify` returns score 90 instead of 100 | Project has no `.github/workflows/` and no `CHANGELOG.md` (operational axis is 0) | Add either; both are 5pts |
|
|
374
374
|
| Receipt's `signatures` is null | `AAAA_NEXUS_API_KEY` not set, or AAAA-Nexus endpoint not yet shipped | Soft-fail behavior — Receipt is still valid for local use |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Atomadic Forge — Command Reference
|
|
2
2
|
|
|
3
|
-
All verbs available in the `forge` CLI as of 0.5.
|
|
3
|
+
All verbs available in the `forge` CLI as of 0.5.3.
|
|
4
4
|
|
|
5
5
|
## Pipeline / absorption
|
|
6
6
|
|
|
@@ -419,7 +419,7 @@ Prints the installed Forge version and exits 0. Hardened in v0.3.0
|
|
|
419
419
|
|
|
420
420
|
```bash
|
|
421
421
|
$ forge --version
|
|
422
|
-
atomadic-forge 0.5.
|
|
422
|
+
atomadic-forge 0.5.3
|
|
423
423
|
```
|
|
424
424
|
|
|
425
425
|
### `.forge` sidecars — Lane D W8
|
|
@@ -185,7 +185,7 @@ You now know enough to use Forge productively. For specific topics:
|
|
|
185
185
|
- [tutorials/](tutorials/) — quickstart tutorials and the JS / TS path.
|
|
186
186
|
- **[AGENTS_GUIDE.md](AGENTS_GUIDE.md)** — using Forge from a coding
|
|
187
187
|
agent (Cursor / Claude Code / Aider / Devin / Copilot / Codex) via
|
|
188
|
-
the built-in MCP server. One config snippet, **
|
|
188
|
+
the built-in MCP server. One config snippet, **23 tools**, 5
|
|
189
189
|
resources. The fastest way to put Forge in front of every PR your
|
|
190
190
|
team's agents touch.
|
|
191
191
|
- [MULTI_REPO.md](MULTI_REPO.md) — absorbing more than one repo at once.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "atomadic-forge"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.6.0"
|
|
8
8
|
description = "Atomadic Forge — absorb, enforce, emerge. Polyglot (Python + JavaScript/TypeScript) architecture guardian for AI-generated code."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -279,7 +279,8 @@ def emit_agent_plan(
|
|
|
279
279
|
score = float((certify_report or {}).get("score", 0.0))
|
|
280
280
|
if wire_report and wire_report.get("verdict") == "PASS" and score >= 100:
|
|
281
281
|
verdict = "PASS"
|
|
282
|
-
elif not cards:
|
|
282
|
+
elif not cards and score >= 75:
|
|
283
|
+
# No action cards AND score is acceptable -- genuinely passing.
|
|
283
284
|
verdict = "PASS"
|
|
284
285
|
else:
|
|
285
286
|
verdict = "FAIL"
|
{atomadic_forge-0.5.2 → atomadic_forge-0.6.0}/src/atomadic_forge/a1_at_functions/certify_checks.py
RENAMED
|
@@ -285,6 +285,7 @@ def check_changelog(root: Path) -> tuple[bool, dict]:
|
|
|
285
285
|
|
|
286
286
|
def certify(root: Path, *, project: str = "Atomadic project",
|
|
287
287
|
package: str | None = None) -> dict:
|
|
288
|
+
_scan_start = time.perf_counter()
|
|
288
289
|
docs_ok, docs_d = check_documentation(root)
|
|
289
290
|
tests_ok, tests_d = check_tests_present(root)
|
|
290
291
|
layout_ok, layout_d = check_tier_layout(root, package)
|
|
@@ -358,12 +359,14 @@ def certify(root: Path, *, project: str = "Atomadic project",
|
|
|
358
359
|
"(or run `forge auto` to scaffold them).")
|
|
359
360
|
if not wire_ok:
|
|
360
361
|
issues.append(f"Upward-import violations: {wire_d['violation_count']}")
|
|
361
|
-
recs.append("Run `forge wire` to inspect violations, then move imports
|
|
362
|
+
recs.append("Run `forge wire` to inspect violations, then move imports "
|
|
363
|
+
"down-tier or split modules.")
|
|
362
364
|
if not no_stubs:
|
|
363
365
|
issues.append(f"Stub bodies detected: {len(stub_findings)} "
|
|
364
366
|
"function(s) with `pass`/NotImplementedError/TODO")
|
|
365
367
|
for f in stub_findings[:5]:
|
|
366
|
-
issues.append(f" · {f['file']}:{f['lineno']}
|
|
368
|
+
issues.append(f" · {f['file']}:{f['lineno']} "
|
|
369
|
+
f"{f['qualname']} ({f['kind']})")
|
|
367
370
|
recs.append("Replace stub bodies with real implementations before shipping.")
|
|
368
371
|
if smoke is not None and not importable:
|
|
369
372
|
issues.append(f"Package fails to import: {smoke['error_kind']} — "
|
|
@@ -392,7 +395,7 @@ def certify(root: Path, *, project: str = "Atomadic project",
|
|
|
392
395
|
# docs / layout / wire — 10 each (30 max — structural axis)
|
|
393
396
|
# tests-present — 5 (structural axis)
|
|
394
397
|
# importable runtime — 25 (runtime axis)
|
|
395
|
-
# tests-pass-ratio — 30 max (behavioural axis
|
|
398
|
+
# tests-pass-ratio — 30 max (behavioural axis)
|
|
396
399
|
# ci workflow — 5 (operational axis)
|
|
397
400
|
# changelog/release notes — 5 (operational axis)
|
|
398
401
|
# stub-body penalty — up to 40 deducted
|
|
@@ -410,6 +413,9 @@ def certify(root: Path, *, project: str = "Atomadic project",
|
|
|
410
413
|
+ (5 if changelog_ok else 0)
|
|
411
414
|
)
|
|
412
415
|
score = max(0.0, float(structural + runtime + behavioral + operational) - stub_pen)
|
|
416
|
+
scan_duration_ms = int((time.perf_counter() - _scan_start) * 1000)
|
|
417
|
+
blockers = len([i for i in issues if not i.startswith(" ·")])
|
|
418
|
+
verdict = "PASS" if score >= 75 and blockers == 0 else "FAIL"
|
|
413
419
|
return {
|
|
414
420
|
"schema_version": "atomadic-forge.certify/v1",
|
|
415
421
|
"project": project,
|
|
@@ -424,6 +430,12 @@ def certify(root: Path, *, project: str = "Atomadic project",
|
|
|
424
430
|
"ci_workflow_present": ci_ok,
|
|
425
431
|
"changelog_present": changelog_ok,
|
|
426
432
|
"score": score,
|
|
433
|
+
"health_summary": {
|
|
434
|
+
"score": score,
|
|
435
|
+
"verdict": verdict,
|
|
436
|
+
"blockers": blockers,
|
|
437
|
+
"scan_duration_ms": scan_duration_ms,
|
|
438
|
+
},
|
|
427
439
|
"score_components": {
|
|
428
440
|
"structural": structural,
|
|
429
441
|
"runtime": runtime,
|
|
@@ -433,6 +445,64 @@ def certify(root: Path, *, project: str = "Atomadic project",
|
|
|
433
445
|
},
|
|
434
446
|
"issues": issues,
|
|
435
447
|
"recommendations": recs,
|
|
448
|
+
"axes": {
|
|
449
|
+
"documentation": {
|
|
450
|
+
"ok": docs_ok, "score_weight": 10,
|
|
451
|
+
"how_to_fix": ("Add README.md or docs/*.md files."
|
|
452
|
+
if not docs_ok else None),
|
|
453
|
+
},
|
|
454
|
+
"tests_present": {
|
|
455
|
+
"ok": tests_ok, "score_weight": 5,
|
|
456
|
+
"how_to_fix": ("Create tests/test_*.py with at least one test."
|
|
457
|
+
if not tests_ok else None),
|
|
458
|
+
},
|
|
459
|
+
"tier_layout": {
|
|
460
|
+
"ok": layout_ok, "score_weight": 10,
|
|
461
|
+
"how_to_fix": (
|
|
462
|
+
"Add 3+ of a0_qk_constants/ a1_at_functions/ "
|
|
463
|
+
"a2_mo_composites/ a3_og_features/ a4_sy_orchestration/."
|
|
464
|
+
) if not layout_ok else None,
|
|
465
|
+
},
|
|
466
|
+
"wire_clean": {
|
|
467
|
+
"ok": wire_ok, "score_weight": 10,
|
|
468
|
+
"how_to_fix": (
|
|
469
|
+
f"Fix {wire_d['violation_count']} upward import(s): "
|
|
470
|
+
"run forge wire --suggest-repairs."
|
|
471
|
+
) if not wire_ok else None,
|
|
472
|
+
},
|
|
473
|
+
"no_stubs": {
|
|
474
|
+
"ok": no_stubs, "score_weight": 0,
|
|
475
|
+
"how_to_fix": (
|
|
476
|
+
f"Replace {len(stub_findings)} stub bodies "
|
|
477
|
+
"(pass/NotImplementedError/TODO) with real code."
|
|
478
|
+
) if not no_stubs else None,
|
|
479
|
+
},
|
|
480
|
+
"importable": {
|
|
481
|
+
"ok": importable, "score_weight": 25,
|
|
482
|
+
"how_to_fix": (
|
|
483
|
+
f"Fix import error: {smoke['error_kind']} — "
|
|
484
|
+
f"{smoke['error_message']}"
|
|
485
|
+
if smoke else "Package not importable."
|
|
486
|
+
) if not importable else None,
|
|
487
|
+
},
|
|
488
|
+
"tests_pass": {
|
|
489
|
+
"ok": test_pass_ratio == 1.0, "score_weight": 30,
|
|
490
|
+
"how_to_fix": (
|
|
491
|
+
f"Fix {test_run['failed']} failing test(s)." if test_run
|
|
492
|
+
else "Run pytest to diagnose."
|
|
493
|
+
) if test_pass_ratio < 1.0 else None,
|
|
494
|
+
},
|
|
495
|
+
"ci_workflow": {
|
|
496
|
+
"ok": ci_ok, "score_weight": 5,
|
|
497
|
+
"how_to_fix": ("Add .github/workflows/ci.yml."
|
|
498
|
+
if not ci_ok else None),
|
|
499
|
+
},
|
|
500
|
+
"changelog": {
|
|
501
|
+
"ok": changelog_ok, "score_weight": 5,
|
|
502
|
+
"how_to_fix": ("Add CHANGELOG.md (200+ bytes) at project root."
|
|
503
|
+
if not changelog_ok else None),
|
|
504
|
+
},
|
|
505
|
+
},
|
|
436
506
|
"detail": {"docs": docs_d, "tests": tests_d, "layout": layout_d,
|
|
437
507
|
"wire": wire_d,
|
|
438
508
|
"ci": ci_d,
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""Tier a1 - deterministic semantic fingerprint for Python source.
|
|
2
|
+
|
|
3
|
+
Pure stateless. Given a Python source string, returns stable hashes
|
|
4
|
+
that survive renames, whitespace changes, and comment edits but
|
|
5
|
+
break on real logic changes. The atomic primitive that makes
|
|
6
|
+
"never reinvent the wheel" enforceable: two modules with identical
|
|
7
|
+
function-shape signatures are duplicate logic.
|
|
8
|
+
|
|
9
|
+
PREVENT pillar - block duplicate emits at the gate before they ship.
|
|
10
|
+
|
|
11
|
+
Returned shape::
|
|
12
|
+
|
|
13
|
+
ModuleSignature(
|
|
14
|
+
schema="atomadic-forge.code-signature/v1",
|
|
15
|
+
module_hash="<hex>", # full-module shape hash
|
|
16
|
+
functions=[FunctionSignature(...), ...],
|
|
17
|
+
classes=[ClassSignature(...), ...],
|
|
18
|
+
imports=("os", "ast", ...), # sorted top-level imports
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
Two ModuleSignatures with the same ``module_hash`` are byte-stable
|
|
22
|
+
duplicates of each other's logic, regardless of identifier names
|
|
23
|
+
or formatting.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import ast
|
|
29
|
+
import hashlib
|
|
30
|
+
from dataclasses import dataclass, field
|
|
31
|
+
|
|
32
|
+
SCHEMA: str = "atomadic-forge.code-signature/v1"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class FunctionSignature:
|
|
37
|
+
name: str
|
|
38
|
+
arg_count: int
|
|
39
|
+
is_async: bool
|
|
40
|
+
body_hash: str # hash of normalized AST (no identifiers)
|
|
41
|
+
calls: tuple[str, ...] # sorted set of names this function calls
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class ClassSignature:
|
|
46
|
+
name: str
|
|
47
|
+
method_count: int
|
|
48
|
+
has_state: bool # __init__ assigns to self.X
|
|
49
|
+
body_hash: str
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True)
|
|
53
|
+
class ModuleSignature:
|
|
54
|
+
schema: str = SCHEMA
|
|
55
|
+
module_hash: str = ""
|
|
56
|
+
functions: tuple[FunctionSignature, ...] = field(default_factory=tuple)
|
|
57
|
+
classes: tuple[ClassSignature, ...] = field(default_factory=tuple)
|
|
58
|
+
imports: tuple[str, ...] = field(default_factory=tuple)
|
|
59
|
+
parse_ok: bool = True
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _normalize_ast(node: ast.AST) -> str:
|
|
63
|
+
"""Walk an AST node and emit a string of just the structural
|
|
64
|
+
shape - no identifier names, no string literals, no docstrings.
|
|
65
|
+
Two functions with identical control flow + call shape get
|
|
66
|
+
identical normalized output regardless of variable names."""
|
|
67
|
+
parts: list[str] = []
|
|
68
|
+
for sub in ast.walk(node):
|
|
69
|
+
cls = type(sub).__name__
|
|
70
|
+
# Capture structural detail without leaking identifiers.
|
|
71
|
+
if isinstance(sub, ast.Constant):
|
|
72
|
+
parts.append(f"C:{type(sub.value).__name__}")
|
|
73
|
+
elif isinstance(sub, ast.BinOp):
|
|
74
|
+
parts.append(f"B:{type(sub.op).__name__}")
|
|
75
|
+
elif isinstance(sub, ast.Compare):
|
|
76
|
+
ops = "/".join(type(o).__name__ for o in sub.ops)
|
|
77
|
+
parts.append(f"Cmp:{ops}")
|
|
78
|
+
elif isinstance(sub, ast.UnaryOp):
|
|
79
|
+
parts.append(f"U:{type(sub.op).__name__}")
|
|
80
|
+
elif isinstance(sub, ast.For | ast.While | ast.If | ast.Try
|
|
81
|
+
| ast.With | ast.Return | ast.Raise
|
|
82
|
+
| ast.Assign | ast.AugAssign | ast.AnnAssign
|
|
83
|
+
| ast.FunctionDef | ast.AsyncFunctionDef
|
|
84
|
+
| ast.ClassDef | ast.Lambda | ast.ListComp
|
|
85
|
+
| ast.DictComp | ast.SetComp | ast.GeneratorExp
|
|
86
|
+
| ast.Import | ast.ImportFrom | ast.Call
|
|
87
|
+
| ast.Attribute | ast.Subscript):
|
|
88
|
+
parts.append(cls)
|
|
89
|
+
return "|".join(parts)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _hash(s: str) -> str:
|
|
93
|
+
return hashlib.sha256(s.encode("utf-8")).hexdigest()[:16]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _called_names(func_node: ast.AST) -> tuple[str, ...]:
|
|
97
|
+
"""Set of names called inside a function body. Used to detect
|
|
98
|
+
'this module just wraps existing primitives' (good reuse) vs
|
|
99
|
+
'this module reimplements logic locally' (bad)."""
|
|
100
|
+
out: set[str] = set()
|
|
101
|
+
for sub in ast.walk(func_node):
|
|
102
|
+
if isinstance(sub, ast.Call):
|
|
103
|
+
f = sub.func
|
|
104
|
+
if isinstance(f, ast.Name):
|
|
105
|
+
out.add(f.id)
|
|
106
|
+
elif isinstance(f, ast.Attribute):
|
|
107
|
+
out.add(f.attr)
|
|
108
|
+
return tuple(sorted(out))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _has_self_assign(func_node: ast.FunctionDef) -> bool:
|
|
112
|
+
for stmt in ast.walk(func_node):
|
|
113
|
+
if isinstance(stmt, ast.Assign):
|
|
114
|
+
for tgt in stmt.targets:
|
|
115
|
+
if (isinstance(tgt, ast.Attribute)
|
|
116
|
+
and isinstance(tgt.value, ast.Name)
|
|
117
|
+
and tgt.value.id == "self"):
|
|
118
|
+
return True
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _signature_function(node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
123
|
+
) -> FunctionSignature:
|
|
124
|
+
body_hash = _hash(_normalize_ast(node))
|
|
125
|
+
return FunctionSignature(
|
|
126
|
+
name=node.name,
|
|
127
|
+
arg_count=len(node.args.args) + len(node.args.kwonlyargs),
|
|
128
|
+
is_async=isinstance(node, ast.AsyncFunctionDef),
|
|
129
|
+
body_hash=body_hash,
|
|
130
|
+
calls=_called_names(node),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _signature_class(node: ast.ClassDef) -> ClassSignature:
|
|
135
|
+
methods = [n for n in node.body
|
|
136
|
+
if isinstance(n, ast.FunctionDef | ast.AsyncFunctionDef)]
|
|
137
|
+
has_state = any(_has_self_assign(m) for m in methods
|
|
138
|
+
if isinstance(m, ast.FunctionDef)
|
|
139
|
+
and m.name == "__init__")
|
|
140
|
+
body_hash = _hash(_normalize_ast(node))
|
|
141
|
+
return ClassSignature(
|
|
142
|
+
name=node.name,
|
|
143
|
+
method_count=len(methods),
|
|
144
|
+
has_state=has_state,
|
|
145
|
+
body_hash=body_hash,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _top_level_imports(tree: ast.Module) -> tuple[str, ...]:
|
|
150
|
+
out: set[str] = set()
|
|
151
|
+
for node in tree.body:
|
|
152
|
+
if isinstance(node, ast.Import):
|
|
153
|
+
out.update(a.name for a in node.names)
|
|
154
|
+
elif isinstance(node, ast.ImportFrom):
|
|
155
|
+
if node.module:
|
|
156
|
+
out.add(node.module)
|
|
157
|
+
return tuple(sorted(out))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def signature_of(source: str) -> ModuleSignature:
|
|
161
|
+
"""Pure: source -> ModuleSignature with stable shape hashes."""
|
|
162
|
+
try:
|
|
163
|
+
tree = ast.parse(source)
|
|
164
|
+
except SyntaxError:
|
|
165
|
+
return ModuleSignature(parse_ok=False, module_hash=_hash(source))
|
|
166
|
+
|
|
167
|
+
funcs: list[FunctionSignature] = []
|
|
168
|
+
classes: list[ClassSignature] = []
|
|
169
|
+
for node in tree.body:
|
|
170
|
+
if isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef):
|
|
171
|
+
funcs.append(_signature_function(node))
|
|
172
|
+
elif isinstance(node, ast.ClassDef):
|
|
173
|
+
classes.append(_signature_class(node))
|
|
174
|
+
|
|
175
|
+
imports = _top_level_imports(tree)
|
|
176
|
+
# Module hash is order-invariant over functions + classes;
|
|
177
|
+
# sort by body_hash so two files with same content in different
|
|
178
|
+
# order collide.
|
|
179
|
+
fn_hashes = sorted(f.body_hash for f in funcs)
|
|
180
|
+
cls_hashes = sorted(c.body_hash for c in classes)
|
|
181
|
+
blob = "|".join(fn_hashes + cls_hashes + list(imports))
|
|
182
|
+
return ModuleSignature(
|
|
183
|
+
module_hash=_hash(blob),
|
|
184
|
+
functions=tuple(funcs),
|
|
185
|
+
classes=tuple(classes),
|
|
186
|
+
imports=imports,
|
|
187
|
+
parse_ok=True,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def function_overlap(a: ModuleSignature, b: ModuleSignature
|
|
192
|
+
) -> tuple[FunctionSignature, ...]:
|
|
193
|
+
"""Functions present in both modules with identical body_hash.
|
|
194
|
+
Use to spot 'this new module reimplements N functions that
|
|
195
|
+
already exist' before accepting an emit."""
|
|
196
|
+
a_by_hash = {f.body_hash: f for f in a.functions}
|
|
197
|
+
return tuple(f for f in b.functions if f.body_hash in a_by_hash)
|