atomadic-forge 0.3.2__py3-none-any.whl

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.
Files changed (131) hide show
  1. atomadic_forge/__init__.py +12 -0
  2. atomadic_forge/__main__.py +5 -0
  3. atomadic_forge/a0_qk_constants/__init__.py +1 -0
  4. atomadic_forge/a0_qk_constants/agent_plan_schema.py +120 -0
  5. atomadic_forge/a0_qk_constants/commandsmith_types.py +49 -0
  6. atomadic_forge/a0_qk_constants/config_defaults.py +38 -0
  7. atomadic_forge/a0_qk_constants/emergent_types.py +77 -0
  8. atomadic_forge/a0_qk_constants/error_codes.py +296 -0
  9. atomadic_forge/a0_qk_constants/forge_types.py +89 -0
  10. atomadic_forge/a0_qk_constants/gen_language.py +116 -0
  11. atomadic_forge/a0_qk_constants/lang_extensions.py +150 -0
  12. atomadic_forge/a0_qk_constants/policy_schema.py +48 -0
  13. atomadic_forge/a0_qk_constants/receipt_schema.py +311 -0
  14. atomadic_forge/a0_qk_constants/roi_constants.py +96 -0
  15. atomadic_forge/a0_qk_constants/semantic_types.py +61 -0
  16. atomadic_forge/a0_qk_constants/sidecar_schema.py +81 -0
  17. atomadic_forge/a0_qk_constants/synergy_types.py +62 -0
  18. atomadic_forge/a0_qk_constants/tier_names.py +47 -0
  19. atomadic_forge/a1_at_functions/__init__.py +1 -0
  20. atomadic_forge/a1_at_functions/agent_context_pack.py +193 -0
  21. atomadic_forge/a1_at_functions/agent_memory.py +139 -0
  22. atomadic_forge/a1_at_functions/agent_plan_emitter.py +324 -0
  23. atomadic_forge/a1_at_functions/agent_summary.py +277 -0
  24. atomadic_forge/a1_at_functions/body_extractor.py +306 -0
  25. atomadic_forge/a1_at_functions/card_renderer.py +210 -0
  26. atomadic_forge/a1_at_functions/certify_checks.py +445 -0
  27. atomadic_forge/a1_at_functions/chat_context.py +170 -0
  28. atomadic_forge/a1_at_functions/cherry_pick.py +71 -0
  29. atomadic_forge/a1_at_functions/classify_tier.py +115 -0
  30. atomadic_forge/a1_at_functions/commandsmith_discover.py +167 -0
  31. atomadic_forge/a1_at_functions/commandsmith_render.py +267 -0
  32. atomadic_forge/a1_at_functions/compiler_feedback.py +94 -0
  33. atomadic_forge/a1_at_functions/compliance_checker.py +228 -0
  34. atomadic_forge/a1_at_functions/config_io.py +68 -0
  35. atomadic_forge/a1_at_functions/cs1_renderer.py +588 -0
  36. atomadic_forge/a1_at_functions/doc_synthesizer.py +205 -0
  37. atomadic_forge/a1_at_functions/emergent_compose.py +192 -0
  38. atomadic_forge/a1_at_functions/emergent_rank.py +116 -0
  39. atomadic_forge/a1_at_functions/emergent_signature_extract.py +242 -0
  40. atomadic_forge/a1_at_functions/emergent_synthesize.py +88 -0
  41. atomadic_forge/a1_at_functions/enforce_planner.py +208 -0
  42. atomadic_forge/a1_at_functions/error_hints.py +105 -0
  43. atomadic_forge/a1_at_functions/evolution_log.py +94 -0
  44. atomadic_forge/a1_at_functions/forge_feedback.py +433 -0
  45. atomadic_forge/a1_at_functions/generation_quality.py +322 -0
  46. atomadic_forge/a1_at_functions/import_repair.py +211 -0
  47. atomadic_forge/a1_at_functions/import_smoke.py +102 -0
  48. atomadic_forge/a1_at_functions/js_parser.py +539 -0
  49. atomadic_forge/a1_at_functions/lineage_chain.py +144 -0
  50. atomadic_forge/a1_at_functions/lineage_reader.py +107 -0
  51. atomadic_forge/a1_at_functions/llm_client.py +554 -0
  52. atomadic_forge/a1_at_functions/local_signer.py +134 -0
  53. atomadic_forge/a1_at_functions/lsp_protocol.py +379 -0
  54. atomadic_forge/a1_at_functions/manifest_diff.py +314 -0
  55. atomadic_forge/a1_at_functions/mcp_protocol.py +1066 -0
  56. atomadic_forge/a1_at_functions/patch_scorer.py +267 -0
  57. atomadic_forge/a1_at_functions/plan_adapter.py +75 -0
  58. atomadic_forge/a1_at_functions/policy_loader.py +107 -0
  59. atomadic_forge/a1_at_functions/preflight_change.py +227 -0
  60. atomadic_forge/a1_at_functions/progress_reporter.py +81 -0
  61. atomadic_forge/a1_at_functions/provider_detect.py +157 -0
  62. atomadic_forge/a1_at_functions/provider_resolver.py +48 -0
  63. atomadic_forge/a1_at_functions/receipt_emitter.py +291 -0
  64. atomadic_forge/a1_at_functions/recipes.py +186 -0
  65. atomadic_forge/a1_at_functions/repo_explainer.py +124 -0
  66. atomadic_forge/a1_at_functions/roi_calculator.py +265 -0
  67. atomadic_forge/a1_at_functions/rollback_planner.py +147 -0
  68. atomadic_forge/a1_at_functions/sbom_emitter.py +155 -0
  69. atomadic_forge/a1_at_functions/scaffold_js.py +55 -0
  70. atomadic_forge/a1_at_functions/scaffold_pyproject.py +62 -0
  71. atomadic_forge/a1_at_functions/scaffold_starter.py +94 -0
  72. atomadic_forge/a1_at_functions/scout_walk.py +309 -0
  73. atomadic_forge/a1_at_functions/sidecar_parser.py +161 -0
  74. atomadic_forge/a1_at_functions/sidecar_validator.py +202 -0
  75. atomadic_forge/a1_at_functions/stub_detector.py +158 -0
  76. atomadic_forge/a1_at_functions/synergy_detect.py +166 -0
  77. atomadic_forge/a1_at_functions/synergy_render.py +252 -0
  78. atomadic_forge/a1_at_functions/synergy_surface_extract.py +163 -0
  79. atomadic_forge/a1_at_functions/test_runner.py +196 -0
  80. atomadic_forge/a1_at_functions/test_selector.py +122 -0
  81. atomadic_forge/a1_at_functions/tier_init_rebuild.py +122 -0
  82. atomadic_forge/a1_at_functions/tool_composer.py +130 -0
  83. atomadic_forge/a1_at_functions/transcript_log.py +70 -0
  84. atomadic_forge/a1_at_functions/wire_check.py +260 -0
  85. atomadic_forge/a2_mo_composites/__init__.py +1 -0
  86. atomadic_forge/a2_mo_composites/lineage_chain_store.py +122 -0
  87. atomadic_forge/a2_mo_composites/manifest_store.py +46 -0
  88. atomadic_forge/a2_mo_composites/plan_store.py +164 -0
  89. atomadic_forge/a2_mo_composites/receipt_signer.py +231 -0
  90. atomadic_forge/a3_og_features/__init__.py +1 -0
  91. atomadic_forge/a3_og_features/commandsmith_feature.py +267 -0
  92. atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/__init__.py +3 -0
  93. atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/a0_qk_constants/__init__.py +4 -0
  94. atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/a1_at_functions/__init__.py +14 -0
  95. atomadic_forge/a3_og_features/demo_packages/mixed_py_js/tests/conftest.py +10 -0
  96. atomadic_forge/a3_og_features/demo_packages/mixed_py_js/tests/test_mixed.py +18 -0
  97. atomadic_forge/a3_og_features/demo_runner.py +502 -0
  98. atomadic_forge/a3_og_features/emergent_feature.py +95 -0
  99. atomadic_forge/a3_og_features/emergent_pipeline_integration.py +154 -0
  100. atomadic_forge/a3_og_features/forge_enforce.py +107 -0
  101. atomadic_forge/a3_og_features/forge_evolve.py +176 -0
  102. atomadic_forge/a3_og_features/forge_loop.py +528 -0
  103. atomadic_forge/a3_og_features/forge_pipeline.py +295 -0
  104. atomadic_forge/a3_og_features/forge_plan_apply.py +222 -0
  105. atomadic_forge/a3_og_features/lsp_server.py +98 -0
  106. atomadic_forge/a3_og_features/mcp_server.py +160 -0
  107. atomadic_forge/a3_og_features/setup_wizard.py +337 -0
  108. atomadic_forge/a3_og_features/synergy_feature.py +65 -0
  109. atomadic_forge/a4_sy_orchestration/__init__.py +1 -0
  110. atomadic_forge/a4_sy_orchestration/cli.py +1284 -0
  111. atomadic_forge/commands/__init__.py +1 -0
  112. atomadic_forge/commands/_registry.py +36 -0
  113. atomadic_forge/commands/audit.py +142 -0
  114. atomadic_forge/commands/chat.py +133 -0
  115. atomadic_forge/commands/commandsmith.py +178 -0
  116. atomadic_forge/commands/config_cmd.py +145 -0
  117. atomadic_forge/commands/demo.py +142 -0
  118. atomadic_forge/commands/emergent.py +124 -0
  119. atomadic_forge/commands/emergent_then_synergy.py +70 -0
  120. atomadic_forge/commands/evolve.py +122 -0
  121. atomadic_forge/commands/evolve_then_iterate.py +70 -0
  122. atomadic_forge/commands/feature_then_emergent.py +111 -0
  123. atomadic_forge/commands/iterate.py +140 -0
  124. atomadic_forge/commands/synergy.py +96 -0
  125. atomadic_forge/commands/synergy_then_emergent.py +70 -0
  126. atomadic_forge-0.3.2.dist-info/METADATA +471 -0
  127. atomadic_forge-0.3.2.dist-info/RECORD +131 -0
  128. atomadic_forge-0.3.2.dist-info/WHEEL +5 -0
  129. atomadic_forge-0.3.2.dist-info/entry_points.txt +3 -0
  130. atomadic_forge-0.3.2.dist-info/licenses/LICENSE +15 -0
  131. atomadic_forge-0.3.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,12 @@
1
+ """Atomadic Forge — absorb, enforce, emerge.
2
+
3
+ A monadic-architecture engine for absorbing Python or JavaScript /
4
+ TypeScript repositories into a verified 5-tier layout, enforcing layered
5
+ import discipline, and surfacing emergent compositions across the
6
+ resulting catalog.
7
+
8
+ Public version surface only — every symbol lives in its tier package and
9
+ is re-exported lazily by the CLI.
10
+ """
11
+
12
+ __version__ = "0.3.2"
@@ -0,0 +1,5 @@
1
+ """Entry for ``python -m atomadic_forge``."""
2
+ from .a4_sy_orchestration.cli import main
3
+
4
+ if __name__ == "__main__":
5
+ main()
@@ -0,0 +1 @@
1
+ """Tier a0 — constants, types, schema names. Zero logic."""
@@ -0,0 +1,120 @@
1
+ """Tier a0 — agent-plan/v1 wire-format schema.
2
+
3
+ Direct response to Codex's follow-up directive after using earlier
4
+ Forge releases:
5
+
6
+ > 'Make Forge output \"next best action cards.\" The active agent
7
+ > then does what agents are good at: inspect files, edit
8
+ > carefully, run tests, decide whether the suggestion was
9
+ > actually good. Forge stays the architectural conscience and
10
+ > candidate generator.'
11
+
12
+ Where ``agent_summary/v1`` (prior commit) was a compact dashboard,
13
+ ``agent_plan/v1`` is the operational manifest the agent actually
14
+ executes against. Each ``AgentActionCard`` is a bounded, named,
15
+ applyable proposal: id, why, write_scope, validation commands,
16
+ risk, and whether Forge itself can apply it via existing verbs.
17
+
18
+ a0 invariant: this file holds only TypedDicts + constant strings.
19
+ No logic. Imports limited to ``__future__`` and ``typing``.
20
+ """
21
+ from __future__ import annotations
22
+
23
+ from typing import Literal, TypedDict
24
+
25
+ SCHEMA_VERSION_AGENT_PLAN_V1 = "atomadic-forge.agent_plan/v1"
26
+ SCHEMA_VERSION_AGENT_ACTION_V1 = "atomadic-forge.agent_action/v1"
27
+
28
+
29
+ # Card kinds — pinned for downstream filtering. Adding a new kind is
30
+ # additive; renaming or removing one is a major schema bump.
31
+ ACTION_KINDS: tuple[str, ...] = (
32
+ "operational", # CI / changelog / README / tests-present gaps
33
+ "architectural", # wire violations, F-coded
34
+ "composition", # emergent — symbol chains
35
+ "synthesis", # synergy — feature-pair adapters
36
+ "release", # final-mile (sign Receipt, gate certify ≥ N)
37
+ )
38
+
39
+ # Plan modes — the agent declares what it wants to do.
40
+ PLAN_MODES: tuple[str, ...] = (
41
+ "improve", # operate on the existing repo in-place
42
+ "absorb", # take a flat repo and emit a new tier-organized package
43
+ )
44
+
45
+ # Risk levels — drives the agent's confirmation policy.
46
+ RISK_LEVELS: tuple[str, ...] = ("low", "medium", "high")
47
+
48
+
49
+ class AgentActionCard(TypedDict, total=False):
50
+ """One bounded proposal for the agent to inspect, edit, apply.
51
+
52
+ Required fields:
53
+ schema_version — atomadic-forge.agent_action/v1
54
+ id — stable slug (e.g. 'fix-wire-F0042-helper-py');
55
+ the agent uses this in `auto step <id>`
56
+ kind — one of ACTION_KINDS
57
+ title — one-line human label
58
+ why — 1-3 sentence justification (concrete, no fluff)
59
+ risk — one of RISK_LEVELS
60
+ applyable — true when Forge has a verb that can execute this
61
+ unattended; false means hand to the agent
62
+
63
+ Optional but strongly encouraged:
64
+ write_scope — list of file globs the action will touch
65
+ commands — list of validation commands the agent should
66
+ run AFTER applying (pytest, forge wire, etc.)
67
+ related_fcodes — F-codes this action resolves
68
+ next_command — the single shell command to execute the action
69
+ sample_path — representative file (when applicable)
70
+ score_delta_estimate — expected certify score increase (heuristic)
71
+ """
72
+ schema_version: str
73
+ id: str
74
+ kind: str
75
+ title: str
76
+ why: str
77
+ risk: str
78
+ applyable: bool
79
+ write_scope: list[str]
80
+ commands: list[str]
81
+ related_fcodes: list[str]
82
+ next_command: str
83
+ sample_path: str | None
84
+ score_delta_estimate: int
85
+
86
+
87
+ class AgentPlan(TypedDict, total=False):
88
+ """The plan envelope: ordered top-N actions + meta.
89
+
90
+ Required:
91
+ schema_version, generated_at_utc, verdict, goal, mode,
92
+ top_actions, project_root.
93
+ """
94
+ schema_version: str
95
+ generated_at_utc: str
96
+ verdict: Literal["PASS", "FAIL", "REFINE", "QUARANTINE"]
97
+ goal: str
98
+ mode: str # one of PLAN_MODES
99
+ project_root: str
100
+ top_actions: list[AgentActionCard]
101
+ action_count: int # full count regardless of top_n cap
102
+ applyable_count: int
103
+ next_command: str
104
+ # Codex feedback: surface the certify score on the plan envelope
105
+ # so MCP _summary digests don't have to guess. None when no
106
+ # certify_report fed the plan.
107
+ score: float | None
108
+ # Provenance — what scans fed into this plan.
109
+ sources: dict[str, str] # {source_name: schema_version}
110
+
111
+
112
+ REQUIRED_PLAN_FIELDS: tuple[str, ...] = (
113
+ "schema_version",
114
+ "generated_at_utc",
115
+ "verdict",
116
+ "goal",
117
+ "mode",
118
+ "project_root",
119
+ "top_actions",
120
+ )
@@ -0,0 +1,49 @@
1
+ """Tier a0 — types for the Commandsmith CLI command-management phase.
2
+
3
+ Commandsmith discovers Python modules that expose CLI commands (existing
4
+ or freshly assimilated) and emits:
5
+
6
+ 1. A registry manifest (JSON) describing every known command.
7
+ 2. An auto-wired Python registry module that registers all commands on a
8
+ Typer app (replacing the hand-maintained ``register_commands`` shim).
9
+ 3. Per-command Markdown docs and an INDEX.
10
+
11
+ These TypedDicts are the wire format for that pipeline. No logic here —
12
+ only data shapes.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Literal, TypedDict
18
+
19
+ CommandSurface = Literal["typer_app", "register_fn", "wrapped_class"]
20
+
21
+
22
+ class CommandSignatureCard(TypedDict):
23
+ """One sub-command's parameter signature as a small dict-of-strings."""
24
+
25
+ name: str
26
+ parameters: list[str] # e.g. ["repo: Path", "--tier (a0|a1|...): str"]
27
+ return_type: str # e.g. "ReconReport"
28
+ docstring: str
29
+
30
+
31
+ class RegisteredCommandCard(TypedDict):
32
+ """Static description of one CLI command discovered by Commandsmith."""
33
+
34
+ name: str # CLI verb (e.g. "cherry-pick")
35
+ module: str # Python module path
36
+ surface: CommandSurface # how the module exposes commands
37
+ help_text: str # one-line description
38
+ hidden: bool
39
+ sub_commands: list[CommandSignatureCard]
40
+ source_root: str # which root contributed it ("atomadic_forge_seed", "atomadic_v2", ...)
41
+
42
+
43
+ class RegistryManifestCard(TypedDict):
44
+ """Persistent registry of every command Commandsmith has wired."""
45
+
46
+ schema_version: str # "atomadic-forge.commandsmith.registry/v1"
47
+ generated_at_utc: str
48
+ commands: list[RegisteredCommandCard]
49
+ smoke_results: dict[str, bool] # cmd-name -> --help exit 0?
@@ -0,0 +1,38 @@
1
+ """Tier a0 — config defaults and file-location constants for Atomadic Forge."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TypedDict
6
+
7
+
8
+ class ForgeConfig(TypedDict, total=False):
9
+ provider: str
10
+ ollama_url: str
11
+ ollama_model: str
12
+ gemini_key: str | None
13
+ anthropic_key: str | None
14
+ openai_key: str | None
15
+ default_target_score: float
16
+ auto_apply: bool
17
+ output_dir: str
18
+ sources_dir: str
19
+ package_prefix: str
20
+
21
+
22
+ DEFAULT_CONFIG: ForgeConfig = {
23
+ "provider": "auto",
24
+ "ollama_url": "http://localhost:11434",
25
+ "ollama_model": "mistral:7b-instruct",
26
+ "gemini_key": None,
27
+ "anthropic_key": None,
28
+ "openai_key": None,
29
+ "default_target_score": 75.0,
30
+ "auto_apply": False,
31
+ "output_dir": "./forged",
32
+ "sources_dir": "./sources",
33
+ "package_prefix": "forged",
34
+ }
35
+
36
+ CONFIG_FILE_NAME = "config.json"
37
+ GLOBAL_CONFIG_DIR = "~/.atomadic-forge"
38
+ LOCAL_CONFIG_DIR = ".atomadic-forge"
@@ -0,0 +1,77 @@
1
+ """Tier a0 — types for the Emergent Behaviors Scan.
2
+
3
+ Emergent Scan reads the existing tier-organized catalog and synthesizes
4
+ *new* features by composing components that nobody wired together yet.
5
+
6
+ Pipeline:
7
+
8
+ symbols → signatures (a1) → composition graph (a1) → candidates (a1)
9
+
10
+ optional synthesis (a1) → a3 feature
11
+
12
+ These TypedDicts are the wire format between those steps. No logic here.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Literal, TypedDict
18
+
19
+ Tier = Literal[
20
+ "a0_qk_constants",
21
+ "a1_at_functions",
22
+ "a2_mo_composites",
23
+ "a3_og_features",
24
+ "a4_sy_orchestration",
25
+ ]
26
+
27
+
28
+ class SymbolSignatureCard(TypedDict):
29
+ """One callable's typed surface, tier, and domain tag."""
30
+
31
+ name: str # bare function/class name (e.g. ``infer_tier``)
32
+ qualname: str # module-qualified (e.g. ``atomadic_forge.a1_at_functions.foo.bar``)
33
+ module: str # importable module path
34
+ tier: str # one of Tier
35
+ domain: str # heuristic group (``kg``, ``swarm``, ``cherry`` …)
36
+ inputs: list[tuple[str, str]] # [(param_name, type_annotation_text), …]
37
+ output: str # return annotation text (or "Any")
38
+ is_pure: bool # heuristic: no obvious I/O / mutable state
39
+ docstring: str # one-line
40
+
41
+
42
+ class CompositionChain(TypedDict):
43
+ """An ordered chain of symbols where each output feeds the next."""
44
+
45
+ chain: list[str] # qualnames in execution order
46
+ bridges: list[str] # type-text describing each consumed-output edge
47
+ tiers: list[str] # tier of each step
48
+ domains: list[str] # domain tag of each step
49
+ crosses_domains: int # number of distinct domains involved
50
+ crosses_tiers: int # number of distinct tiers involved
51
+ final_output_type: str
52
+ pure: bool # all steps inferred pure
53
+
54
+
55
+ class EmergentCandidateCard(TypedDict):
56
+ """A scored synthesis candidate ready for review or materialisation."""
57
+
58
+ candidate_id: str # short hash identifier
59
+ name: str # heuristic kebab-case name
60
+ summary: str # 1-line description
61
+ chain: CompositionChain
62
+ score: float # 0..100
63
+ score_breakdown: dict[str, float]
64
+ suggested_tier: str
65
+ novelty_signals: list[str] # why this is "emergent"
66
+
67
+
68
+ class EmergentScanReport(TypedDict):
69
+ """Top-level wire format produced by ``atomadic-forge emergent scan --json``."""
70
+
71
+ schema_version: str # "atomadic-forge.emergent.scan/v1"
72
+ generated_at_utc: str
73
+ catalog_size: int
74
+ chain_count_considered: int
75
+ candidates: list[EmergentCandidateCard]
76
+ domain_inventory: dict[str, int]
77
+ tier_inventory: dict[str, int]
@@ -0,0 +1,296 @@
1
+ """Tier a0 — F-code registry: stable, citeable error codes.
2
+
3
+ Golden Path Lane A W5 deliverable. Every error Forge surfaces (wire
4
+ violations, certify failures, scout misclassifications, …) carries an
5
+ ``F-code`` of the form ``F0042``: a 4-digit, zero-padded, fixed
6
+ integer that is **never reused or renumbered**. Once an F-code is
7
+ assigned to a class of error, that mapping is part of the schema
8
+ contract — the same way ``schema_version`` is.
9
+
10
+ Why F-codes:
11
+ * Linker-style citeability — a CI report can say "F0042 occurred 3
12
+ times" without leaking the underlying message string, which
13
+ reviewers and tooling can grep / pivot / dashboards on.
14
+ * Machine-applicable ``--fix`` — Lane A W6's ``forge enforce``
15
+ routes by F-code: F0042 has a known mechanical fix (move the
16
+ importing file up to the higher tier), F0050 (no README) has
17
+ another, etc.
18
+ * Forward-compat — adding an F-code is additive. Removing or
19
+ renumbering an F-code is a major schema bump.
20
+ * Internationalization-ready — the message string can change per
21
+ locale; the F-code stays.
22
+
23
+ Registry convention:
24
+ F0001..F0009 — scout / classification (info)
25
+ F0010..F0019 — cherry-pick (warn)
26
+ F0040..F0049 — wire / upward-import violations ← W5 seeds these
27
+ F0050..F0059 — certify axis failures ← W5 seeds these
28
+ F0060..F0069 — stub detection
29
+ F0070..F0079 — import-repair
30
+ F0080..F0089 — assimilate conflicts
31
+ F0090..F0099 — receipt / signing
32
+
33
+ Pure data: this module imports only ``__future__`` and ``typing``.
34
+ The lookup helpers are pure.
35
+ """
36
+ from __future__ import annotations
37
+
38
+ from typing import Literal, TypedDict
39
+
40
+ FCodeSeverity = Literal["info", "warn", "error"]
41
+
42
+
43
+ class FCode(TypedDict):
44
+ """One entry in the F-code registry.
45
+
46
+ Fields:
47
+ code — 'F' + 4 zero-padded digits (e.g. 'F0042')
48
+ name — short kebab-case slug (stable; never renamed)
49
+ severity — info | warn | error
50
+ title — one-line human label (may be localized)
51
+ hint_key — name of an a1.error_hints template; '' for none
52
+ auto_fixable — when True, Lane A W6 'forge enforce' has a
53
+ mechanical fix path
54
+ doc_anchor — section anchor in docs/F_CODES.md (forthcoming)
55
+ """
56
+ code: str
57
+ name: str
58
+ severity: FCodeSeverity
59
+ title: str
60
+ hint_key: str
61
+ auto_fixable: bool
62
+ doc_anchor: str
63
+
64
+
65
+ F_CODE_REGISTRY: dict[str, FCode] = {
66
+ # ---- Wire / upward-import violations (F0040-F0049) ----
67
+ "F0040": FCode(
68
+ code="F0040",
69
+ name="a0-cannot-import-anything",
70
+ severity="error",
71
+ title="a0_qk_constants must not import any other tier (a0 holds zero logic).",
72
+ hint_key="wire_fail_with_violations",
73
+ auto_fixable=False,
74
+ doc_anchor="f0040-a0-cannot-import-anything",
75
+ ),
76
+ "F0041": FCode(
77
+ code="F0041",
78
+ name="a1-imports-a2",
79
+ severity="error",
80
+ title="a1_at_functions imports from a2_mo_composites (upward).",
81
+ hint_key="wire_fail_with_violations",
82
+ auto_fixable=True,
83
+ doc_anchor="f0041-a1-imports-a2",
84
+ ),
85
+ "F0042": FCode(
86
+ code="F0042",
87
+ name="a1-imports-a3",
88
+ severity="error",
89
+ title="a1_at_functions imports from a3_og_features (upward).",
90
+ hint_key="wire_fail_with_violations",
91
+ auto_fixable=True,
92
+ doc_anchor="f0042-a1-imports-a3",
93
+ ),
94
+ "F0043": FCode(
95
+ code="F0043",
96
+ name="a1-imports-a4",
97
+ severity="error",
98
+ title="a1_at_functions imports from a4_sy_orchestration (upward).",
99
+ hint_key="wire_fail_with_violations",
100
+ auto_fixable=True,
101
+ doc_anchor="f0043-a1-imports-a4",
102
+ ),
103
+ "F0044": FCode(
104
+ code="F0044",
105
+ name="a2-imports-a3",
106
+ severity="error",
107
+ title="a2_mo_composites imports from a3_og_features (upward).",
108
+ hint_key="wire_fail_with_violations",
109
+ auto_fixable=True,
110
+ doc_anchor="f0044-a2-imports-a3",
111
+ ),
112
+ "F0045": FCode(
113
+ code="F0045",
114
+ name="a2-imports-a4",
115
+ severity="error",
116
+ title="a2_mo_composites imports from a4_sy_orchestration (upward).",
117
+ hint_key="wire_fail_with_violations",
118
+ auto_fixable=True,
119
+ doc_anchor="f0045-a2-imports-a4",
120
+ ),
121
+ "F0046": FCode(
122
+ code="F0046",
123
+ name="a3-imports-a4",
124
+ severity="error",
125
+ title="a3_og_features imports from a4_sy_orchestration (upward).",
126
+ hint_key="wire_fail_with_violations",
127
+ auto_fixable=True,
128
+ doc_anchor="f0046-a3-imports-a4",
129
+ ),
130
+ "F0049": FCode(
131
+ code="F0049",
132
+ name="unknown-tier-violation",
133
+ severity="error",
134
+ title="Upward import detected but tier shape is non-canonical; review manually.",
135
+ hint_key="wire_fail_with_violations",
136
+ auto_fixable=False,
137
+ doc_anchor="f0049-unknown-tier-violation",
138
+ ),
139
+ # ---- Certify axis failures (F0050-F0059) ----
140
+ "F0050": FCode(
141
+ code="F0050",
142
+ name="documentation-missing",
143
+ severity="error",
144
+ title="Documentation axis FAIL: no README.md and < 2 docs/*.md files.",
145
+ hint_key="certify_below_threshold",
146
+ auto_fixable=True,
147
+ doc_anchor="f0050-documentation-missing",
148
+ ),
149
+ "F0051": FCode(
150
+ code="F0051",
151
+ name="tests-missing",
152
+ severity="error",
153
+ title="Tests axis FAIL: no tests/test_*.py or tests/*_test.py present.",
154
+ hint_key="certify_below_threshold",
155
+ auto_fixable=False,
156
+ doc_anchor="f0051-tests-missing",
157
+ ),
158
+ "F0052": FCode(
159
+ code="F0052",
160
+ name="tier-layout-incomplete",
161
+ severity="error",
162
+ title="Tier-layout axis FAIL: fewer than 3 tier directories present.",
163
+ hint_key="no_tier_dirs",
164
+ auto_fixable=False,
165
+ doc_anchor="f0052-tier-layout-incomplete",
166
+ ),
167
+ "F0053": FCode(
168
+ code="F0053",
169
+ name="upward-imports-present",
170
+ severity="error",
171
+ title="Import-discipline axis FAIL: at least one wire violation present.",
172
+ hint_key="wire_fail_with_violations",
173
+ auto_fixable=True,
174
+ doc_anchor="f0053-upward-imports-present",
175
+ ),
176
+ # ---- Sidecar drift (F0100-F0109; Lane D W8/W11) ----
177
+ "F0100": FCode(
178
+ code="F0100",
179
+ name="sidecar-source-unparseable",
180
+ severity="error",
181
+ title="Sidecar present but the source file did not parse.",
182
+ hint_key="",
183
+ auto_fixable=False,
184
+ doc_anchor="f0100-sidecar-source-unparseable",
185
+ ),
186
+ "F0101": FCode(
187
+ code="F0101",
188
+ name="sidecar-declares-missing-symbol",
189
+ severity="error",
190
+ title="Sidecar declares a symbol the source file doesn't have.",
191
+ hint_key="",
192
+ auto_fixable=False,
193
+ doc_anchor="f0101-sidecar-declares-missing-symbol",
194
+ ),
195
+ "F0102": FCode(
196
+ code="F0102",
197
+ name="sidecar-coverage-incomplete",
198
+ severity="warn",
199
+ title="Source has a public symbol the sidecar didn't declare (advisory).",
200
+ hint_key="",
201
+ auto_fixable=False,
202
+ doc_anchor="f0102-sidecar-coverage-incomplete",
203
+ ),
204
+ "F0103": FCode(
205
+ code="F0103",
206
+ name="sidecar-pure-violates-purity",
207
+ severity="error",
208
+ title="Sidecar declares Pure but source uses I/O / network / non-deterministic input.",
209
+ hint_key="",
210
+ auto_fixable=False,
211
+ doc_anchor="f0103-sidecar-pure-violates-purity",
212
+ ),
213
+ "F0106": FCode(
214
+ code="F0106",
215
+ name="sidecar-tier-mismatch",
216
+ severity="warn",
217
+ title="Sidecar declares a tier different from the source file's actual path tier.",
218
+ hint_key="",
219
+ auto_fixable=False,
220
+ doc_anchor="f0106-sidecar-tier-mismatch",
221
+ ),
222
+ }
223
+
224
+
225
+ # Sidecar S-code → F-code mapping. Exposed so the validator (a1) can
226
+ # promote local drift labels into the global F-code namespace
227
+ # without re-importing this whole registry.
228
+ SIDECAR_S_TO_F: dict[str, str] = {
229
+ "S0000": "F0100",
230
+ "S0001": "F0101",
231
+ "S0002": "F0102",
232
+ "S0003": "F0103",
233
+ "S0006": "F0106",
234
+ }
235
+ """Canonical F-code registry. Adding a new code is additive; renaming
236
+ or renumbering is a major schema bump (and requires updating every
237
+ test and consumer). See the H1 pre-audit smoke for the drift sentinel.
238
+ """
239
+
240
+
241
+ _TIER_PAIR_TO_FCODE: dict[tuple[str, str], str] = {
242
+ # (from_tier, to_tier) → F-code. a0 is special-cased below.
243
+ ("a1_at_functions", "a2_mo_composites"): "F0041",
244
+ ("a1_at_functions", "a3_og_features"): "F0042",
245
+ ("a1_at_functions", "a4_sy_orchestration"): "F0043",
246
+ ("a2_mo_composites", "a3_og_features"): "F0044",
247
+ ("a2_mo_composites", "a4_sy_orchestration"): "F0045",
248
+ ("a3_og_features", "a4_sy_orchestration"): "F0046",
249
+ }
250
+
251
+
252
+ def fcode_for_tier_violation(from_tier: str, to_tier: str) -> str:
253
+ """Map a (from, to) tier pair to its registered F-code.
254
+
255
+ Returns:
256
+ 'F0040' when from_tier is a0 (a0 may not import anything)
257
+ one of F0041..F0046 for the canonical upward-import pairs
258
+ 'F0049' for any other shape (non-canonical tier, mixed-language)
259
+
260
+ Pure: O(1) lookup.
261
+ """
262
+ if from_tier == "a0_qk_constants":
263
+ return "F0040"
264
+ return _TIER_PAIR_TO_FCODE.get((from_tier, to_tier), "F0049")
265
+
266
+
267
+ def fcode_for_certify_axis(axis: str) -> str:
268
+ """Map a certify axis name to its F-code.
269
+
270
+ Recognised axis names match the CertifyResult booleans:
271
+ 'documentation_complete' → F0050
272
+ 'tests_present' → F0051
273
+ 'tier_layout_present' → F0052
274
+ 'no_upward_imports' → F0053
275
+ Unknown axes return '' (no F-code assigned).
276
+ """
277
+ return {
278
+ "documentation_complete": "F0050",
279
+ "tests_present": "F0051",
280
+ "tier_layout_present": "F0052",
281
+ "no_upward_imports": "F0053",
282
+ }.get(axis, "")
283
+
284
+
285
+ def get_fcode(code: str) -> FCode | None:
286
+ """Return the registry entry for ``code`` or None if unregistered."""
287
+ return F_CODE_REGISTRY.get(code)
288
+
289
+
290
+ def all_auto_fixable_fcodes() -> tuple[str, ...]:
291
+ """Return the codes Lane A W6 'forge enforce' has a path for.
292
+
293
+ Sorted ascending so consumers get a stable iteration order.
294
+ """
295
+ return tuple(sorted(c for c, e in F_CODE_REGISTRY.items()
296
+ if e["auto_fixable"]))
@@ -0,0 +1,89 @@
1
+ """Tier a0 — Forge-specific report shapes.
2
+
3
+ Wire formats for scout / cherry-pick / wire / certify / assimilate output.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Literal, TypedDict
9
+
10
+ SymbolKind = Literal["function", "class", "method"]
11
+
12
+
13
+ class SymbolRecord(TypedDict):
14
+ """One callable discovered in a target repo."""
15
+
16
+ name: str
17
+ qualname: str # e.g. "Counter.incr"
18
+ kind: SymbolKind
19
+ file: str # repo-relative path
20
+ lineno: int
21
+ tier_guess: str # one of TIER_NAMES
22
+ effects: list[str] # ["pure"], ["io"], etc.
23
+ complexity: int # ast.dump length (cheap proxy)
24
+ has_self_assign: bool
25
+
26
+
27
+ class ScoutReport(TypedDict):
28
+ schema_version: str # "atomadic-forge.scout/v1"
29
+ repo: str
30
+ file_count: int
31
+ python_file_count: int
32
+ symbol_count: int
33
+ tier_distribution: dict[str, int]
34
+ effect_distribution: dict[str, int]
35
+ symbols: list[SymbolRecord]
36
+ recommendations: list[str]
37
+
38
+
39
+ class CherryPickItem(TypedDict):
40
+ qualname: str
41
+ target_tier: str
42
+ confidence: float
43
+ reasons: list[str]
44
+
45
+
46
+ class CherryPickManifest(TypedDict):
47
+ schema_version: str # "atomadic-forge.cherry/v1"
48
+ source_repo: str
49
+ items: list[CherryPickItem]
50
+
51
+
52
+ class WireViolation(TypedDict):
53
+ file: str
54
+ from_tier: str
55
+ to_tier: str
56
+ imported: str
57
+ proposed_fix: str # rewritten import line, or "" if no fix found
58
+
59
+
60
+ class WireReport(TypedDict):
61
+ schema_version: str # "atomadic-forge.wire/v1"
62
+ source_dir: str
63
+ violation_count: int
64
+ auto_fixable: int
65
+ violations: list[WireViolation]
66
+ verdict: Literal["PASS", "FAIL"]
67
+
68
+
69
+ class CertifyResult(TypedDict):
70
+ schema_version: str # "atomadic-forge.certify/v1"
71
+ project: str
72
+ timestamp: str
73
+ documentation_complete: bool
74
+ tests_present: bool
75
+ tier_layout_present: bool
76
+ no_upward_imports: bool
77
+ score: float # 0..100
78
+ issues: list[str]
79
+ recommendations: list[str]
80
+
81
+
82
+ class AssimilateReport(TypedDict):
83
+ schema_version: str # "atomadic-forge.assimilate/v1"
84
+ target_root: str
85
+ source_repos: list[str]
86
+ components_emitted: int
87
+ tier_distribution: dict[str, int]
88
+ files_repaired: int
89
+ digest: str