devlyn-cli 3.0.1 → 3.1.1
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.
- package/AGENTS.md +1 -1
- package/CLAUDE.md +3 -3
- package/README.md +20 -3
- package/bin/devlyn.js +69 -23
- package/config/skills/_shared/adapters/claude.md +3 -2
- package/config/skills/_shared/adapters/codex.md +1 -1
- package/config/skills/_shared/archive_run.py +5 -3
- package/config/skills/_shared/codex-config.md +13 -8
- package/config/skills/_shared/codex-monitored.sh +12 -19
- package/config/skills/_shared/collect-codex-findings.py +1 -0
- package/config/skills/_shared/engine-preflight.md +2 -0
- package/config/skills/_shared/finish-gate.py +4 -1
- package/config/skills/_shared/invocation-receipt.py +194 -5
- package/config/skills/_shared/judge-role-evidence.py +33 -6
- package/config/skills/_shared/phase-prompt-render.py +2 -0
- package/config/skills/_shared/platform-support.py +370 -0
- package/config/skills/_shared/process-evidence.py +4 -2
- package/config/skills/_shared/resolve-bootstrap.py +102 -59
- package/config/skills/_shared/resolve-stop-hook.py +3 -1
- package/config/skills/_shared/role-config.py +7 -5
- package/config/skills/_shared/run-bounded.py +35 -21
- package/config/skills/_shared/spec-verify-check.py +274 -157
- package/config/skills/_shared/state-phase-write.py +26 -9
- package/config/skills/_shared/task-complete.py +1173 -0
- package/config/skills/_shared/verify-merge-findings.py +9 -8
- package/config/skills/devlyn:queue/SKILL.md +3 -3
- package/config/skills/devlyn:resolve/SKILL.md +14 -7
- package/config/skills/devlyn:resolve/references/outer-loop.md +10 -2
- package/config/skills/devlyn:resolve/references/phases/verify.md +5 -3
- package/config/skills/devlyn:resolve/references/task-completion.md +117 -0
- package/package.json +1 -1
- package/scripts/lint-skills.sh +10 -6
|
@@ -2035,7 +2035,7 @@ def final_report_self_test() -> None:
|
|
|
2035
2035
|
run_id = "rs-final-report-0126"
|
|
2036
2036
|
write_state(state_path, {"version": "3.0", "run_id": run_id, "engine": "claude", "phases": {}})
|
|
2037
2037
|
command = [sys.executable, str(script), "--devlyn-dir", ".devlyn", "--phase", "final_report"]
|
|
2038
|
-
spawned = subprocess.run(command + ["spawn", "--round", "0"], cwd=work, capture_output=True, text=True)
|
|
2038
|
+
spawned = subprocess.run(command + ["spawn", "--round", "0"], cwd=work, capture_output=True, text=True, encoding="utf-8")
|
|
2039
2039
|
assert spawned.returncode == 0, spawned.stderr
|
|
2040
2040
|
before = state_path.read_bytes()
|
|
2041
2041
|
marker = f"<!-- devlyn:final-report run_id={run_id} -->\n"
|
|
@@ -2074,12 +2074,12 @@ def final_report_self_test() -> None:
|
|
|
2074
2074
|
report.write_bytes(valid)
|
|
2075
2075
|
args = ["--log-file", str(outside)]
|
|
2076
2076
|
result = subprocess.run(command + ["complete", "--verdict", "PASS_WITH_ISSUES", *args],
|
|
2077
|
-
cwd=work, capture_output=True, text=True)
|
|
2077
|
+
cwd=work, capture_output=True, text=True, encoding="utf-8")
|
|
2078
2078
|
assert result.returncode == 1 and "BLOCKED:final-report-invalid" in result.stderr, (case, result)
|
|
2079
2079
|
assert state_path.read_bytes() == before, case
|
|
2080
2080
|
report.write_bytes(valid)
|
|
2081
2081
|
result = subprocess.run(command + ["complete", "--verdict", "PASS_WITH_ISSUES", "--log-file", ".devlyn/final-report.md"],
|
|
2082
|
-
cwd=work, capture_output=True, text=True)
|
|
2082
|
+
cwd=work, capture_output=True, text=True, encoding="utf-8")
|
|
2083
2083
|
assert result.returncode == 0, result.stderr
|
|
2084
2084
|
completed_bytes = state_path.read_bytes()
|
|
2085
2085
|
completed = read_state(state_path)
|
|
@@ -2105,7 +2105,7 @@ def final_report_self_test() -> None:
|
|
|
2105
2105
|
report.symlink_to(outside)
|
|
2106
2106
|
write_state(state_path, candidate)
|
|
2107
2107
|
before_archive = state_path.read_bytes()
|
|
2108
|
-
result = subprocess.run(archive_command, cwd=work, capture_output=True, text=True)
|
|
2108
|
+
result = subprocess.run(archive_command, cwd=work, capture_output=True, text=True, encoding="utf-8")
|
|
2109
2109
|
assert result.returncode == 1 and "error: archive blocked:" in result.stderr, (case, result)
|
|
2110
2110
|
assert state_path.read_bytes() == before_archive, case
|
|
2111
2111
|
assert not (devlyn / "runs").exists(), case
|
|
@@ -2116,7 +2116,7 @@ def final_report_self_test() -> None:
|
|
|
2116
2116
|
report.unlink()
|
|
2117
2117
|
report.write_bytes(valid)
|
|
2118
2118
|
state_path.write_bytes(completed_bytes)
|
|
2119
|
-
result = subprocess.run(archive_command, cwd=work, capture_output=True, text=True)
|
|
2119
|
+
result = subprocess.run(archive_command, cwd=work, capture_output=True, text=True, encoding="utf-8")
|
|
2120
2120
|
assert result.returncode == 0, result.stderr
|
|
2121
2121
|
target = devlyn / "runs" / run_id
|
|
2122
2122
|
assert (target / "final-report.md").read_bytes() == valid
|
|
@@ -2138,7 +2138,7 @@ def self_test() -> int:
|
|
|
2138
2138
|
(devlyn / "engines.json").write_bytes(helper["encoded"](config))
|
|
2139
2139
|
state = {"version": "3.0", "engine": "codex", "engine_source": "default", "phases": {}}
|
|
2140
2140
|
frozen = freeze_roles(state, work, "codex")
|
|
2141
|
-
(devlyn / "engines.json").write_text('{"executor":"claude"}')
|
|
2141
|
+
(devlyn / "engines.json").write_text('{"executor":"claude"}', encoding="utf-8")
|
|
2142
2142
|
assert freeze_roles(state, work, "codex") == frozen
|
|
2143
2143
|
do_spawn(state, "verify", 0, None, None, None, None, devlyn=devlyn)
|
|
2144
2144
|
assert state["engine"] == "codex" and state["phases"]["verify"]["engine"] == "claude"
|
|
@@ -2154,10 +2154,10 @@ def self_test() -> int:
|
|
|
2154
2154
|
argv = ["--json", "-m", "gpt-6-astra", "-c", "model_reasoning_effort=high", "task"]
|
|
2155
2155
|
receipt = {"argv_sha256": hashlib.sha256(json.dumps(argv, separators=(",", ":")).encode()).hexdigest()}
|
|
2156
2156
|
path = devlyn / "implement.argv.0.json"
|
|
2157
|
-
path.write_text(json.dumps(argv))
|
|
2157
|
+
path.write_text(json.dumps(argv), encoding="utf-8")
|
|
2158
2158
|
binding = bind_worker_role_argv(state, "implement", {"round": 0}, devlyn, receipt)
|
|
2159
2159
|
assert binding["effort_requested"] == "high"
|
|
2160
|
-
path.write_text(json.dumps([*argv, "extra"]))
|
|
2160
|
+
path.write_text(json.dumps([*argv, "extra"]), encoding="utf-8")
|
|
2161
2161
|
try:
|
|
2162
2162
|
bind_worker_role_argv(state, "implement", {"round": 0}, devlyn, receipt)
|
|
2163
2163
|
except ValueError:
|
|
@@ -2193,6 +2193,7 @@ def self_test() -> int:
|
|
|
2193
2193
|
"--phase", "plan", *event_args,
|
|
2194
2194
|
],
|
|
2195
2195
|
capture_output=True, text=True, check=False,
|
|
2196
|
+
encoding="utf-8",
|
|
2196
2197
|
)
|
|
2197
2198
|
|
|
2198
2199
|
def assert_plan_rejected_unchanged(
|
|
@@ -2340,6 +2341,7 @@ def self_test() -> int:
|
|
|
2340
2341
|
"--phase", "build_gate", "spawn", "--round", "0",
|
|
2341
2342
|
],
|
|
2342
2343
|
capture_output=True, text=True, check=False,
|
|
2344
|
+
encoding="utf-8",
|
|
2343
2345
|
)
|
|
2344
2346
|
assert blocked_plan_mutation.returncode != 0
|
|
2345
2347
|
assert "BLOCKED:plan-integrity-mismatch" in blocked_plan_mutation.stderr
|
|
@@ -2491,6 +2493,7 @@ def self_test() -> int:
|
|
|
2491
2493
|
"--phase", "implement", event, *event_args,
|
|
2492
2494
|
],
|
|
2493
2495
|
cwd=evidence_work, capture_output=True, text=True, check=False,
|
|
2496
|
+
encoding="utf-8",
|
|
2494
2497
|
)
|
|
2495
2498
|
|
|
2496
2499
|
for event_args in (
|
|
@@ -2514,6 +2517,7 @@ def self_test() -> int:
|
|
|
2514
2517
|
"--phase", "implement", "--id", "red-first",
|
|
2515
2518
|
],
|
|
2516
2519
|
cwd=evidence_work, capture_output=True, text=True, check=False,
|
|
2520
|
+
encoding="utf-8",
|
|
2517
2521
|
)
|
|
2518
2522
|
assert captured.returncode == 0, captured.stderr
|
|
2519
2523
|
manifest_rel = loads_strict_json(captured.stdout)["manifest_path"]
|
|
@@ -2553,6 +2557,7 @@ def self_test() -> int:
|
|
|
2553
2557
|
"--phase", "build_gate", event, *event_args,
|
|
2554
2558
|
],
|
|
2555
2559
|
cwd=evidence_work, capture_output=True, text=True, check=False,
|
|
2560
|
+
encoding="utf-8",
|
|
2556
2561
|
)
|
|
2557
2562
|
|
|
2558
2563
|
results_path = evidence_devlyn / "spec-verify.results.json"
|
|
@@ -2778,6 +2783,7 @@ def self_test() -> int:
|
|
|
2778
2783
|
[sys.executable, str(pathlib.Path(__file__).resolve()),
|
|
2779
2784
|
"--devlyn-dir", ".devlyn", "--phase", "build_gate", *args],
|
|
2780
2785
|
cwd=work, env=cli_env, capture_output=True, text=True, check=False,
|
|
2786
|
+
encoding="utf-8",
|
|
2781
2787
|
)
|
|
2782
2788
|
|
|
2783
2789
|
def rejected(*args: str, error: str = "BLOCKED:process-evidence-invalid") -> None:
|
|
@@ -3126,6 +3132,7 @@ def self_test() -> int:
|
|
|
3126
3132
|
"--next-round", "0", "--next-engine", "claude",
|
|
3127
3133
|
],
|
|
3128
3134
|
capture_output=True, text=True,
|
|
3135
|
+
encoding="utf-8",
|
|
3129
3136
|
)
|
|
3130
3137
|
assert cli_transition.returncode == 0, cli_transition.stderr
|
|
3131
3138
|
cli_receipt = loads_strict_json(cli_transition.stdout)
|
|
@@ -3372,6 +3379,7 @@ def self_test() -> int:
|
|
|
3372
3379
|
subprocess.run(["git", "commit", "-qm", "base"], cwd=work, check=True)
|
|
3373
3380
|
pre_sha = subprocess.run(
|
|
3374
3381
|
["git", "rev-parse", "HEAD"], cwd=work, check=True, capture_output=True, text=True,
|
|
3382
|
+
encoding="utf-8",
|
|
3375
3383
|
).stdout.strip()
|
|
3376
3384
|
work_devlyn = work / ".devlyn"
|
|
3377
3385
|
work_devlyn.mkdir()
|
|
@@ -3441,6 +3449,7 @@ def self_test() -> int:
|
|
|
3441
3449
|
"--authorized-surface-json", json.dumps([entry]),
|
|
3442
3450
|
],
|
|
3443
3451
|
cwd=work, capture_output=True, text=True,
|
|
3452
|
+
encoding="utf-8",
|
|
3444
3453
|
)
|
|
3445
3454
|
if (
|
|
3446
3455
|
surface_check.returncode == 0
|
|
@@ -3830,7 +3839,7 @@ def self_test() -> int:
|
|
|
3830
3839
|
else:
|
|
3831
3840
|
raise AssertionError("SURFACE_CLOSE opened BUILD_GATE without native evidence")
|
|
3832
3841
|
assert transition_state == inherited_surface
|
|
3833
|
-
wrapper_log.write_text('{"modelUsage":{"different-model":{"inputTokens":1}}}\n')
|
|
3842
|
+
wrapper_log.write_text('{"modelUsage":{"different-model":{"inputTokens":1}}}\n', encoding="utf-8")
|
|
3834
3843
|
mismatch = copy.deepcopy(surface_state)
|
|
3835
3844
|
assert "model-attestation-mismatch" in do_complete(
|
|
3836
3845
|
mismatch, "surface_close", "PASS", None, None, None, None, None,
|
|
@@ -4348,6 +4357,7 @@ def self_test() -> int:
|
|
|
4348
4357
|
[sys.executable, script, "--devlyn-dir", str(receipt_devlyn),
|
|
4349
4358
|
"--phase", phase, *event_args],
|
|
4350
4359
|
cwd=receipt_work, capture_output=True, text=True, check=False,
|
|
4360
|
+
encoding="utf-8",
|
|
4351
4361
|
)
|
|
4352
4362
|
|
|
4353
4363
|
write_state(receipt_state_path, {
|
|
@@ -4421,6 +4431,7 @@ def self_test() -> int:
|
|
|
4421
4431
|
archived = subprocess.run(
|
|
4422
4432
|
[sys.executable, str(pathlib.Path(script).with_name("archive_run.py"))],
|
|
4423
4433
|
cwd=archive_work, capture_output=True, text=True,
|
|
4434
|
+
encoding="utf-8",
|
|
4424
4435
|
)
|
|
4425
4436
|
assert archived.returncode == 0, archived.stderr
|
|
4426
4437
|
destination = archive_devlyn / "runs" / terminal_state["run_id"]
|
|
@@ -4608,6 +4619,7 @@ def self_test() -> int:
|
|
|
4608
4619
|
return subprocess.run(
|
|
4609
4620
|
["git", "rev-parse", "HEAD"], cwd=repo, check=True,
|
|
4610
4621
|
capture_output=True, text=True,
|
|
4622
|
+
encoding="utf-8",
|
|
4611
4623
|
).stdout.strip()
|
|
4612
4624
|
|
|
4613
4625
|
def durability_fixture(
|
|
@@ -4668,6 +4680,7 @@ def self_test() -> int:
|
|
|
4668
4680
|
assert subprocess.run(
|
|
4669
4681
|
["git", "show", "-s", "--format=%s", "HEAD"], cwd=exact_repo,
|
|
4670
4682
|
check=True, capture_output=True, text=True,
|
|
4683
|
+
encoding="utf-8",
|
|
4671
4684
|
).stdout.strip() == "chore(pipeline): closure-restore round 1"
|
|
4672
4685
|
gate_spec = importlib.util.spec_from_file_location(
|
|
4673
4686
|
"f7_carrier_gate", pathlib.Path(__file__).resolve().parents[3]
|
|
@@ -4794,6 +4807,7 @@ def self_test() -> int:
|
|
|
4794
4807
|
"--devlyn-dir", ".devlyn", "--phase", "verify", "spawn",
|
|
4795
4808
|
"--round", "1"],
|
|
4796
4809
|
cwd=target_repo, capture_output=True, text=True,
|
|
4810
|
+
encoding="utf-8",
|
|
4797
4811
|
)
|
|
4798
4812
|
assert reentry.returncode != 0
|
|
4799
4813
|
assert "closure-durability-receipt" in reentry.stderr
|
|
@@ -4813,12 +4827,14 @@ def self_test() -> int:
|
|
|
4813
4827
|
[sys.executable, str(pathlib.Path(__file__).resolve()),
|
|
4814
4828
|
"--devlyn-dir", ".devlyn", "--phase", "build_gate", "spawn", "--round", "1"],
|
|
4815
4829
|
cwd=skipped_repo, capture_output=True, text=True,
|
|
4830
|
+
encoding="utf-8",
|
|
4816
4831
|
)
|
|
4817
4832
|
assert skipped.returncode != 0 and "checkpoint receipt is missing" in skipped.stderr
|
|
4818
4833
|
assert not (skipped_devlyn / "closure-durability.round-1.json").exists()
|
|
4819
4834
|
assert subprocess.run(
|
|
4820
4835
|
["git", "rev-parse", "HEAD"], cwd=skipped_repo, check=True,
|
|
4821
4836
|
capture_output=True, text=True,
|
|
4837
|
+
encoding="utf-8",
|
|
4822
4838
|
).stdout.strip() == skipped_fix
|
|
4823
4839
|
|
|
4824
4840
|
receipt_repo, receipt_devlyn, receipt_state, receipt_fix, _ = durability_fixture(
|
|
@@ -5180,4 +5196,5 @@ def main() -> int:
|
|
|
5180
5196
|
|
|
5181
5197
|
|
|
5182
5198
|
if __name__ == "__main__":
|
|
5199
|
+
runpy.run_path(str(pathlib.Path(__file__).with_name("platform-support.py")))["configure_utf8"]()
|
|
5183
5200
|
raise SystemExit(main())
|