devlyn-cli 2.10.0 → 2.10.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.
@@ -70,6 +70,7 @@ devlyn:ideate/references/project-mode.md
70
70
  devlyn:ideate/references/from-spec-mode.md
71
71
  devlyn:resolve/SKILL.md
72
72
  devlyn:queue/SKILL.md
73
+ devlyn:engines/SKILL.md
73
74
  devlyn:resolve/references/state-schema.md
74
75
  devlyn:resolve/references/free-form-mode.md
75
76
  devlyn:resolve/references/phases/plan.md
@@ -152,6 +153,7 @@ offenders=$(git grep -Il -- 'mcp__codex-cli__' -- \
152
153
  ':!config/skills/preflight-workspace/**' \
153
154
  ':!benchmark/auto-resolve/external/**' \
154
155
  ':!benchmark/auto-resolve/results/**' \
156
+ ':!benchmark/ceiling/results/**' \
155
157
  ':!benchmark/auto-resolve/PILOT-RESULTS*' \
156
158
  2>/dev/null || true)
157
159
  if [ -z "$offenders" ]; then
@@ -176,6 +178,7 @@ offenders=$(git grep -Il -- 'Requires Codex MCP\|Codex MCP server\|Codex MCP ava
176
178
  ':!config/skills/preflight-workspace/**' \
177
179
  ':!benchmark/auto-resolve/external/**' \
178
180
  ':!benchmark/auto-resolve/results/**' \
181
+ ':!benchmark/ceiling/results/**' \
179
182
  ':!benchmark/auto-resolve/PILOT-RESULTS*' \
180
183
  2>/dev/null || true)
181
184
  if [ -z "$offenders" ]; then
@@ -266,6 +269,84 @@ else
266
269
  bad "devlyn:design-ui must not be listed in OPTIONAL_ADDONS"
267
270
  fi
268
271
 
272
+ # ---------------------------------------------------------------------------
273
+ # 5b. Every completed managed-skills install writes its own version marker.
274
+ # ---------------------------------------------------------------------------
275
+ section "Check 5b: Installed skill roots carry exact version markers"
276
+ if make_temp_dir tmp_install_marker /tmp/devlyn-install-marker.XXXXXX; then
277
+ marker_home="$tmp_install_marker/home"
278
+ marker_project="$tmp_install_marker/project"
279
+ installer="$PWD/bin/devlyn.js"
280
+ mkdir -p "$marker_home" "$marker_project"
281
+
282
+ if (cd "$marker_project" \
283
+ && HOME="$marker_home" node "$installer" -y >"$tmp_install_marker/claude.log" 2>&1 \
284
+ && HOME="$marker_home" node "$installer" agents all >"$tmp_install_marker/agents.log" 2>&1); then
285
+ if python3 - "$installer" "$marker_home" "$marker_project" <<'PY'
286
+ import json
287
+ import pathlib
288
+ import stat
289
+ import sys
290
+
291
+ installer = pathlib.Path(sys.argv[1])
292
+ home = pathlib.Path(sys.argv[2])
293
+ project = pathlib.Path(sys.argv[3])
294
+ package = json.loads((installer.parent.parent / "package.json").read_text())
295
+ expected = {"schemaVersion": 1, "package": package["name"], "version": package["version"]}
296
+ roots = [
297
+ project / ".claude" / "skills",
298
+ home / ".codex" / "skills",
299
+ home / ".agents" / "skills",
300
+ home / ".grok" / "skills",
301
+ ]
302
+ for root in roots:
303
+ marker = root / ".devlyn-install.json"
304
+ actual = json.loads(marker.read_text())
305
+ if actual != expected:
306
+ raise SystemExit(f"{marker}: expected {expected}, got {actual}")
307
+ if stat.S_IMODE(marker.stat().st_mode) != 0o600:
308
+ raise SystemExit(f"{marker}: expected mode 0600")
309
+ if list(root.glob(".devlyn-install.json.*.tmp")):
310
+ raise SystemExit(f"{root}: stale marker temp file")
311
+ PY
312
+ then
313
+ if grep -Fxq '.claude/skills/.devlyn-install.json' "$marker_project/.gitignore"; then
314
+ ok "Claude, Codex, shared-agent, and Grok roots have exact 0600 markers; project marker is ignored"
315
+ else
316
+ bad "Claude project install must ignore its local marker"
317
+ fi
318
+ else
319
+ bad "completed installs must write the exact per-root marker"
320
+ fi
321
+ else
322
+ bad "isolated managed-skills install failed"
323
+ fi
324
+
325
+ incomplete="$tmp_install_marker/incomplete"
326
+ mkdir -p "$incomplete/package" "$incomplete/home/.codex/skills" "$incomplete/claude/.claude/skills" "$incomplete/codex"
327
+ cp -R bin config package.json "$incomplete/package/"
328
+ rm -rf "$incomplete/package/config/skills/devlyn:queue"
329
+ printf '{"version":"stale"}\n' > "$incomplete/home/.codex/skills/.devlyn-install.json"
330
+ printf '{"version":"stale"}\n' > "$incomplete/claude/.claude/skills/.devlyn-install.json"
331
+ if ! (cd "$incomplete/claude" \
332
+ && HOME="$incomplete/home" node "$incomplete/package/bin/devlyn.js" -y >"$incomplete/claude.log" 2>&1) \
333
+ && ! (cd "$incomplete/codex" \
334
+ && HOME="$incomplete/home" node "$incomplete/package/bin/devlyn.js" agents codex >"$incomplete/codex.log" 2>&1) \
335
+ && grep -Fq 'Incomplete devlyn skill install; missing: devlyn:queue' "$incomplete/claude.log" \
336
+ && grep -Fq 'Incomplete devlyn skill install; missing: devlyn:queue' "$incomplete/codex.log" \
337
+ && [ -d "$incomplete/claude/.claude/skills/devlyn:resolve" ] \
338
+ && [ -d "$incomplete/home/.codex/skills/devlyn:resolve" ] \
339
+ && [ ! -e "$incomplete/claude/.claude/skills/.devlyn-install.json" ] \
340
+ && [ ! -e "$incomplete/home/.codex/skills/.devlyn-install.json" ]; then
341
+ ok "incomplete Claude and Codex copies fail visibly without stale or replacement markers"
342
+ else
343
+ bad "incomplete Claude and Codex copies must fail visibly without an install marker"
344
+ fi
345
+ rm -rf "$tmp_install_marker"
346
+ else
347
+ bad "could not allocate install-marker smoke directory"
348
+ fi
349
+
269
350
  # ---------------------------------------------------------------------------
270
351
  # 6. Source ↔ installed mirror parity on critical path.
271
352
  # Only runs if .claude/skills exists (i.e. installer has been run).
@@ -386,9 +467,17 @@ if python3 config/skills/_shared/collect-codex-findings.py --self-test >/dev/nul
386
467
  else
387
468
  bad "collect-codex-findings.py self-test failed"
388
469
  fi
470
+ # iter-0082: the frozen collector contract — every negative through every ingress
471
+ # path, plus real judge captures replayed against hand-verified expectations.
472
+ # The self-test above is unit-shaped; this is the integration bar against real bytes.
473
+ if python3 benchmark/ceiling/probes/r-weld-0082/test-collector-contract.py >/dev/null 2>&1; then
474
+ ok "collector contract (iter-0082 negatives + real-capture corpus) passed"
475
+ else
476
+ bad "collector contract failed — run benchmark/ceiling/probes/r-weld-0082/test-collector-contract.py"
477
+ fi
389
478
  if ! grep -Fq 'def reject_json_constant' config/skills/_shared/collect-codex-findings.py \
390
479
  || ! grep -Fq 'loads_strict_json(raw)' config/skills/_shared/collect-codex-findings.py \
391
- || ! grep -Fq 'NaN Codex stdout finding must not normalize' config/skills/_shared/collect-codex-findings.py; then
480
+ || ! grep -Fq 'NaN pair-JUDGE stdout finding' config/skills/_shared/collect-codex-findings.py; then
392
481
  bad "collect-codex-findings.py must reject non-standard JSON constants in pair-JUDGE stdout"
393
482
  fi
394
483
 
@@ -702,13 +791,16 @@ fi
702
791
  if ! grep -Fq '"uses_visible_input_key_names"' config/skills/_shared/spec-verify-check.py \
703
792
  || ! grep -Fq '"asserts_visible_output_key_names"' config/skills/_shared/spec-verify-check.py \
704
793
  || ! grep -Fq '"asserts_no_unexpected_output_keys"' config/skills/_shared/spec-verify-check.py \
705
- || ! grep -Fq 'shape_contract co-occurring with error_contract without' config/skills/_shared/spec-verify-check.py \
794
+ || ! grep -Fq 'shape_contract claiming visible exact error object without' config/skills/_shared/spec-verify-check.py \
795
+ || ! grep -Fq 'visible_text_names_exact_json_error_object' config/skills/_shared/spec-verify-check.py \
706
796
  || ! grep -Fq 'JSON error object shape_contract with exact object evidence was rejected' config/skills/_shared/spec-verify-check.py \
707
797
  || ! grep -Fq 'shape_contract without any evidence was accepted' config/skills/_shared/spec-verify-check.py \
708
798
  || ! grep -Fq 'shape_contract with exact key evidence was rejected' config/skills/_shared/spec-verify-check.py \
709
799
  || ! grep -Fq '`shape_contract` when the visible text names exact keys' config/skills/devlyn:resolve/references/phases/probe-derive.md \
710
- || ! grep -Fq '`shape_contract` must' config/skills/devlyn:resolve/SKILL.md; then
711
- bad "risk-probe shape contracts must require exact visible input/output key evidence unconditionally, and asserts_exact_error_object when co-occurring with error_contract/http_error_contract"
800
+ || ! grep -Fq 'visible_text_names_exact_json_error_object' config/skills/devlyn:resolve/references/phases/probe-derive.md \
801
+ || ! grep -Fq '`shape_contract` must' config/skills/devlyn:resolve/SKILL.md \
802
+ || ! grep -Fq 'visible_text_names_exact_json_error_object' config/skills/devlyn:resolve/SKILL.md; then
803
+ bad "risk-probe shape contracts must require exact visible input/output key evidence unconditionally, and asserts_exact_error_object when the probe claims the visible text names an exact JSON error object"
712
804
  fi
713
805
  if ! grep -Fq '"auth_signature_contract"' config/skills/_shared/spec-verify-check.py \
714
806
  || ! grep -Fq '"idempotency_replay"' config/skills/_shared/spec-verify-check.py \
@@ -1015,7 +1107,7 @@ fi
1015
1107
  if ! grep -Fq 'per-feature Verification must include a solo-headroom' config/skills/devlyn:ideate/references/project-mode.md \
1016
1108
  || ! grep -Fq 'per-feature Verification must include a solo-headroom' .claude/skills/devlyn:ideate/references/project-mode.md \
1017
1109
  || ! grep -Fq 'per-feature Verification must include a solo-headroom' .agents/skills/devlyn:ideate/references/project-mode.md \
1018
- || rg -q 'Context or Verification' config/skills/devlyn:ideate/references .claude/skills/devlyn:ideate/references .agents/skills/devlyn:ideate/references; then
1110
+ || grep -rq 'Context or Verification' config/skills/devlyn:ideate/references .claude/skills/devlyn:ideate/references .agents/skills/devlyn:ideate/references; then
1019
1111
  bad "ideate project mode must keep solo-headroom hypothesis inside each pair-candidate feature spec"
1020
1112
  else
1021
1113
  ok "ideate project mode keeps solo-headroom hypothesis inside each pair-candidate feature spec"
@@ -1421,6 +1513,37 @@ if [ $pair_trigger_order_missing -eq 0 ]; then
1421
1513
  ok "VERIFY default pair dispatches concurrently"
1422
1514
  fi
1423
1515
 
1516
+ section "Check 6k: PLAN route is orchestrator-fixed"
1517
+ plan_route_ok=1
1518
+ for plan_route_file in "config/skills/devlyn:resolve/SKILL.md" CLAUDE.md AGENTS.md \
1519
+ "config/skills/devlyn:engines/SKILL.md" ".agents/skills/devlyn:resolve/SKILL.md" \
1520
+ ".agents/skills/devlyn:engines/SKILL.md"; do
1521
+ [ -f "$plan_route_file" ] || plan_route_ok=0
1522
+ done
1523
+ sed -n '/^<engine_routing>$/,/^<\/engine_routing>$/p' "config/skills/devlyn:resolve/SKILL.md" \
1524
+ | grep -Fq 'PLAN is orchestrator-fixed and never inherits `--engine`, an executor pin, or `state.engine`' || plan_route_ok=0
1525
+ sed -n '/^## PHASE 1: PLAN$/,/^## PHASE 1.5/p' "config/skills/devlyn:resolve/SKILL.md" \
1526
+ | grep -Fq 'PLAN is orchestrator-fixed and never inherits `--engine`, an executor pin, or `state.engine`' || plan_route_ok=0
1527
+ grep -Fq 'PLAN is orchestrator-fixed and never inherits `--engine` or an executor pin' CLAUDE.md || plan_route_ok=0
1528
+ grep -Fq 'PLAN is orchestrator-fixed and never inherits `--engine` or the executor pin' AGENTS.md || plan_route_ok=0
1529
+ grep -Fq 'PLAN is orchestrator-fixed and never follows the pin' config/skills/devlyn:engines/SKILL.md || plan_route_ok=0
1530
+ if grep -En '([Ee]xecutor|Default engine: Claude for)[^[:cntrl:]]{0,80}PLAN[[:space:]]*/[[:space:]]*IMPLEMENT([[:space:]]*/[[:space:]]*BUILD_GATE)?[[:space:]]*/[[:space:]]*CLEANUP' \
1531
+ config/skills/devlyn:resolve/SKILL.md \
1532
+ CLAUDE.md \
1533
+ AGENTS.md \
1534
+ README.md \
1535
+ config/skills/devlyn:engines/SKILL.md \
1536
+ .agents/skills/devlyn:resolve/SKILL.md \
1537
+ .agents/skills/devlyn:engines/SKILL.md \
1538
+ >/dev/null 2>&1; then
1539
+ plan_route_ok=0
1540
+ fi
1541
+ if [ $plan_route_ok -eq 1 ]; then
1542
+ ok "PLAN is orchestrator-fixed: exact declarations locked on all surfaces, no executor-role enumeration"
1543
+ else
1544
+ bad "PLAN route contract violated: a locked orchestrator-fixed declaration is missing or an executor-role enumeration re-teaches PLAN"
1545
+ fi
1546
+
1424
1547
  section "Check 6h: No undocumented spec.expected.json.browser_flows field"
1425
1548
  browser_flow_refs=$(grep -RInF 'spec.expected.json.browser_flows' \
1426
1549
  config/skills README.md bin/ package.json 2>/dev/null || true)
@@ -3508,8 +3631,9 @@ else
3508
3631
  offenders="${offenders}"$'\n'"packaged pair evidence subset could not allocate temporary audit workspace"
3509
3632
  fi
3510
3633
  if make_temp_file pack_json /tmp/devlyn-lint-pack.XXXXXX.json \
3511
- && npm pack --dry-run --json > "$pack_json" 2>/dev/null; then
3512
- if ! node - "$pack_json" <<'NODE'
3634
+ && make_temp_dir pack_cache /tmp/devlyn-lint-pack-cache.XXXXXX; then
3635
+ if npm_config_cache="$pack_cache" npm pack --dry-run --json > "$pack_json" 2>/dev/null; then
3636
+ if ! node - "$pack_json" <<'NODE'
3513
3637
  const fs = require("fs");
3514
3638
  const path = require("path");
3515
3639
  const packPath = process.argv[2];
@@ -3595,13 +3719,17 @@ if (forbidden.length > 0) {
3595
3719
  process.exit(2);
3596
3720
  }
3597
3721
  NODE
3598
- then
3599
- offenders="${offenders}"$'\n'"npm pack dry-run must include benchmark runner/gate regression tests, all shadow fixture files, retired fixture replay docs, and exclude pycache artifacts"
3722
+ then
3723
+ offenders="${offenders}"$'\n'"npm pack dry-run must include benchmark runner/gate regression tests, all shadow fixture files, retired fixture replay docs, and exclude pycache artifacts"
3724
+ fi
3725
+ else
3726
+ offenders="${offenders}"$'\n'"npm pack dry-run failed while checking benchmark runner/gate regression tests"
3600
3727
  fi
3601
3728
  else
3602
- offenders="${offenders}"$'\n'"npm pack dry-run failed while checking benchmark runner/gate regression tests"
3729
+ offenders="${offenders}"$'\n'"npm pack dry-run could not allocate temporary package workspace"
3603
3730
  fi
3604
3731
  [ -n "${pack_json:-}" ] && rm -f "$pack_json"
3732
+ [ -n "${pack_cache:-}" ] && rm -rf "$pack_cache"
3605
3733
  non_executable_shell_scripts=$(find benchmark/auto-resolve/scripts -maxdepth 1 -name '*.sh' -type f ! -perm -111 -print | sort)
3606
3734
  if [ -n "$non_executable_shell_scripts" ]; then
3607
3735
  while IFS= read -r f; do