devlyn-cli 2.10.1 → 3.0.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.
Files changed (77) hide show
  1. package/AGENTS.md +8 -9
  2. package/CLAUDE.md +17 -17
  3. package/README.md +33 -9
  4. package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.json +25 -0
  5. package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.md +10 -0
  6. package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.json +18 -0
  7. package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.md +9 -0
  8. package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.json +18 -0
  9. package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.md +9 -0
  10. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F16-cli-quote-tax-rules/l2_risk_probes/result.json +28 -0
  11. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F23-cli-fulfillment-wave/l2_risk_probes/result.json +28 -0
  12. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F25-cli-cart-promotion-rules/l2_risk_probes/result.json +30 -0
  13. package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.json +28 -0
  14. package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.md +15 -0
  15. package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.json +28 -0
  16. package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.md +15 -0
  17. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/F21-cli-scheduler-priority/l2_risk_probes/result.json +32 -0
  18. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.json +44 -0
  19. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.md +16 -0
  20. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.json +18 -0
  21. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.md +9 -0
  22. package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.json +28 -0
  23. package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.md +15 -0
  24. package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.json +28 -0
  25. package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.md +15 -0
  26. package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.json +28 -0
  27. package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.md +15 -0
  28. package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.json +28 -0
  29. package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.md +15 -0
  30. package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.json +28 -0
  31. package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.md +15 -0
  32. package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.json +28 -0
  33. package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.md +15 -0
  34. package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.json +28 -0
  35. package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.md +15 -0
  36. package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.json +28 -0
  37. package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.md +15 -0
  38. package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.json +28 -0
  39. package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.md +15 -0
  40. package/bin/devlyn.js +9 -2
  41. package/config/skills/_shared/.ruff.toml +2 -0
  42. package/config/skills/_shared/adapters/claude.md +9 -1
  43. package/config/skills/_shared/adapters/codex.md +5 -0
  44. package/config/skills/_shared/adapters/grok.md +4 -2
  45. package/config/skills/_shared/archive_run.py +738 -56
  46. package/config/skills/_shared/codex-config.md +19 -4
  47. package/config/skills/_shared/codex-monitored.sh +81 -12
  48. package/config/skills/_shared/collect-codex-findings.py +311 -128
  49. package/config/skills/_shared/engine-preflight.md +8 -7
  50. package/config/skills/_shared/expected.schema.json +61 -0
  51. package/config/skills/_shared/finish-gate.py +12 -11
  52. package/config/skills/_shared/grok-anchor-guard.py +18 -1
  53. package/config/skills/_shared/invocation-receipt.py +1041 -0
  54. package/config/skills/_shared/judge-output-parser.py +200 -0
  55. package/config/skills/_shared/judge-role-evidence.py +273 -0
  56. package/config/skills/_shared/process-evidence.py +909 -0
  57. package/config/skills/_shared/resolve-bootstrap.py +776 -84
  58. package/config/skills/_shared/resolve-stop-hook.py +33 -1
  59. package/config/skills/_shared/role-config.py +552 -0
  60. package/config/skills/_shared/run-bounded.py +1 -1
  61. package/config/skills/_shared/spec-verify-check.py +749 -181
  62. package/config/skills/_shared/state-phase-write.py +1838 -58
  63. package/config/skills/_shared/terminal-claim-check.py +15 -0
  64. package/config/skills/_shared/verify-merge-findings.py +958 -51
  65. package/config/skills/devlyn:engines/SKILL.md +8 -16
  66. package/config/skills/devlyn:ideate/SKILL.md +2 -2
  67. package/config/skills/devlyn:ideate/references/spec-template.md +6 -5
  68. package/config/skills/devlyn:queue/SKILL.md +7 -4
  69. package/config/skills/devlyn:resolve/SKILL.md +93 -35
  70. package/config/skills/devlyn:resolve/references/free-form-mode.md +1 -0
  71. package/config/skills/devlyn:resolve/references/outer-loop.md +15 -0
  72. package/config/skills/devlyn:resolve/references/phases/build-gate.md +31 -2
  73. package/config/skills/devlyn:resolve/references/phases/implement.md +3 -2
  74. package/config/skills/devlyn:resolve/references/phases/verify.md +120 -84
  75. package/config/skills/devlyn:resolve/references/state-schema.md +22 -8
  76. package/package.json +5 -1
  77. package/scripts/lint-skills.sh +314 -285
@@ -277,13 +277,11 @@ def run_gate(work: pathlib.Path, devlyn_dir: pathlib.Path) -> int:
277
277
  return 0
278
278
 
279
279
  findings: list[dict] = []
280
- failed = False
281
280
  reverted = 0
282
281
  revert_failed = 0
283
282
  for seq, path in enumerate(offenders, start=1):
284
283
  ok, detail, existed_at_base = revert_offender(work, base_sha, path)
285
284
  status = "reverted" if ok else "revert-failed"
286
- failed = failed or not ok
287
285
  if ok:
288
286
  reverted += 1
289
287
  else:
@@ -305,7 +303,10 @@ def run_gate(work: pathlib.Path, devlyn_dir: pathlib.Path) -> int:
305
303
  ),
306
304
  ))
307
305
  write_findings(devlyn_dir, findings)
308
- exit_code = 2 if failed else 0
306
+ # Exit 0 let a real run treat reverted orchestrator commits as a clean pass.
307
+ # Any offender is therefore unclean even when every automatic revert succeeds;
308
+ # the caller maps exit 1/2 to BLOCKED:finish-gate-unclean.
309
+ exit_code = 2
309
310
  write_summary(devlyn_dir, {
310
311
  "mode": state.get("mode"),
311
312
  "checked": len(checked),
@@ -386,25 +387,25 @@ def self_test() -> int:
386
387
 
387
388
  work, devlyn, _base = make_fixture(root, "tracked-mutation")
388
389
  write_text(work / "notes.txt", "polluted\n")
389
- assert checked_run_gate(work, devlyn) == 0
390
+ assert checked_run_gate(work, devlyn) == 2
390
391
  assert (work / "notes.txt").read_text(encoding="utf-8") == "base notes\n"
391
392
  findings = read_findings(devlyn)
392
393
  assert findings[0]["rule_id"] == "scope.finish-unaudited-file"
393
394
  assert findings[0]["status"] == "reverted"
394
395
  assert_summary(devlyn, {
395
396
  "mode": "full", "checked": 1, "offenders": 1,
396
- "reverted": 1, "revert_failed": 0, "exit": 0,
397
+ "reverted": 1, "revert_failed": 0, "exit": 2,
397
398
  })
398
399
 
399
400
  work, devlyn, _base = make_fixture(root, "added-file")
400
401
  write_text(work / "runtime.txt", "late\n")
401
402
  git_check(work, "add", "runtime.txt")
402
- assert checked_run_gate(work, devlyn) == 0
403
+ assert checked_run_gate(work, devlyn) == 2
403
404
  assert not (work / "runtime.txt").exists()
404
405
  assert "runtime.txt" not in git_check(work, "diff", "--name-only", _base).splitlines()
405
406
  assert_summary(devlyn, {
406
407
  "mode": "full", "checked": 1, "offenders": 1,
407
- "reverted": 1, "revert_failed": 0, "exit": 0,
408
+ "reverted": 1, "revert_failed": 0, "exit": 2,
408
409
  })
409
410
 
410
411
  work, devlyn, _base = make_fixture(root, "devlyn-owned-tracked-mutation")
@@ -414,7 +415,7 @@ def self_test() -> int:
414
415
  git_check(work, "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-q", "-m", "track devlyn state")
415
416
  write_text(archived, "mutated devlyn state\n")
416
417
  write_text(work / "notes.txt", "polluted\n")
417
- assert checked_run_gate(work, devlyn) == 0
418
+ assert checked_run_gate(work, devlyn) == 2
418
419
  assert archived.read_text(encoding="utf-8") == "mutated devlyn state\n"
419
420
  assert (work / "notes.txt").read_text(encoding="utf-8") == "base notes\n"
420
421
  findings = read_findings(devlyn)
@@ -422,7 +423,7 @@ def self_test() -> int:
422
423
  assert findings[0]["status"] == "reverted"
423
424
  assert_summary(devlyn, {
424
425
  "mode": "full", "checked": 1, "offenders": 1,
425
- "reverted": 1, "revert_failed": 0, "exit": 0,
426
+ "reverted": 1, "revert_failed": 0, "exit": 2,
426
427
  })
427
428
 
428
429
  work, devlyn, base = make_fixture(root, "cleanup-window")
@@ -469,12 +470,12 @@ def self_test() -> int:
469
470
 
470
471
  work, devlyn, _base = make_fixture(root, "deleted-file")
471
472
  (work / "notes.txt").unlink()
472
- assert checked_run_gate(work, devlyn) == 0
473
+ assert checked_run_gate(work, devlyn) == 2
473
474
  assert (work / "notes.txt").read_text(encoding="utf-8") == "base notes\n"
474
475
  assert read_findings(devlyn)[0]["status"] == "reverted"
475
476
  assert_summary(devlyn, {
476
477
  "mode": "full", "checked": 1, "offenders": 1,
477
- "reverted": 1, "revert_failed": 0, "exit": 0,
478
+ "reverted": 1, "revert_failed": 0, "exit": 2,
478
479
  })
479
480
 
480
481
  work, devlyn, _base = make_fixture(root, "git-diff-failure")
@@ -33,9 +33,26 @@ SHELL_TOOLS = {"run_terminal_command", "run_terminal_cmd", "Bash"}
33
33
  FORBIDDEN = set(";&|`$()<>") | {"\n", "\r"}
34
34
 
35
35
 
36
+ def reject_json_constant(token):
37
+ raise ValueError(f"invalid JSON numeric constant: {token}")
38
+
39
+
40
+ def reject_duplicate_keys(pairs):
41
+ result = {}
42
+ for key, value in pairs:
43
+ if key in result:
44
+ raise ValueError(f"duplicate JSON key: {key}")
45
+ result[key] = value
46
+ return result
47
+
48
+
36
49
  def main():
37
50
  anchor = os.environ.get("DEVLYN_PROBE_ANCHOR", "")
38
- payload = json.load(sys.stdin)
51
+ payload = json.load(
52
+ sys.stdin,
53
+ parse_constant=reject_json_constant,
54
+ object_pairs_hook=reject_duplicate_keys,
55
+ )
39
56
 
40
57
  if payload.get("toolName") not in SHELL_TOOLS or not anchor:
41
58
  print(json.dumps({"decision": "allow"}))