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.
- atomadic_forge/__init__.py +12 -0
- atomadic_forge/__main__.py +5 -0
- atomadic_forge/a0_qk_constants/__init__.py +1 -0
- atomadic_forge/a0_qk_constants/agent_plan_schema.py +120 -0
- atomadic_forge/a0_qk_constants/commandsmith_types.py +49 -0
- atomadic_forge/a0_qk_constants/config_defaults.py +38 -0
- atomadic_forge/a0_qk_constants/emergent_types.py +77 -0
- atomadic_forge/a0_qk_constants/error_codes.py +296 -0
- atomadic_forge/a0_qk_constants/forge_types.py +89 -0
- atomadic_forge/a0_qk_constants/gen_language.py +116 -0
- atomadic_forge/a0_qk_constants/lang_extensions.py +150 -0
- atomadic_forge/a0_qk_constants/policy_schema.py +48 -0
- atomadic_forge/a0_qk_constants/receipt_schema.py +311 -0
- atomadic_forge/a0_qk_constants/roi_constants.py +96 -0
- atomadic_forge/a0_qk_constants/semantic_types.py +61 -0
- atomadic_forge/a0_qk_constants/sidecar_schema.py +81 -0
- atomadic_forge/a0_qk_constants/synergy_types.py +62 -0
- atomadic_forge/a0_qk_constants/tier_names.py +47 -0
- atomadic_forge/a1_at_functions/__init__.py +1 -0
- atomadic_forge/a1_at_functions/agent_context_pack.py +193 -0
- atomadic_forge/a1_at_functions/agent_memory.py +139 -0
- atomadic_forge/a1_at_functions/agent_plan_emitter.py +324 -0
- atomadic_forge/a1_at_functions/agent_summary.py +277 -0
- atomadic_forge/a1_at_functions/body_extractor.py +306 -0
- atomadic_forge/a1_at_functions/card_renderer.py +210 -0
- atomadic_forge/a1_at_functions/certify_checks.py +445 -0
- atomadic_forge/a1_at_functions/chat_context.py +170 -0
- atomadic_forge/a1_at_functions/cherry_pick.py +71 -0
- atomadic_forge/a1_at_functions/classify_tier.py +115 -0
- atomadic_forge/a1_at_functions/commandsmith_discover.py +167 -0
- atomadic_forge/a1_at_functions/commandsmith_render.py +267 -0
- atomadic_forge/a1_at_functions/compiler_feedback.py +94 -0
- atomadic_forge/a1_at_functions/compliance_checker.py +228 -0
- atomadic_forge/a1_at_functions/config_io.py +68 -0
- atomadic_forge/a1_at_functions/cs1_renderer.py +588 -0
- atomadic_forge/a1_at_functions/doc_synthesizer.py +205 -0
- atomadic_forge/a1_at_functions/emergent_compose.py +192 -0
- atomadic_forge/a1_at_functions/emergent_rank.py +116 -0
- atomadic_forge/a1_at_functions/emergent_signature_extract.py +242 -0
- atomadic_forge/a1_at_functions/emergent_synthesize.py +88 -0
- atomadic_forge/a1_at_functions/enforce_planner.py +208 -0
- atomadic_forge/a1_at_functions/error_hints.py +105 -0
- atomadic_forge/a1_at_functions/evolution_log.py +94 -0
- atomadic_forge/a1_at_functions/forge_feedback.py +433 -0
- atomadic_forge/a1_at_functions/generation_quality.py +322 -0
- atomadic_forge/a1_at_functions/import_repair.py +211 -0
- atomadic_forge/a1_at_functions/import_smoke.py +102 -0
- atomadic_forge/a1_at_functions/js_parser.py +539 -0
- atomadic_forge/a1_at_functions/lineage_chain.py +144 -0
- atomadic_forge/a1_at_functions/lineage_reader.py +107 -0
- atomadic_forge/a1_at_functions/llm_client.py +554 -0
- atomadic_forge/a1_at_functions/local_signer.py +134 -0
- atomadic_forge/a1_at_functions/lsp_protocol.py +379 -0
- atomadic_forge/a1_at_functions/manifest_diff.py +314 -0
- atomadic_forge/a1_at_functions/mcp_protocol.py +1066 -0
- atomadic_forge/a1_at_functions/patch_scorer.py +267 -0
- atomadic_forge/a1_at_functions/plan_adapter.py +75 -0
- atomadic_forge/a1_at_functions/policy_loader.py +107 -0
- atomadic_forge/a1_at_functions/preflight_change.py +227 -0
- atomadic_forge/a1_at_functions/progress_reporter.py +81 -0
- atomadic_forge/a1_at_functions/provider_detect.py +157 -0
- atomadic_forge/a1_at_functions/provider_resolver.py +48 -0
- atomadic_forge/a1_at_functions/receipt_emitter.py +291 -0
- atomadic_forge/a1_at_functions/recipes.py +186 -0
- atomadic_forge/a1_at_functions/repo_explainer.py +124 -0
- atomadic_forge/a1_at_functions/roi_calculator.py +265 -0
- atomadic_forge/a1_at_functions/rollback_planner.py +147 -0
- atomadic_forge/a1_at_functions/sbom_emitter.py +155 -0
- atomadic_forge/a1_at_functions/scaffold_js.py +55 -0
- atomadic_forge/a1_at_functions/scaffold_pyproject.py +62 -0
- atomadic_forge/a1_at_functions/scaffold_starter.py +94 -0
- atomadic_forge/a1_at_functions/scout_walk.py +309 -0
- atomadic_forge/a1_at_functions/sidecar_parser.py +161 -0
- atomadic_forge/a1_at_functions/sidecar_validator.py +202 -0
- atomadic_forge/a1_at_functions/stub_detector.py +158 -0
- atomadic_forge/a1_at_functions/synergy_detect.py +166 -0
- atomadic_forge/a1_at_functions/synergy_render.py +252 -0
- atomadic_forge/a1_at_functions/synergy_surface_extract.py +163 -0
- atomadic_forge/a1_at_functions/test_runner.py +196 -0
- atomadic_forge/a1_at_functions/test_selector.py +122 -0
- atomadic_forge/a1_at_functions/tier_init_rebuild.py +122 -0
- atomadic_forge/a1_at_functions/tool_composer.py +130 -0
- atomadic_forge/a1_at_functions/transcript_log.py +70 -0
- atomadic_forge/a1_at_functions/wire_check.py +260 -0
- atomadic_forge/a2_mo_composites/__init__.py +1 -0
- atomadic_forge/a2_mo_composites/lineage_chain_store.py +122 -0
- atomadic_forge/a2_mo_composites/manifest_store.py +46 -0
- atomadic_forge/a2_mo_composites/plan_store.py +164 -0
- atomadic_forge/a2_mo_composites/receipt_signer.py +231 -0
- atomadic_forge/a3_og_features/__init__.py +1 -0
- atomadic_forge/a3_og_features/commandsmith_feature.py +267 -0
- atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/__init__.py +3 -0
- atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/a0_qk_constants/__init__.py +4 -0
- atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/a1_at_functions/__init__.py +14 -0
- atomadic_forge/a3_og_features/demo_packages/mixed_py_js/tests/conftest.py +10 -0
- atomadic_forge/a3_og_features/demo_packages/mixed_py_js/tests/test_mixed.py +18 -0
- atomadic_forge/a3_og_features/demo_runner.py +502 -0
- atomadic_forge/a3_og_features/emergent_feature.py +95 -0
- atomadic_forge/a3_og_features/emergent_pipeline_integration.py +154 -0
- atomadic_forge/a3_og_features/forge_enforce.py +107 -0
- atomadic_forge/a3_og_features/forge_evolve.py +176 -0
- atomadic_forge/a3_og_features/forge_loop.py +528 -0
- atomadic_forge/a3_og_features/forge_pipeline.py +295 -0
- atomadic_forge/a3_og_features/forge_plan_apply.py +222 -0
- atomadic_forge/a3_og_features/lsp_server.py +98 -0
- atomadic_forge/a3_og_features/mcp_server.py +160 -0
- atomadic_forge/a3_og_features/setup_wizard.py +337 -0
- atomadic_forge/a3_og_features/synergy_feature.py +65 -0
- atomadic_forge/a4_sy_orchestration/__init__.py +1 -0
- atomadic_forge/a4_sy_orchestration/cli.py +1284 -0
- atomadic_forge/commands/__init__.py +1 -0
- atomadic_forge/commands/_registry.py +36 -0
- atomadic_forge/commands/audit.py +142 -0
- atomadic_forge/commands/chat.py +133 -0
- atomadic_forge/commands/commandsmith.py +178 -0
- atomadic_forge/commands/config_cmd.py +145 -0
- atomadic_forge/commands/demo.py +142 -0
- atomadic_forge/commands/emergent.py +124 -0
- atomadic_forge/commands/emergent_then_synergy.py +70 -0
- atomadic_forge/commands/evolve.py +122 -0
- atomadic_forge/commands/evolve_then_iterate.py +70 -0
- atomadic_forge/commands/feature_then_emergent.py +111 -0
- atomadic_forge/commands/iterate.py +140 -0
- atomadic_forge/commands/synergy.py +96 -0
- atomadic_forge/commands/synergy_then_emergent.py +70 -0
- atomadic_forge-0.3.2.dist-info/METADATA +471 -0
- atomadic_forge-0.3.2.dist-info/RECORD +131 -0
- atomadic_forge-0.3.2.dist-info/WHEEL +5 -0
- atomadic_forge-0.3.2.dist-info/entry_points.txt +3 -0
- atomadic_forge-0.3.2.dist-info/licenses/LICENSE +15 -0
- atomadic_forge-0.3.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Behavioural tests for mixed_pkg — recognised by forge certify."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mixed_pkg.a1_at_functions import greet, length_within
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_greet_returns_prefixed_name() -> None:
|
|
9
|
+
assert greet("Forge") == "hello, Forge"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_greet_handles_empty_name() -> None:
|
|
13
|
+
assert greet("") == "hello, world"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_length_within_caps() -> None:
|
|
17
|
+
assert length_within("ok")
|
|
18
|
+
assert not length_within("x" * 200)
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
"""Tier a3 — preset-driven demo runner.
|
|
2
|
+
|
|
3
|
+
``forge demo`` packages the headline trajectory (intent → evolve → working
|
|
4
|
+
software) into a single command suitable for launch videos and 90-second
|
|
5
|
+
recordings. Two preset families ship with Forge:
|
|
6
|
+
|
|
7
|
+
* ``kind="llm"`` — the original LLM-driven Python presets (`calc`, `kv`,
|
|
8
|
+
`slug`). Each runs a real evolve trajectory with the configured LLM
|
|
9
|
+
provider and produces an importable, pip-installable Python package.
|
|
10
|
+
* ``kind="showcase"`` — static, pre-built source packages copied into
|
|
11
|
+
the output directory and exercised via ``recon → wire → certify``.
|
|
12
|
+
Useful for demonstrating polyglot (JS/TS) capabilities without a paid
|
|
13
|
+
LLM key. Ships with ``js-counter``, ``js-bad-wire``, ``mixed-py-js``.
|
|
14
|
+
|
|
15
|
+
Public API:
|
|
16
|
+
list_presets() → list[DemoPreset]
|
|
17
|
+
run_demo(preset_name, llm, …) → DemoResult (writes DEMO.md artifact)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import datetime as _dt
|
|
23
|
+
import shutil
|
|
24
|
+
import subprocess
|
|
25
|
+
import sys
|
|
26
|
+
from dataclasses import dataclass, field
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
from ..a1_at_functions.certify_checks import certify as _certify_checks
|
|
31
|
+
from ..a1_at_functions.llm_client import LLMClient, resolve_default_client
|
|
32
|
+
from ..a1_at_functions.scout_walk import harvest_repo
|
|
33
|
+
from ..a1_at_functions.wire_check import scan_violations
|
|
34
|
+
from ..a2_mo_composites.manifest_store import ManifestStore
|
|
35
|
+
from .forge_evolve import run_evolve
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class DemoPreset:
|
|
40
|
+
"""A pre-baked intent + verification recipe known to converge on a 7B model."""
|
|
41
|
+
|
|
42
|
+
name: str
|
|
43
|
+
headline: str # one-line pitch for the launch video
|
|
44
|
+
package: str
|
|
45
|
+
intent: str
|
|
46
|
+
rounds: int = 4
|
|
47
|
+
iterations: int = 2
|
|
48
|
+
target_score: float = 90.0
|
|
49
|
+
cli_demo_args: tuple[str, ...] = () # invoked after convergence to prove it runs
|
|
50
|
+
kind: str = "llm" # "llm" — runs evolve; "showcase" — static recon/wire/certify
|
|
51
|
+
source_subdir: str = "" # for kind == "showcase": path under demo_packages/
|
|
52
|
+
certify_package: str | None = None # certify_checks `package` arg, when given
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
_PRESETS: dict[str, DemoPreset] = {
|
|
56
|
+
"calc": DemoPreset(
|
|
57
|
+
name="calc",
|
|
58
|
+
headline="Free local 7B model writes a working calculator in 2 rounds.",
|
|
59
|
+
package="calc",
|
|
60
|
+
intent=(
|
|
61
|
+
"Build a tiny calculator. Required: a1 pure functions add(a,b)->int, "
|
|
62
|
+
"subtract(a,b)->int, multiply(a,b)->int, divide(a,b)->float (raise "
|
|
63
|
+
"ValueError on b==0). a4 cli using argparse takes 'a OP b' (or just "
|
|
64
|
+
"'a b' if simpler) and prints the result. tests/test_calc.py with "
|
|
65
|
+
"concrete assertions: assert add(2,3)==5, assert subtract(10,4)==6, "
|
|
66
|
+
"assert multiply(3,4)==12, assert divide(10,2)==5.0, and "
|
|
67
|
+
"pytest.raises(ValueError) for divide(1,0). Use absolute imports "
|
|
68
|
+
"rooted at 'calc'."
|
|
69
|
+
),
|
|
70
|
+
rounds=4,
|
|
71
|
+
iterations=2,
|
|
72
|
+
target_score=90.0,
|
|
73
|
+
cli_demo_args=("7", "6"),
|
|
74
|
+
),
|
|
75
|
+
"kv": DemoPreset(
|
|
76
|
+
name="kv",
|
|
77
|
+
headline="Tier-organised in-memory key-value store with put/get/delete + tests.",
|
|
78
|
+
package="kv",
|
|
79
|
+
intent=(
|
|
80
|
+
"Build a tiny in-memory KV store. Required: a2 class KvStore with "
|
|
81
|
+
"self.data:dict, put(key,value)/get(key)->Any/delete(key)/keys()->list. "
|
|
82
|
+
"a4 cli using argparse takes 'put KEY VALUE' or 'get KEY' or 'delete KEY' "
|
|
83
|
+
"or 'keys'. tests/test_kv.py with concrete assertions: store.put('a',1); "
|
|
84
|
+
"assert store.get('a')==1; store.delete('a'); assert store.get('a') is None; "
|
|
85
|
+
"store.put('x',1); store.put('y',2); assert sorted(store.keys())==['x','y']. "
|
|
86
|
+
"Use absolute imports rooted at 'kv'."
|
|
87
|
+
),
|
|
88
|
+
rounds=4,
|
|
89
|
+
iterations=2,
|
|
90
|
+
target_score=85.0,
|
|
91
|
+
cli_demo_args=("put", "hello", "world"),
|
|
92
|
+
),
|
|
93
|
+
"slug": DemoPreset(
|
|
94
|
+
name="slug",
|
|
95
|
+
headline="Pure string-slugifier with concrete behavioural tests.",
|
|
96
|
+
package="slug",
|
|
97
|
+
intent=(
|
|
98
|
+
"Build a tiny string slugifier. Required: a1 pure function "
|
|
99
|
+
"slugify(text:str)->str that lowercases, replaces spaces and "
|
|
100
|
+
"punctuation with single dashes, and strips leading/trailing dashes. "
|
|
101
|
+
"a4 cli using argparse takes a single string argument and prints "
|
|
102
|
+
"the slug. tests/test_slug.py with concrete assertions: "
|
|
103
|
+
"assert slugify('Hello World')=='hello-world'; "
|
|
104
|
+
"assert slugify(' Foo!! Bar?? ')=='foo-bar'; "
|
|
105
|
+
"assert slugify('Already-Slugged')=='already-slugged'. "
|
|
106
|
+
"Use absolute imports rooted at 'slug'."
|
|
107
|
+
),
|
|
108
|
+
rounds=4,
|
|
109
|
+
iterations=2,
|
|
110
|
+
target_score=85.0,
|
|
111
|
+
cli_demo_args=("Hello, World!",),
|
|
112
|
+
),
|
|
113
|
+
# ---- Static polyglot showcases (kind="showcase", no LLM needed) -----
|
|
114
|
+
"js-counter": DemoPreset(
|
|
115
|
+
name="js-counter",
|
|
116
|
+
headline="Clean a0..a4 JavaScript package — Worker on top of a stateful Counter.",
|
|
117
|
+
package="js-counter-showcase",
|
|
118
|
+
intent="(static showcase — no LLM trajectory)",
|
|
119
|
+
kind="showcase",
|
|
120
|
+
source_subdir="js_counter",
|
|
121
|
+
target_score=60.0, # 60/100 is the honest ceiling for JS-only today
|
|
122
|
+
),
|
|
123
|
+
"js-bad-wire": DemoPreset(
|
|
124
|
+
name="js-bad-wire",
|
|
125
|
+
headline="Same JS layout, one upward import — wire surfaces the violation.",
|
|
126
|
+
package="js-bad-wire-showcase",
|
|
127
|
+
intent="(static showcase — teaches what wire catches)",
|
|
128
|
+
kind="showcase",
|
|
129
|
+
source_subdir="js_bad_wire",
|
|
130
|
+
target_score=50.0, # wire FAIL deducts 10 from a clean js-only package
|
|
131
|
+
),
|
|
132
|
+
"mixed-py-js": DemoPreset(
|
|
133
|
+
name="mixed-py-js",
|
|
134
|
+
headline="Polyglot package: Python tier + JS tier under the same root.",
|
|
135
|
+
package="mixed_pkg",
|
|
136
|
+
intent="(static showcase — proves one layout works for both languages)",
|
|
137
|
+
kind="showcase",
|
|
138
|
+
source_subdir="mixed_py_js",
|
|
139
|
+
target_score=90.0, # Python tests run; behavioural axis credits 30 points
|
|
140
|
+
certify_package="mixed_pkg",
|
|
141
|
+
),
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass
|
|
146
|
+
class DemoResult:
|
|
147
|
+
preset: str
|
|
148
|
+
package: str
|
|
149
|
+
output_root: str
|
|
150
|
+
rounds_completed: int
|
|
151
|
+
score_trajectory: list[float]
|
|
152
|
+
final_score: float
|
|
153
|
+
converged: bool
|
|
154
|
+
cli_demo_command: list[str] = field(default_factory=list)
|
|
155
|
+
cli_demo_stdout: str = ""
|
|
156
|
+
cli_demo_returncode: int = -1
|
|
157
|
+
duration_s: float = 0.0
|
|
158
|
+
headline: str = ""
|
|
159
|
+
artifact_md_path: str = ""
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def list_presets() -> list[DemoPreset]:
|
|
163
|
+
return list(_PRESETS.values())
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def get_preset(name: str) -> DemoPreset:
|
|
167
|
+
if name not in _PRESETS:
|
|
168
|
+
raise KeyError(f"unknown preset {name!r}; try one of {sorted(_PRESETS)}")
|
|
169
|
+
return _PRESETS[name]
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def run_demo(*, preset_name: str = "calc",
|
|
173
|
+
output: Path | None = None,
|
|
174
|
+
llm: LLMClient | None = None,
|
|
175
|
+
rounds: int | None = None,
|
|
176
|
+
iterations: int | None = None,
|
|
177
|
+
skip_cli_demo: bool = False) -> DemoResult:
|
|
178
|
+
"""Run a preset and emit a DEMO.md artifact.
|
|
179
|
+
|
|
180
|
+
Dispatches by preset ``kind``:
|
|
181
|
+
|
|
182
|
+
* ``"llm"`` (default) — run a real evolve trajectory with the
|
|
183
|
+
configured provider, then invoke the generated CLI.
|
|
184
|
+
* ``"showcase"`` — copy a pre-built source package into ``output``
|
|
185
|
+
and run ``recon → wire → certify`` against it. No LLM needed.
|
|
186
|
+
"""
|
|
187
|
+
preset = get_preset(preset_name)
|
|
188
|
+
if preset.kind == "showcase":
|
|
189
|
+
return run_showcase(preset_name=preset_name, output=output)
|
|
190
|
+
output = (output or Path.cwd() / f"forge-demo-{preset.name}").resolve()
|
|
191
|
+
if output.exists():
|
|
192
|
+
shutil.rmtree(output)
|
|
193
|
+
output.mkdir(parents=True)
|
|
194
|
+
|
|
195
|
+
llm = llm or resolve_default_client()
|
|
196
|
+
import time
|
|
197
|
+
start = time.perf_counter()
|
|
198
|
+
|
|
199
|
+
evolve_report = run_evolve(
|
|
200
|
+
preset.intent,
|
|
201
|
+
output=output,
|
|
202
|
+
package=preset.package,
|
|
203
|
+
llm=llm,
|
|
204
|
+
rounds=rounds or preset.rounds,
|
|
205
|
+
iterations_per_round=iterations or preset.iterations,
|
|
206
|
+
target_score=preset.target_score,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
cli_cmd: list[str] = []
|
|
210
|
+
cli_out = ""
|
|
211
|
+
cli_rc = -1
|
|
212
|
+
if not skip_cli_demo and preset.cli_demo_args:
|
|
213
|
+
cli_cmd = [
|
|
214
|
+
sys.executable, "-m",
|
|
215
|
+
f"{preset.package}.a4_sy_orchestration.cli", *preset.cli_demo_args,
|
|
216
|
+
]
|
|
217
|
+
env = {**__import__("os").environ}
|
|
218
|
+
sep = ";" if sys.platform == "win32" else ":"
|
|
219
|
+
existing = env.get("PYTHONPATH", "")
|
|
220
|
+
env["PYTHONPATH"] = (str(output / "src")
|
|
221
|
+
+ (sep + existing if existing else ""))
|
|
222
|
+
env["PYTHONIOENCODING"] = "utf-8"
|
|
223
|
+
try:
|
|
224
|
+
proc = subprocess.run(cli_cmd, env=env, capture_output=True,
|
|
225
|
+
text=True, timeout=20, encoding="utf-8",
|
|
226
|
+
errors="replace")
|
|
227
|
+
cli_out = (proc.stdout or "") + (proc.stderr or "")
|
|
228
|
+
cli_rc = proc.returncode
|
|
229
|
+
except (subprocess.TimeoutExpired, FileNotFoundError) as exc:
|
|
230
|
+
cli_out = f"(could not run generated CLI: {exc})"
|
|
231
|
+
cli_rc = 1
|
|
232
|
+
|
|
233
|
+
duration = time.perf_counter() - start
|
|
234
|
+
|
|
235
|
+
result = DemoResult(
|
|
236
|
+
preset=preset.name,
|
|
237
|
+
package=preset.package,
|
|
238
|
+
output_root=str(output),
|
|
239
|
+
rounds_completed=evolve_report["rounds_completed"],
|
|
240
|
+
score_trajectory=list(evolve_report["score_trajectory"]),
|
|
241
|
+
final_score=evolve_report["final_score"],
|
|
242
|
+
converged=bool(evolve_report["converged"])
|
|
243
|
+
and not (bool(cli_cmd) and cli_rc != 0),
|
|
244
|
+
cli_demo_command=cli_cmd,
|
|
245
|
+
cli_demo_stdout=cli_out.strip(),
|
|
246
|
+
cli_demo_returncode=cli_rc,
|
|
247
|
+
duration_s=round(duration, 2),
|
|
248
|
+
headline=preset.headline,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
artifact = output / "DEMO.md"
|
|
252
|
+
artifact.write_text(_render_demo_markdown(result, preset, evolve_report,
|
|
253
|
+
llm_name=llm.name),
|
|
254
|
+
encoding="utf-8")
|
|
255
|
+
result.artifact_md_path = str(artifact)
|
|
256
|
+
ManifestStore(output).save("demo", _result_to_dict(result))
|
|
257
|
+
return result
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
_SOURCE_ROOT = Path(__file__).resolve().parent / "demo_packages"
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def run_showcase(*, preset_name: str,
|
|
264
|
+
output: Path | None = None) -> DemoResult:
|
|
265
|
+
"""Run a static showcase preset — copy source, recon/wire/certify.
|
|
266
|
+
|
|
267
|
+
No LLM required. Useful for demonstrating polyglot capabilities
|
|
268
|
+
(`js-counter`, `js-bad-wire`, `mixed-py-js`). Returns the same
|
|
269
|
+
``DemoResult`` shape as :func:`run_demo` so the CLI prints
|
|
270
|
+
consistently across both kinds.
|
|
271
|
+
"""
|
|
272
|
+
preset = get_preset(preset_name)
|
|
273
|
+
if preset.kind != "showcase":
|
|
274
|
+
raise ValueError(f"preset {preset_name!r} is kind={preset.kind!r}, "
|
|
275
|
+
"expected 'showcase'")
|
|
276
|
+
source = _SOURCE_ROOT / preset.source_subdir
|
|
277
|
+
if not source.exists():
|
|
278
|
+
raise FileNotFoundError(f"showcase source not found: {source}")
|
|
279
|
+
|
|
280
|
+
target = (output or Path.cwd() / f"forge-demo-{preset.name}").resolve()
|
|
281
|
+
if target.exists():
|
|
282
|
+
shutil.rmtree(target)
|
|
283
|
+
shutil.copytree(source, target)
|
|
284
|
+
|
|
285
|
+
import time
|
|
286
|
+
start = time.perf_counter()
|
|
287
|
+
|
|
288
|
+
# Recon → wire → certify against the *copied* tree, exactly as a
|
|
289
|
+
# user would run them on the output directory.
|
|
290
|
+
recon_report = harvest_repo(target)
|
|
291
|
+
wire_report = scan_violations(target)
|
|
292
|
+
certify_report = _certify_checks(target, project=preset.name,
|
|
293
|
+
package=preset.certify_package)
|
|
294
|
+
duration = time.perf_counter() - start
|
|
295
|
+
|
|
296
|
+
score = float(certify_report.get("score", 0.0))
|
|
297
|
+
converged = score >= preset.target_score and wire_report["verdict"] == "PASS"
|
|
298
|
+
# For showcases without a CLI, leave the cli_demo fields empty — the
|
|
299
|
+
# output panel falls back to printing the recon snapshot.
|
|
300
|
+
result = DemoResult(
|
|
301
|
+
preset=preset.name,
|
|
302
|
+
package=preset.package,
|
|
303
|
+
output_root=str(target),
|
|
304
|
+
rounds_completed=0,
|
|
305
|
+
score_trajectory=[score],
|
|
306
|
+
final_score=score,
|
|
307
|
+
converged=converged,
|
|
308
|
+
cli_demo_command=[],
|
|
309
|
+
cli_demo_stdout="",
|
|
310
|
+
cli_demo_returncode=0,
|
|
311
|
+
duration_s=round(duration, 2),
|
|
312
|
+
headline=preset.headline,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
artifact = target / "DEMO.md"
|
|
316
|
+
artifact.write_text(_render_showcase_markdown(
|
|
317
|
+
result, preset, recon_report, wire_report, certify_report,
|
|
318
|
+
), encoding="utf-8")
|
|
319
|
+
result.artifact_md_path = str(artifact)
|
|
320
|
+
ManifestStore(target).save("demo", _result_to_dict(result))
|
|
321
|
+
# Persist the raw recon/wire/certify reports too, mirroring run_evolve.
|
|
322
|
+
ManifestStore(target).save("scout", recon_report)
|
|
323
|
+
ManifestStore(target).save("wire", wire_report)
|
|
324
|
+
ManifestStore(target).save("certify", certify_report)
|
|
325
|
+
return result
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _render_showcase_markdown(result: DemoResult, preset: DemoPreset,
|
|
329
|
+
recon_report: dict[str, Any],
|
|
330
|
+
wire_report: dict[str, Any],
|
|
331
|
+
certify_report: dict[str, Any]) -> str:
|
|
332
|
+
"""Render the showcase DEMO.md artifact."""
|
|
333
|
+
py = recon_report.get("python_file_count", 0)
|
|
334
|
+
js = recon_report.get("javascript_file_count", 0)
|
|
335
|
+
ts = recon_report.get("typescript_file_count", 0)
|
|
336
|
+
primary = recon_report.get("primary_language", "?")
|
|
337
|
+
components = certify_report.get("score_components", {})
|
|
338
|
+
|
|
339
|
+
lines = [
|
|
340
|
+
f"# Atomadic Forge — `forge demo {preset.name}` (showcase)",
|
|
341
|
+
"",
|
|
342
|
+
f"_{preset.headline}_",
|
|
343
|
+
"",
|
|
344
|
+
"- **Kind**: static showcase (no LLM call)",
|
|
345
|
+
f"- **Source package**: `{preset.package}`",
|
|
346
|
+
f"- **Output**: `{result.output_root}`",
|
|
347
|
+
f"- **Duration**: {result.duration_s:.1f}s",
|
|
348
|
+
"",
|
|
349
|
+
"## recon",
|
|
350
|
+
"",
|
|
351
|
+
f"- python files: **{py}**",
|
|
352
|
+
f"- javascript files: **{js}**",
|
|
353
|
+
f"- typescript files: **{ts}**",
|
|
354
|
+
f"- primary language: **{primary}**",
|
|
355
|
+
f"- symbols: {recon_report.get('symbol_count', 0)}",
|
|
356
|
+
f"- tier dist: `{recon_report.get('tier_distribution', {})}`",
|
|
357
|
+
f"- effect dist: `{recon_report.get('effect_distribution', {})}`",
|
|
358
|
+
"",
|
|
359
|
+
]
|
|
360
|
+
recs = recon_report.get("recommendations") or []
|
|
361
|
+
if recs:
|
|
362
|
+
lines.append("recommendations:")
|
|
363
|
+
for r in recs:
|
|
364
|
+
lines.append(f"- {r}")
|
|
365
|
+
lines.append("")
|
|
366
|
+
|
|
367
|
+
lines.extend([
|
|
368
|
+
"## wire",
|
|
369
|
+
"",
|
|
370
|
+
f"- verdict: **{wire_report['verdict']}**",
|
|
371
|
+
f"- violations: **{wire_report['violation_count']}**",
|
|
372
|
+
])
|
|
373
|
+
for v in wire_report.get("violations", [])[:10]:
|
|
374
|
+
lines.append(
|
|
375
|
+
f" - `{v['file']}` — {v['from_tier']} ⟵ {v['to_tier']}.{v['imported']} "
|
|
376
|
+
f"({v.get('language', 'python')})"
|
|
377
|
+
)
|
|
378
|
+
lines.extend([
|
|
379
|
+
"",
|
|
380
|
+
"## certify",
|
|
381
|
+
"",
|
|
382
|
+
f"- score: **{result.final_score:.0f}/100**"
|
|
383
|
+
+ (" — CONVERGED" if result.converged else ""),
|
|
384
|
+
f"- docs: {'PASS' if certify_report.get('documentation_complete') else 'FAIL'}",
|
|
385
|
+
f"- tests: {'PASS' if certify_report.get('tests_present') else 'FAIL'}",
|
|
386
|
+
f"- layout: {'PASS' if certify_report.get('tier_layout_present') else 'FAIL'}",
|
|
387
|
+
f"- wire: {'PASS' if certify_report.get('no_upward_imports') else 'FAIL'}",
|
|
388
|
+
"",
|
|
389
|
+
"score components:",
|
|
390
|
+
"",
|
|
391
|
+
"| axis | points |",
|
|
392
|
+
"|-------------|-------:|",
|
|
393
|
+
f"| structural | {components.get('structural', 0)} |",
|
|
394
|
+
f"| runtime | {components.get('runtime', 0)} |",
|
|
395
|
+
f"| behavioral | {components.get('behavioral', 0)} |",
|
|
396
|
+
f"| stub_penalty| {components.get('stub_penalty', 0)} |",
|
|
397
|
+
"",
|
|
398
|
+
])
|
|
399
|
+
issues = certify_report.get("issues") or []
|
|
400
|
+
if issues:
|
|
401
|
+
lines.append("issues:")
|
|
402
|
+
for it in issues[:10]:
|
|
403
|
+
lines.append(f"- {it}")
|
|
404
|
+
lines.append("")
|
|
405
|
+
|
|
406
|
+
lines.extend([
|
|
407
|
+
"## Reproduce",
|
|
408
|
+
"",
|
|
409
|
+
"```bash",
|
|
410
|
+
f"forge demo run --preset {preset.name}",
|
|
411
|
+
"```",
|
|
412
|
+
"",
|
|
413
|
+
f"Generated at "
|
|
414
|
+
f"{_dt.datetime.now(_dt.timezone.utc).isoformat(timespec='seconds')}.",
|
|
415
|
+
"",
|
|
416
|
+
])
|
|
417
|
+
return "\n".join(lines)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _result_to_dict(r: DemoResult) -> dict[str, Any]:
|
|
421
|
+
return {
|
|
422
|
+
"schema_version": "atomadic-forge.demo/v1",
|
|
423
|
+
"preset": r.preset,
|
|
424
|
+
"package": r.package,
|
|
425
|
+
"output_root": r.output_root,
|
|
426
|
+
"rounds_completed": r.rounds_completed,
|
|
427
|
+
"score_trajectory": r.score_trajectory,
|
|
428
|
+
"final_score": r.final_score,
|
|
429
|
+
"converged": r.converged,
|
|
430
|
+
"cli_demo_command": r.cli_demo_command,
|
|
431
|
+
"cli_demo_stdout": r.cli_demo_stdout,
|
|
432
|
+
"cli_demo_returncode": r.cli_demo_returncode,
|
|
433
|
+
"duration_s": r.duration_s,
|
|
434
|
+
"headline": r.headline,
|
|
435
|
+
"artifact_md_path": r.artifact_md_path,
|
|
436
|
+
"generated_at_utc": (_dt.datetime.now(_dt.timezone.utc)
|
|
437
|
+
.isoformat(timespec="seconds")),
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def _render_demo_markdown(result: DemoResult, preset: DemoPreset,
|
|
442
|
+
evolve_report: dict[str, Any],
|
|
443
|
+
llm_name: str) -> str:
|
|
444
|
+
arc = " → ".join(f"{s:.0f}" for s in result.score_trajectory)
|
|
445
|
+
lines = [
|
|
446
|
+
f"# Atomadic Forge — `forge demo {preset.name}`",
|
|
447
|
+
"",
|
|
448
|
+
f"_{preset.headline}_",
|
|
449
|
+
"",
|
|
450
|
+
f"- **LLM**: `{llm_name}`",
|
|
451
|
+
f"- **Package**: `{preset.package}`",
|
|
452
|
+
f"- **Rounds**: {result.rounds_completed} / {preset.rounds}",
|
|
453
|
+
f"- **Trajectory**: `{arc}`",
|
|
454
|
+
f"- **Final**: **{result.final_score:.0f}/100**"
|
|
455
|
+
+ (" — CONVERGED" if result.converged else ""),
|
|
456
|
+
f"- **Duration**: {result.duration_s:.1f}s",
|
|
457
|
+
"",
|
|
458
|
+
"## Score arc",
|
|
459
|
+
"",
|
|
460
|
+
"```",
|
|
461
|
+
_ascii_trajectory(result.score_trajectory),
|
|
462
|
+
"```",
|
|
463
|
+
"",
|
|
464
|
+
"## Generated CLI invocation",
|
|
465
|
+
"",
|
|
466
|
+
"```bash",
|
|
467
|
+
"$ " + " ".join(result.cli_demo_command),
|
|
468
|
+
result.cli_demo_stdout or "(no output)",
|
|
469
|
+
"```",
|
|
470
|
+
"",
|
|
471
|
+
f"Exit code: `{result.cli_demo_returncode}` "
|
|
472
|
+
+ ("✓" if result.cli_demo_returncode == 0 else "(see output above)"),
|
|
473
|
+
"",
|
|
474
|
+
"## What this proves",
|
|
475
|
+
"",
|
|
476
|
+
"- Forge enforces the 5-tier monadic law on every emitted file.",
|
|
477
|
+
"- The behavioral pytest runner gates the score on real test passes.",
|
|
478
|
+
"- A free local 7B model produced architecturally-coherent, "
|
|
479
|
+
"test-passing software in a few minutes.",
|
|
480
|
+
"- Plug a stronger LLM in via `--provider gemini|anthropic|openai` "
|
|
481
|
+
"and the same trajectory carries harder tasks higher.",
|
|
482
|
+
"",
|
|
483
|
+
"## Reproduce",
|
|
484
|
+
"",
|
|
485
|
+
"```bash",
|
|
486
|
+
f"forge demo run --preset {preset.name} --provider auto",
|
|
487
|
+
"```",
|
|
488
|
+
"",
|
|
489
|
+
f"Generated at {_dt.datetime.now(_dt.timezone.utc).isoformat(timespec='seconds')}.",
|
|
490
|
+
"",
|
|
491
|
+
]
|
|
492
|
+
return "\n".join(lines)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def _ascii_trajectory(scores: list[float], width: int = 50) -> str:
|
|
496
|
+
if not scores:
|
|
497
|
+
return "(no trajectory)"
|
|
498
|
+
lines: list[str] = []
|
|
499
|
+
for i, s in enumerate(scores):
|
|
500
|
+
bar_len = int(width * (s / 100.0))
|
|
501
|
+
lines.append(f" R{i} [{('█' * bar_len).ljust(width)}] {s:5.1f}/100")
|
|
502
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Tier a3 — Emergent Behaviors Scan feature.
|
|
2
|
+
|
|
3
|
+
Glues the a1 harvester / composer / ranker / synthesiser into one pipeline.
|
|
4
|
+
The a4 CLI surface (``commands/emergent.py``) is the only thing that
|
|
5
|
+
should depend on this module.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import collections
|
|
11
|
+
import datetime as _dt
|
|
12
|
+
import json
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from ..a0_qk_constants.emergent_types import (
|
|
16
|
+
EmergentScanReport,
|
|
17
|
+
SymbolSignatureCard,
|
|
18
|
+
)
|
|
19
|
+
from ..a1_at_functions.emergent_compose import find_chains
|
|
20
|
+
from ..a1_at_functions.emergent_rank import rank_chains
|
|
21
|
+
from ..a1_at_functions.emergent_signature_extract import harvest_signatures
|
|
22
|
+
from ..a1_at_functions.emergent_synthesize import render_emergent_feature
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class EmergentScan:
|
|
26
|
+
"""Scan a tier-organized package for emergent feature candidates."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, *, src_root: Path, package: str = "atomadic_forge") -> None:
|
|
29
|
+
self.src_root = Path(src_root)
|
|
30
|
+
self.package = package
|
|
31
|
+
self._catalog: list[SymbolSignatureCard] | None = None
|
|
32
|
+
self._chains_count: int = 0
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def catalog(self) -> list[SymbolSignatureCard]:
|
|
36
|
+
if self._catalog is None:
|
|
37
|
+
self._catalog = harvest_signatures(self.src_root, self.package)
|
|
38
|
+
return self._catalog
|
|
39
|
+
|
|
40
|
+
def scan(
|
|
41
|
+
self,
|
|
42
|
+
*,
|
|
43
|
+
max_depth: int = 3,
|
|
44
|
+
top_n: int = 25,
|
|
45
|
+
require_pure: bool = False,
|
|
46
|
+
domain_jump_required: bool = True,
|
|
47
|
+
max_chains: int = 5_000,
|
|
48
|
+
) -> EmergentScanReport:
|
|
49
|
+
catalog = self.catalog
|
|
50
|
+
chains = find_chains(
|
|
51
|
+
catalog,
|
|
52
|
+
max_depth=max_depth,
|
|
53
|
+
max_chains=max_chains,
|
|
54
|
+
require_pure=require_pure,
|
|
55
|
+
domain_jump_required=domain_jump_required,
|
|
56
|
+
)
|
|
57
|
+
self._chains_count = len(chains)
|
|
58
|
+
candidates = rank_chains(chains, catalog=catalog, top_n=top_n)
|
|
59
|
+
|
|
60
|
+
domain_inv: collections.Counter[str] = collections.Counter(
|
|
61
|
+
c["domain"] for c in catalog
|
|
62
|
+
)
|
|
63
|
+
tier_inv: collections.Counter[str] = collections.Counter(
|
|
64
|
+
c["tier"] for c in catalog
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return EmergentScanReport(
|
|
68
|
+
schema_version="atomadic-forge.emergent.scan/v1",
|
|
69
|
+
generated_at_utc=_dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
70
|
+
catalog_size=len(catalog),
|
|
71
|
+
chain_count_considered=len(chains),
|
|
72
|
+
candidates=candidates,
|
|
73
|
+
domain_inventory=dict(domain_inv),
|
|
74
|
+
tier_inventory=dict(tier_inv),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def synthesize(self, candidate_id: str, report: EmergentScanReport,
|
|
78
|
+
*, out_dir: Path | None = None) -> Path:
|
|
79
|
+
"""Materialize a candidate as a new ``a3_og_features/<name>.py``."""
|
|
80
|
+
match = next((c for c in report["candidates"] if c["candidate_id"] == candidate_id),
|
|
81
|
+
None)
|
|
82
|
+
if match is None:
|
|
83
|
+
raise KeyError(f"candidate {candidate_id} not found in report")
|
|
84
|
+
out_dir = out_dir or (self.src_root / self.package / "a3_og_features")
|
|
85
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
slug = match["name"].replace("-", "_") + "_emergent.py"
|
|
87
|
+
target = out_dir / slug
|
|
88
|
+
target.write_text(render_emergent_feature(match), encoding="utf-8")
|
|
89
|
+
return target
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def save_report(report: EmergentScanReport, target: Path) -> Path:
|
|
93
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
target.write_text(json.dumps(report, indent=2, default=str), encoding="utf-8")
|
|
95
|
+
return target
|