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.
- package/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/README.md +2 -2
- package/benchmark/auto-resolve/scripts/run-fixture.sh +11 -1
- package/benchmark/auto-resolve/scripts/test-benchmark-arg-parsing.sh +83 -1
- package/bin/devlyn.js +53 -8
- package/config/skills/_shared/adapters/grok.md +34 -12
- package/config/skills/_shared/collect-codex-findings.py +228 -52
- package/config/skills/_shared/expected.schema.json +7 -0
- package/config/skills/_shared/grok-anchor-guard.py +61 -0
- package/config/skills/_shared/phase-prompt-render.py +181 -0
- package/config/skills/_shared/spec-verify-check.py +308 -31
- package/config/skills/_shared/state-phase-write.py +720 -32
- package/config/skills/_shared/verify-merge-findings.py +170 -0
- package/config/skills/devlyn:engines/SKILL.md +1 -1
- package/config/skills/devlyn:ideate/SKILL.md +1 -1
- package/config/skills/devlyn:resolve/SKILL.md +7 -6
- package/config/skills/devlyn:resolve/references/phases/build-gate.md +2 -2
- package/config/skills/devlyn:resolve/references/phases/implement.md +1 -1
- package/config/skills/devlyn:resolve/references/phases/plan.md +1 -5
- package/config/skills/devlyn:resolve/references/phases/probe-derive.md +4 -2
- package/config/skills/devlyn:resolve/references/phases/verify.md +5 -1
- package/config/skills/devlyn:resolve/references/state-schema.md +2 -2
- package/package.json +1 -1
- package/scripts/lint-fixtures.sh +4 -1
- package/scripts/lint-skills.sh +138 -10
package/scripts/lint-skills.sh
CHANGED
|
@@ -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
|
|
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
|
|
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 '
|
|
711
|
-
|
|
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
|
-
||
|
|
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
|
-
&&
|
|
3512
|
-
if
|
|
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
|
-
|
|
3599
|
-
|
|
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
|
|
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
|