devlyn-cli 2.10.0 → 3.0.0
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 +4 -9
- package/CLAUDE.md +11 -15
- package/README.md +31 -7
- package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.json +25 -0
- package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.md +10 -0
- package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.json +18 -0
- package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.md +9 -0
- package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.json +18 -0
- package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.md +9 -0
- package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F16-cli-quote-tax-rules/l2_risk_probes/result.json +28 -0
- package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F23-cli-fulfillment-wave/l2_risk_probes/result.json +28 -0
- package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F25-cli-cart-promotion-rules/l2_risk_probes/result.json +30 -0
- package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.md +15 -0
- package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.md +15 -0
- package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/F21-cli-scheduler-priority/l2_risk_probes/result.json +32 -0
- package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.json +44 -0
- package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.md +16 -0
- package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.json +18 -0
- package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.md +9 -0
- package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.md +15 -0
- package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.md +15 -0
- package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.md +15 -0
- package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.md +15 -0
- package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.md +15 -0
- package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.md +15 -0
- package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.md +15 -0
- package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.md +15 -0
- package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.json +28 -0
- package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.md +15 -0
- 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 +59 -9
- package/config/skills/_shared/.ruff.toml +2 -0
- package/config/skills/_shared/adapters/claude.md +9 -1
- package/config/skills/_shared/adapters/codex.md +5 -0
- package/config/skills/_shared/adapters/grok.md +37 -13
- package/config/skills/_shared/archive_run.py +738 -56
- package/config/skills/_shared/codex-config.md +19 -4
- package/config/skills/_shared/codex-monitored.sh +81 -12
- package/config/skills/_shared/collect-codex-findings.py +419 -60
- package/config/skills/_shared/engine-preflight.md +8 -7
- package/config/skills/_shared/expected.schema.json +68 -0
- package/config/skills/_shared/finish-gate.py +12 -11
- package/config/skills/_shared/grok-anchor-guard.py +78 -0
- package/config/skills/_shared/invocation-receipt.py +1041 -0
- package/config/skills/_shared/judge-output-parser.py +200 -0
- package/config/skills/_shared/judge-role-evidence.py +273 -0
- package/config/skills/_shared/phase-prompt-render.py +181 -0
- package/config/skills/_shared/process-evidence.py +909 -0
- package/config/skills/_shared/resolve-bootstrap.py +776 -84
- package/config/skills/_shared/resolve-stop-hook.py +33 -1
- package/config/skills/_shared/role-config.py +552 -0
- package/config/skills/_shared/run-bounded.py +1 -1
- package/config/skills/_shared/spec-verify-check.py +1037 -192
- package/config/skills/_shared/state-phase-write.py +2551 -83
- package/config/skills/_shared/terminal-claim-check.py +15 -0
- package/config/skills/_shared/verify-merge-findings.py +1127 -50
- package/config/skills/devlyn:engines/SKILL.md +8 -16
- package/config/skills/devlyn:ideate/SKILL.md +1 -1
- package/config/skills/devlyn:ideate/references/spec-template.md +1 -0
- package/config/skills/devlyn:queue/SKILL.md +7 -4
- package/config/skills/devlyn:resolve/SKILL.md +94 -35
- package/config/skills/devlyn:resolve/references/outer-loop.md +15 -0
- package/config/skills/devlyn:resolve/references/phases/build-gate.md +33 -4
- package/config/skills/devlyn:resolve/references/phases/implement.md +4 -3
- 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 +120 -80
- package/config/skills/devlyn:resolve/references/state-schema.md +23 -9
- package/package.json +5 -1
- package/scripts/lint-fixtures.sh +4 -1
- package/scripts/lint-skills.sh +447 -290
package/bin/devlyn.js
CHANGED
|
@@ -16,6 +16,7 @@ const PKG = require('../package.json');
|
|
|
16
16
|
// ui-*) stay Claude-core-only, matching the pre-existing Codex bundle.
|
|
17
17
|
const DEVLYN_CORE_SKILLS = ['devlyn:resolve', 'devlyn:ideate', 'devlyn:design-ui', 'devlyn:engines', 'devlyn:queue', '_shared'];
|
|
18
18
|
const DEVLYN_SKILL_DIR_STAMP = '__DEVLYN_SKILL_DIR__';
|
|
19
|
+
const DEVLYN_INSTALL_MARKER = '.devlyn-install.json';
|
|
19
20
|
|
|
20
21
|
// Cross-agent shared skills directory read by BOTH oh-my-pi and Pi. Verified
|
|
21
22
|
// from the omp binary's skill-provider strings ("skills from .agents/skills —
|
|
@@ -354,6 +355,39 @@ function copyRecursive(src, dest, baseDir) {
|
|
|
354
355
|
}
|
|
355
356
|
}
|
|
356
357
|
|
|
358
|
+
function clearInstallMarker(skillsDir) {
|
|
359
|
+
fs.rmSync(path.join(skillsDir, DEVLYN_INSTALL_MARKER), { force: true });
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function assertCompleteSkillInstall(sourceSkillsDir, skillsDir, skillNames) {
|
|
363
|
+
const missing = skillNames.filter((skillName) =>
|
|
364
|
+
!fs.existsSync(path.join(sourceSkillsDir, skillName))
|
|
365
|
+
|| !fs.existsSync(path.join(skillsDir, skillName)));
|
|
366
|
+
if (missing.length > 0) {
|
|
367
|
+
throw new Error(`Incomplete devlyn skill install; missing: ${missing.join(', ')}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function writeInstallMarker(skillsDir) {
|
|
372
|
+
const markerPath = path.join(skillsDir, DEVLYN_INSTALL_MARKER);
|
|
373
|
+
const tempPath = `${markerPath}.${process.pid}.tmp`;
|
|
374
|
+
const marker = {
|
|
375
|
+
schemaVersion: 1,
|
|
376
|
+
package: PKG.name,
|
|
377
|
+
version: PKG.version,
|
|
378
|
+
};
|
|
379
|
+
try {
|
|
380
|
+
fs.writeFileSync(tempPath, JSON.stringify(marker, null, 2) + '\n', {
|
|
381
|
+
encoding: 'utf8',
|
|
382
|
+
flag: 'wx',
|
|
383
|
+
mode: 0o600,
|
|
384
|
+
});
|
|
385
|
+
fs.renameSync(tempPath, markerPath);
|
|
386
|
+
} finally {
|
|
387
|
+
fs.rmSync(tempPath, { force: true });
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
357
391
|
function shellDoubleQuoteDefault(value) {
|
|
358
392
|
if (value.includes('\n')) {
|
|
359
393
|
throw new Error(`Cannot stamp skill path with newline: ${value}`);
|
|
@@ -594,10 +628,10 @@ function installSkillsForCLI(cliKey) {
|
|
|
594
628
|
if (!cli || !cli.skillsDir || !cli.skillsToInstall) return 0;
|
|
595
629
|
|
|
596
630
|
const sourceSkillsDir = path.join(CONFIG_SOURCE, 'skills');
|
|
597
|
-
if (!fs.existsSync(sourceSkillsDir)) return 0;
|
|
598
631
|
if (!fs.existsSync(cli.skillsDir)) {
|
|
599
632
|
fs.mkdirSync(cli.skillsDir, { recursive: true });
|
|
600
633
|
}
|
|
634
|
+
clearInstallMarker(cli.skillsDir);
|
|
601
635
|
|
|
602
636
|
const removed = cleanupDeprecated(path.dirname(cli.skillsDir));
|
|
603
637
|
if (removed > 0) {
|
|
@@ -619,6 +653,8 @@ function installSkillsForCLI(cliKey) {
|
|
|
619
653
|
copied++;
|
|
620
654
|
log(` → ${cli.skillsDir.replace(os.homedir(), '~')}/${skillName}`, 'dim');
|
|
621
655
|
}
|
|
656
|
+
assertCompleteSkillInstall(sourceSkillsDir, cli.skillsDir, cli.skillsToInstall);
|
|
657
|
+
writeInstallMarker(cli.skillsDir);
|
|
622
658
|
return copied;
|
|
623
659
|
}
|
|
624
660
|
|
|
@@ -726,15 +762,20 @@ function installAgentsForAllDetected() {
|
|
|
726
762
|
// is chosen. Behavior is unchanged from the previous unconditional core install.
|
|
727
763
|
function installClaudeCore() {
|
|
728
764
|
const targetDir = getTargetDir();
|
|
765
|
+
const skillsDir = path.join(targetDir, 'skills');
|
|
729
766
|
log('\n📁 Installing Claude Code config to .claude/', 'green');
|
|
767
|
+
if (!fs.existsSync(skillsDir)) fs.mkdirSync(skillsDir, { recursive: true });
|
|
768
|
+
clearInstallMarker(skillsDir);
|
|
730
769
|
const refreshed = cleanManagedSkillDirs(
|
|
731
770
|
path.join(CONFIG_SOURCE, 'skills'),
|
|
732
|
-
|
|
771
|
+
skillsDir,
|
|
733
772
|
);
|
|
734
773
|
if (refreshed > 0) {
|
|
735
774
|
log(` 🔄 Refreshing ${refreshed} managed skill director${refreshed === 1 ? 'y' : 'ies'}`, 'dim');
|
|
736
775
|
}
|
|
737
776
|
copyRecursive(CONFIG_SOURCE, targetDir, targetDir);
|
|
777
|
+
assertCompleteSkillInstall(path.join(CONFIG_SOURCE, 'skills'), skillsDir, DEVLYN_CORE_SKILLS);
|
|
778
|
+
writeInstallMarker(skillsDir);
|
|
738
779
|
|
|
739
780
|
// Remove deprecated files from previous versions
|
|
740
781
|
const removed = cleanupDeprecated(targetDir);
|
|
@@ -751,17 +792,21 @@ function installClaudeCore() {
|
|
|
751
792
|
log(' → CLAUDE.md', 'dim');
|
|
752
793
|
}
|
|
753
794
|
|
|
754
|
-
//
|
|
795
|
+
// Keep installer-managed pipeline state and install metadata out of git.
|
|
755
796
|
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
756
|
-
const
|
|
797
|
+
const gitignoreEntries = ['.devlyn/', '.claude/skills/.devlyn-install.json'];
|
|
757
798
|
let gitignoreContent = fs.existsSync(gitignorePath)
|
|
758
799
|
? fs.readFileSync(gitignorePath, 'utf8')
|
|
759
800
|
: '';
|
|
760
|
-
|
|
801
|
+
const gitignoreLines = gitignoreContent.split('\n').map((line) => line.trim());
|
|
802
|
+
const missingGitignoreEntries = gitignoreEntries.filter((entry) =>
|
|
803
|
+
!gitignoreLines.includes(entry) && !(entry === '.devlyn/' && gitignoreLines.includes('.devlyn')));
|
|
804
|
+
if (missingGitignoreEntries.length > 0) {
|
|
761
805
|
const prefix = gitignoreContent && !gitignoreContent.endsWith('\n') ? '\n' : '';
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
806
|
+
const hasManagedHeader = gitignoreLines.includes('# devlyn-cli pipeline state');
|
|
807
|
+
const header = hasManagedHeader ? '' : gitignoreContent ? '\n# devlyn-cli pipeline state\n' : '# devlyn-cli pipeline state\n';
|
|
808
|
+
fs.writeFileSync(gitignorePath, gitignoreContent + prefix + header + missingGitignoreEntries.join('\n') + '\n');
|
|
809
|
+
log(` → .gitignore (added ${missingGitignoreEntries.join(', ')})`, 'dim');
|
|
765
810
|
}
|
|
766
811
|
|
|
767
812
|
// Enable agent teams in project settings
|
|
@@ -820,6 +865,11 @@ function installClaudeCore() {
|
|
|
820
865
|
settings.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1';
|
|
821
866
|
settingsChanged = true;
|
|
822
867
|
}
|
|
868
|
+
const bashMaxTimeoutMs = Number.parseInt(settings.env.BASH_MAX_TIMEOUT_MS, 10);
|
|
869
|
+
if (!Number.isFinite(bashMaxTimeoutMs) || bashMaxTimeoutMs < 3600000) {
|
|
870
|
+
settings.env.BASH_MAX_TIMEOUT_MS = '3600000';
|
|
871
|
+
settingsChanged = true;
|
|
872
|
+
}
|
|
823
873
|
if (!hasOwnSetting('hooks')) {
|
|
824
874
|
settings.hooks = {};
|
|
825
875
|
settingsChanged = true;
|
|
@@ -848,7 +898,7 @@ function installClaudeCore() {
|
|
|
848
898
|
}
|
|
849
899
|
if (settingsChanged) {
|
|
850
900
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
851
|
-
log(' → settings.json (agent teams + pipeline permissions + Stop hook)', 'dim');
|
|
901
|
+
log(' → settings.json (agent teams + one-hour Bash max + pipeline permissions + Stop hook)', 'dim');
|
|
852
902
|
}
|
|
853
903
|
|
|
854
904
|
// Configure global Claude Code settings (~/.claude/settings.json)
|
|
@@ -24,7 +24,7 @@ two targeted probes; the invoking phase sets `--effort`, pair-JUDGE uses
|
|
|
24
24
|
```bash
|
|
25
25
|
python3 "$DEVLYN_SHARED_DIR/run-bounded.py" 600 -- claude -p "<judge prompt>" \
|
|
26
26
|
--permission-mode dontAsk \
|
|
27
|
-
--
|
|
27
|
+
--tools "Read,Grep,Glob" --allowedTools "Read,Grep,Glob" \
|
|
28
28
|
--setting-sources project --strict-mcp-config --mcp-config '{"mcpServers":{}}' \
|
|
29
29
|
--effort medium \
|
|
30
30
|
> .devlyn/claude-judge.stdout 2> .devlyn/claude-judge.stderr
|
|
@@ -85,3 +85,11 @@ You interpret instructions literally and explicitly. The official guide is expli
|
|
|
85
85
|
3. **Overengineering**: do NOT add files, abstractions, error handling, validation, or "future flexibility" beyond what the spec asks. A bug fix doesn't need surrounding cleanup. The right complexity is the minimum needed for the current task.
|
|
86
86
|
|
|
87
87
|
You do NOT need stronger imperatives ("CRITICAL!", "YOU MUST!") to follow rules. Normal phrasing is sufficient.
|
|
88
|
+
|
|
89
|
+
## Explicit role capability
|
|
90
|
+
|
|
91
|
+
`role-config.py` validates explicit CLI judge effort against this bounded native capability declaration. Source: Claude Code2.1.263 built-in Fable5.1 metadata (native SHA ef5d2909c8af49f31ab6d5487e90316777bc2fac170adfe8160716caa8aaf4f9, byte157907500; effort/max/xhigh gates159068038/159068443/159068839). This is option support, not model fitness. Unknown version/model effort support fails with actionable unsupported-role-option. Model-only requests pass the exact ID to the native CLI without this effort table and still require matching native result evidence; engine-only defaults remain available. Native Agent workers have no validated exact-model/effort transport here, so explicit worker fields are rejected instead of switching channels.
|
|
92
|
+
|
|
93
|
+
<!-- devlyn-effort 2.1.263 claude-fable-5-1 low,medium,high,xhigh,max -->
|
|
94
|
+
|
|
95
|
+
For an explicit CLI judge profile, use the resolved `--model`/`--effort` in the bounded read-only invocation and add `--output-format json`. Follow the canonical resolve `JUDGE_STEM` round-scoped argv/prompt/raw capture and `judge-role-evidence.py` derivation contract. Only its exact derived result goes to the findings collector; never feed the JSON wrapper directly. Omitted options keep that role’s existing defaults.
|
|
@@ -14,6 +14,11 @@ Your default is efficient, direct, task-oriented. The canonical body specifies t
|
|
|
14
14
|
|
|
15
15
|
Resolve the request in the fewest useful tool loops without sacrificing correctness. For retrieval tasks: start with one broad search using short discriminative keywords; make another retrieval call only when the top results don't answer the core question or a required fact / parameter / source is missing. For tool-heavy tasks, start with a brief preamble: a one-line acknowledgment of the request and the first step you'll take.
|
|
16
16
|
|
|
17
|
+
For VERIFY JUDGE retrieval, batch related reads. After sealed parity proves a
|
|
18
|
+
canonical file and its `.agents` mirror are byte-identical, inspect the
|
|
19
|
+
canonical file only. Open self-test bodies or MECHANICAL raw streams only for a
|
|
20
|
+
named clause that remains unresolved after the broad pass.
|
|
21
|
+
|
|
17
22
|
## Validation pattern
|
|
18
23
|
|
|
19
24
|
Validation is concrete commands and tools, not self-belief. When the canonical body lists verification commands, execute them and trust their output. Do not substitute your judgment for a deterministic check the harness has provided. When validation tools are available (test runners, lint, type-check, the harness's `spec-verify-check.py`), run them before declaring success. The minimum evidence sufficient to answer correctly, cited precisely — then stop.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
executor: no
|
|
6
6
|
pair_judge: yes
|
|
7
7
|
|
|
8
|
-
Certification status: the pair-judge seat is wired,
|
|
8
|
+
Certification status: the pair-judge seat is wired, reachable, and probe-capable, but not emission-certified; isolation has three independent invariants — tool callability, MCP connection/enumeration, and context injection — and only P-0080-C can certify them; the doctor cannot. Before this change, the shipped recipe connected 65 MCP tools and injected both an MCP reminder and a plugin-skills reminder into the judge's conversation; a durable `pair grok` pin remains gated on follow-up emission registration and the standing seat-fitness rule.
|
|
9
9
|
|
|
10
10
|
## Invocation
|
|
11
11
|
|
|
@@ -24,10 +24,15 @@ escalation remains a reported solo skip).
|
|
|
24
24
|
#
|
|
25
25
|
# [skills]
|
|
26
26
|
# ignore = ["~/.agents", "~/.claude", "~/.cursor"]
|
|
27
|
+
# plus $ISO_HOME/hooks/anchor-guard.json. grok reads that file as JSON, not
|
|
28
|
+
# through a shell, so its "command" must carry an ALREADY-EXPANDED path:
|
|
29
|
+
# {"hooks":{"PreToolUse":[{"hooks":[{"type":"command",
|
|
30
|
+
# "command":"python3 /abs/path/to/_shared/grok-anchor-guard.py","timeout":10}]}]}}
|
|
27
31
|
# $NEUTRAL = empty dir outside any git repo
|
|
28
32
|
# $REPO = absolute repo root (redirects MUST be absolute — cwd is $NEUTRAL)
|
|
29
33
|
cd "$NEUTRAL"
|
|
30
|
-
GROK_HOME="$ISO_HOME" \
|
|
34
|
+
HOME="$NEUTRAL" ZDOTDIR="$NEUTRAL" GROK_HOME="$ISO_HOME" \
|
|
35
|
+
DEVLYN_PROBE_ANCHOR="<the bare anchor, derived exactly as the --allow rule below>" \
|
|
31
36
|
GROK_CLAUDE_MCPS_ENABLED=false GROK_CURSOR_MCPS_ENABLED=false \
|
|
32
37
|
GROK_CLAUDE_SKILLS_ENABLED=false GROK_CURSOR_SKILLS_ENABLED=false \
|
|
33
38
|
GROK_CLAUDE_HOOKS_ENABLED=false GROK_CURSOR_HOOKS_ENABLED=false \
|
|
@@ -37,13 +42,18 @@ python3 "$DEVLYN_SHARED_DIR/run-bounded.py" 600 -- grok -p "<judge prompt>" \
|
|
|
37
42
|
--permission-mode dontAsk --no-memory \
|
|
38
43
|
--tools "read_file,grep,list_dir,run_terminal_cmd" \
|
|
39
44
|
--disallowed-tools "Agent,use_tool,search_tool" \
|
|
40
|
-
--allow 'Bash(<
|
|
45
|
+
--allow 'Bash(<the bare anchor>)' \
|
|
41
46
|
--reasoning-effort medium \
|
|
47
|
+
--output-format streaming-messages-json \
|
|
42
48
|
> "$REPO/.devlyn/grok-judge.stdout" 2> "$REPO/.devlyn/grok-judge.stderr"
|
|
43
49
|
```
|
|
44
50
|
|
|
45
51
|
Copy `auth.json` into `$ISO_HOME` for every run and seed its `config.toml` with
|
|
46
|
-
the shown `[plugins]` and `[skills] ignore` entries
|
|
52
|
+
the shown `[plugins]` and `[skills] ignore` entries and its
|
|
53
|
+
`hooks/anchor-guard.json` with the shown `PreToolUse` entry. Before launch, create
|
|
54
|
+
`$NEUTRAL/.zshenv` containing `export HOME=<the operator's real home>`.
|
|
55
|
+
Both overrides are needed because grok scans `~/.claude.json` from `$HOME`,
|
|
56
|
+
while zsh reads rc files from `$ZDOTDIR`, which is exported in some environments.
|
|
47
57
|
`$NEUTRAL` and `$ISO_HOME` must be on paths that do not embed a project or repo identifier.
|
|
48
58
|
`--permission-mode plan` is forbidden headless because it silently blocks
|
|
49
59
|
all tools and returns an empty review; `--always-approve` is banned because it
|
|
@@ -51,8 +61,11 @@ auto-approves writes. `--tools` and `--disallowed-tools` are headless-only (the
|
|
|
51
61
|
TUI warns and ignores them); when both are present, the disallow list wins and
|
|
52
62
|
removes tools entirely rather than merely gating execution.
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
Never use `--json-schema` for the pair judge: measured runs degraded the
|
|
65
|
+
tool-use loop (2/3 made zero tool calls and fabricated findings), and a
|
|
66
|
+
`Cancelled` run emitted a PASS-shaped payload after its schema had failed.
|
|
67
|
+
|
|
68
|
+
The judge prompt must name the exact permitted command(s). Derive the prompt
|
|
56
69
|
anchor and allow-list string identically from one source, in this precedence:
|
|
57
70
|
the solo-headroom hypothesis's backticked observable command when present;
|
|
58
71
|
otherwise the backticked commands in the spec's `## Verification` bullets;
|
|
@@ -60,18 +73,29 @@ otherwise the repo's existing test/CLI runner. If none exists, the probe
|
|
|
60
73
|
obligation does not arise and a static review is correct for that spec.
|
|
61
74
|
The documented `Bash(<command>)` form matches the exact command or its prefix;
|
|
62
75
|
the trailing ` *` form fails a no-argument command.
|
|
76
|
+
Under `dontAsk`, every allow shape tested for a chained command — including an
|
|
77
|
+
exact full-string rule for the chain — returned `PermissionCancelled`, so the
|
|
78
|
+
mandatory dominance-loss anchor must run unchained with the allow rule scoped
|
|
79
|
+
to that bare anchor.
|
|
63
80
|
|
|
64
|
-
Grok's deny semantics are not Claude-parity:
|
|
65
|
-
truncates the review at exit 0.
|
|
66
|
-
`
|
|
67
|
-
|
|
81
|
+
Grok's deny semantics are not Claude-parity: reaching the permission mode's
|
|
82
|
+
auto-deny silently truncates the review at exit 0. On the configured, measured
|
|
83
|
+
path the `PreToolUse` hook above keeps the judge from reaching it — the hook runs
|
|
84
|
+
at step 1, before the rules and before the mode policy, and returns the denial as
|
|
85
|
+
a model-visible `tool_result`.
|
|
86
|
+
It admits the bare anchor and anchor-plus-argv, and vetoes any character that
|
|
87
|
+
could start a second command, so `verify.md`'s bounded input variations survive
|
|
88
|
+
while chaining does not. The hook fails open, so the collector rejection remains
|
|
89
|
+
the fail-closed floor: treat it as `BLOCKED` for
|
|
90
|
+
`verify.pair.emission-contract`, never PASS. After a non-timeout spawn, run:
|
|
68
91
|
|
|
69
92
|
```bash
|
|
70
93
|
python3 "$DEVLYN_SHARED_DIR/collect-codex-findings.py" \
|
|
71
94
|
--devlyn-dir "$REPO/.devlyn" \
|
|
72
|
-
--stdout-file grok-judge.stdout
|
|
73
|
-
--summary-out grok-judge.summary.json
|
|
95
|
+
--stdout-file grok-judge.stdout
|
|
74
96
|
```
|
|
75
97
|
|
|
76
98
|
On exit 124, write `.devlyn/verify.pair.timeout.json` with
|
|
77
|
-
`{"engine": "grok", "budget_seconds": 600}` before merge.
|
|
99
|
+
`{"engine": "grok", "budget_seconds": 600}` before merge. A budget abort can
|
|
100
|
+
leave the stream truncated before its terminal `result` record; that capture
|
|
101
|
+
stays `TIMEOUT` at merge, never a pair PASS.
|