devlyn-cli 4.3.23 → 4.3.25

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/bin/engines.js CHANGED
@@ -4,10 +4,11 @@ const ENGINE_IDS = Object.freeze([
4
4
  'claude', 'codex', 'grok', 'omp', 'antigravity', 'cursor', 'opencode', 'kimi', 'copilot', 'qwen',
5
5
  ]);
6
6
 
7
- // The versions each channel was examined on. Newer releases of the same major are taken as
8
- // compatible: these CLIs ship minor releases weekly, and gating on the examined minor would turn
9
- // the harness off at every one of them. A new major (or a Cursor build older than the examined one)
10
- // is refused, since any flag may change there.
7
+ // The versions each channel was examined on. Any newer release, a new major included, is taken as
8
+ // compatible: a version number is not evidence that a channel broke (omp 18 kept both flags devlyn
9
+ // uses), and refusing unexamined majors turned working engines off. Only a version below the examined
10
+ // floor (or a Cursor build older than the examined one) is refused; an unreadable version is refused
11
+ // with its own reason.
11
12
  const ENGINE_CAPABILITIES = Object.freeze({
12
13
  claude: { minimum: '2.1.307', executor: true },
13
14
  codex: { minimum: '0.163.0', executor: true },
@@ -21,20 +22,21 @@ const ENGINE_CAPABILITIES = Object.freeze({
21
22
  qwen: { minimum: '0.26.0', executor: false },
22
23
  });
23
24
 
25
+ function readableVersion(engine, version) {
26
+ return typeof version === 'string'
27
+ && (engine === 'cursor' ? /^\d{4}\.\d{2}\.\d{2}-[0-9a-f]+$/ : /^\d+\.\d+\.\d+$/).test(version);
28
+ }
29
+
24
30
  function supportedVersion(engine, version) {
31
+ if (!readableVersion(engine, version)) return false;
25
32
  const capability = ENGINE_CAPABILITIES[engine];
26
- if (engine === 'cursor') {
27
- const build = typeof version === 'string' && version.match(/^(\d{4})\.(\d{2})\.(\d{2})-[0-9a-f]+$/);
28
- return Boolean(build) && build.slice(1).join('') >= capability.minimum.slice(0, 10).replace(/\./g, '');
29
- }
30
- if (typeof version !== 'string' || !/^\d+\.\d+\.\d+$/.test(version)) return false;
33
+ if (engine === 'cursor') return version.slice(0, 10).replace(/\./g, '') >= capability.minimum.slice(0, 10).replace(/\./g, '');
31
34
  const actual = version.split('.').map(Number);
32
35
  const floor = capability.minimum.split('.').map(Number);
33
- if (actual[0] !== floor[0]) return false;
34
- for (let i = 1; i < 3; i++) {
36
+ for (let i = 0; i < 3; i++) {
35
37
  if (actual[i] !== floor[i]) return actual[i] > floor[i];
36
38
  }
37
39
  return true;
38
40
  }
39
41
 
40
- module.exports = { ENGINE_IDS, ENGINE_CAPABILITIES, supportedVersion };
42
+ module.exports = { ENGINE_IDS, ENGINE_CAPABILITIES, readableVersion, supportedVersion };
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const os = require('os');
3
3
  const path = require('path');
4
- const { ENGINE_IDS, ENGINE_CAPABILITIES, supportedVersion } = require('./engines');
4
+ const { ENGINE_IDS, ENGINE_CAPABILITIES, readableVersion, supportedVersion } = require('./engines');
5
5
  const { verifyRelease, sha256, fail, options, HarnessError } = require('./release');
6
6
  const { fingerprint } = require('./skill-ownership');
7
7
  const history = require('./skill-history.json');
@@ -301,7 +301,9 @@ function planActivation(releaseRoot, input, env = process.env) {
301
301
  if (off) return finish(plan);
302
302
  plan.release = release;
303
303
  if (!supported) {
304
- const reasons = [reason('engine-version-unsupported', { minimum: ENGINE_CAPABILITIES[context.engine].minimum })];
304
+ const reasons = [readableVersion(context.engine, context.version)
305
+ ? reason('engine-version-unsupported', { minimum: ENGINE_CAPABILITIES[context.engine].minimum })
306
+ : reason('engine-version-unreadable')];
305
307
  status.principles = channel('unavailable', null, reasons);
306
308
  status.skills = channel('unavailable', null, reasons);
307
309
  return finish(plan);
@@ -312,7 +314,8 @@ function planActivation(releaseRoot, input, env = process.env) {
312
314
  status.skills = channel('partial', null, reasons);
313
315
  return finish(plan);
314
316
  }
315
- const defaults = context.committedBlockApplies ? '' : fs.readFileSync(path.join(release.root, 'config/skills/_shared/runtime-principles.md'), 'utf8');
317
+ // The selected release is the one source of principles; a committed project block never suppresses it.
318
+ const defaults = fs.readFileSync(path.join(release.root, 'config/skills/_shared/runtime-principles.md'), 'utf8');
316
319
  const catalog = skillCatalog(release);
317
320
  const instructions = joinText(defaults, catalog);
318
321
  const file = (relativePath, utf8) => {
@@ -327,7 +330,7 @@ function planActivation(releaseRoot, input, env = process.env) {
327
330
  plan.files.push({ relativePath, utf8, sha256: sha256(utf8) });
328
331
  return path.join(context.runDir, relativePath);
329
332
  };
330
- const principles = (mechanism) => { status.principles = channel('configured', context.committedBlockApplies ? 'native-project' : mechanism); };
333
+ const principles = (mechanism) => { status.principles = channel('configured', mechanism); };
331
334
  const fallback = (reasons = []) => {
332
335
  status.skills = channel('partial', 'absolute-catalog', [...reasons, reason('skills-catalog')]);
333
336
  status.invocation = { kind: 'read-file', text: `Read ${path.join(release.root, 'config/skills/devlyn-ideate/SKILL.md')}` };
@@ -419,16 +422,14 @@ function planActivation(releaseRoot, input, env = process.env) {
419
422
  break;
420
423
  }
421
424
  case 'copilot': {
422
- if (!context.committedBlockApplies) {
423
- const directory = path.join(context.runDir, 'copilot');
424
- if (directory.includes(',')) {
425
- status.principles = channel('unavailable', null, [reason('principles-unavailable', { detail: 'comma-in-path' })]);
426
- } else {
427
- file('copilot/devlyn.instructions.md', '---\napplyTo: "**"\n---\n' + instructions);
428
- const dirs = config.instructionsDirs ?? env.COPILOT_CUSTOM_INSTRUCTIONS_DIRS ?? '';
429
- plan.envPatch.COPILOT_CUSTOM_INSTRUCTIONS_DIRS = dirs.split(',').includes(directory) ? dirs : (dirs ? `${dirs},${directory}` : directory);
430
- principles('COPILOT_CUSTOM_INSTRUCTIONS_DIRS');
431
- }
425
+ const directory = path.join(context.runDir, 'copilot');
426
+ if (directory.includes(',')) {
427
+ status.principles = channel('unavailable', null, [reason('principles-unavailable', { detail: 'comma-in-path' })]);
428
+ } else {
429
+ file('copilot/devlyn.instructions.md', '---\napplyTo: "**"\n---\n' + instructions);
430
+ const dirs = config.instructionsDirs ?? env.COPILOT_CUSTOM_INSTRUCTIONS_DIRS ?? '';
431
+ plan.envPatch.COPILOT_CUSTOM_INSTRUCTIONS_DIRS = dirs.split(',').includes(directory) ? dirs : (dirs ? `${dirs},${directory}` : directory);
432
+ principles('COPILOT_CUSTOM_INSTRUCTIONS_DIRS');
432
433
  }
433
434
  const root = path.join(release.root, 'config/skills');
434
435
  if (root.includes(',') || collisions.length || errors.length) {
@@ -464,7 +465,6 @@ function planActivation(releaseRoot, input, env = process.env) {
464
465
  } else status.skills = channel('partial', null, [reason('skills-unavailable', { detail: 'no-initial-task' })]);
465
466
  break;
466
467
  }
467
- if (context.committedBlockApplies) status.principles = channel('configured', 'native-project');
468
468
  if (plan.engineArgs.length || Object.keys(plan.envPatch).length || plan.prompt) plan.envPatch.DEVLYN_HARNESS_ROOT = release.root;
469
469
  return finish(plan);
470
470
  }
@@ -24,7 +24,7 @@ function managedBlock(template, eol = '\n') {
24
24
  }
25
25
 
26
26
  function sessionDefaults(template) {
27
- return 'The following Devlyn instructions are defaults for this session. Applicable explicit user instructions and native owner/project instructions take precedence over these defaults. This delegation applies only to the Devlyn defaults below; it does not change other engine instructions or permissions.\n\n'
27
+ return 'The following Devlyn instructions are defaults for this session. Explicit user and native owner/project instructions take precedence, except a Devlyn managed block (devlyn:instructions markers), which these defaults replace. They change no other engine instructions or permissions.\n\n'
28
28
  + normalize(template);
29
29
  }
30
30
 
@@ -58,6 +58,7 @@
58
58
  "45ef27af9ec27089e2861c13b882521fc6bf073dc6b7a356b326b4a27c5e6e24",
59
59
  "4685be6a94ad5587ce53785c14a60cccf9a852d9d23391dba0a5bcd0c64931cf",
60
60
  "484db9dd2635de5f5acbcf692e2f329f33c0a483951e27bcd77bcbafbc052e19",
61
+ "491fab4aca617e7e1c9735addfe4b0f249192a4c14c43a85225e65b99031c89b",
61
62
  "4ecb004b24a8adfe41c7416c3fa24e62cf76baa3b276ae1ce5091caef77e57b8",
62
63
  "4fed91aed4dc1cd28a9e07229dbe9b7f6d8127e5f763dde8f76bb1b6f6855cce",
63
64
  "514bfaf5667af3c9d4d36f391e6567a5eb759c6dfb06a5c3ea23cf29692844ef",
@@ -346,6 +347,7 @@
346
347
  "1d8c3bd31c8b755acd0c3c9f416acb5024dfa99fbf5e2c14d8d6df00a24fe9b4",
347
348
  "300d1af60cc53285935dc4b5b370e157707218d844a4303b96d7826eef36ed43",
348
349
  "302b1a6c109087dce02ed6af184d6aeeca6cecfd40ebe638beecf8bdf21cf59c",
350
+ "338a2b5ddde0f09444040c45922716663b1df76ec1a6c97ebdeb770b9e3ede49",
349
351
  "33cf196ba7dd2d96df119707d874317e6005f1323fc2cae677c53e2f134f9ff4",
350
352
  "36b58dbdc26e51abc05a60872c4582faeebd67e9fe5626a3933467ba07c68dc4",
351
353
  "40237cd5259dbcf7c490f130e844a9e32eea48a8edabd50800edee5029bf95a4",
@@ -800,6 +802,8 @@
800
802
  "4.3.21",
801
803
  "4.3.22",
802
804
  "4.3.23",
805
+ "4.3.24",
806
+ "4.3.25",
803
807
  "4.3.3",
804
808
  "4.3.4",
805
809
  "4.3.5",
@@ -1,4 +1,4 @@
1
- The following Devlyn instructions are defaults for this session. Applicable explicit user instructions and native owner/project instructions take precedence over these defaults. This delegation applies only to the Devlyn defaults below; it does not change other engine instructions or permissions.
1
+ The following Devlyn instructions are defaults for this session. Explicit user and native owner/project instructions take precedence, except a Devlyn managed block (devlyn:instructions markers), which these defaults replace. They change no other engine instructions or permissions.
2
2
 
3
3
  # Project Instructions
4
4
 
@@ -116,7 +116,7 @@ Follow [loop.md](references/loop.md). The executor is the configured route, the
116
116
  Another engine needs an argv that starts one fresh non-interactive session able, without prompts, to edit, run commands and commit in its task worktree and to write the packet's submission file. `<prompt>` carries the absolute binding values, while `{packet}` stays literal for the drain to fill:
117
117
 
118
118
  ```text
119
- Execute the devlyn loop task packet {packet}: read its workerPrompt when present; read methodology.defaults.path when named and apply those verified defaults, with explicit user and native owner/project instructions taking precedence; work only in its owned worktree under the installed instructions; meet its obligations, then write its submission and any review records as "Executor exchange" and "Review records" in <DEVLYN_SKILL_DIR>/references/loop.md specify. Use the literal DEVLYN_SKILL_DIR and DEVLYN_SHARED_DIR bindings in workerPrompt when present; otherwise set DEVLYN_SKILL_DIR=<DEVLYN_SKILL_DIR> and DEVLYN_SHARED_DIR=<DEVLYN_SHARED_DIR> from these literal values. Never use inherited environment values. Never push, open a pull request or merge: the drain delivers.
119
+ Execute the devlyn loop task packet {packet}: read its workerPrompt when present; read methodology.defaults.path when named and apply those verified defaults, with explicit user and native owner/project instructions taking precedence, except a Devlyn managed block (devlyn:instructions markers), which those defaults replace; work only in its owned worktree under the installed instructions; meet its obligations, then write its submission and any review records as "Executor exchange" and "Review records" in <DEVLYN_SKILL_DIR>/references/loop.md specify. Use the literal DEVLYN_SKILL_DIR and DEVLYN_SHARED_DIR bindings in workerPrompt when present; otherwise set DEVLYN_SKILL_DIR=<DEVLYN_SKILL_DIR> and DEVLYN_SHARED_DIR=<DEVLYN_SHARED_DIR> from these literal values. Never use inherited environment values. Never push, open a pull request or merge: the drain delivers.
120
120
  ```
121
121
 
122
122
  ```sh
@@ -59,7 +59,7 @@ Package cleanup, parent pruning and report failures appear as diagnostics under
59
59
 
60
60
  `queue.py inspect --repo <dir> --json` reads without cleanup, locks or writes. It returns the status summary plus `binding` (null for legacy) and `compatibility`: `compatible`, `legacy`, `reasons`, `recoveryArgv`, `taskPacket` ranges and `queueBinding` ranges. Incompatible or unreadable state is reported, never migrated. Apps should use this reader instead of `status`, which still reconciles cleanup. For a mismatch, `recoveryArgv` names the bound queue’s inspect invocation; use the same bound queue entry point for authorized add/drain operations.
61
61
 
62
- Codex projection writers place `.devlyn-harness-projection.json` at the copied plugin root, beside `skills` and `.codex-plugin`: `{schemaVersion: 1, release: <ReleaseRef>, source: "native/codex/plugins/devlyn"}`. The reader verifies the named release and every projected file’s bytes/mode against that subtree (excluding only the marker). Symlinked skill invocations resolve through their real path. Standalone installs and existing unfinished unbound queues retain legacy behavior; new legacy packets use v2 with a null release, and their `defaults` name the invoking installation's own `_shared/runtime-principles.md` (`source: "skill"`). No engine applicability is inferred from instruction filenames: bound queues use verified release defaults with native instructions taking precedence. Worker and runner paths point to the bound release, independent of a projection cache’s lifetime.
62
+ Codex projection writers place `.devlyn-harness-projection.json` at the copied plugin root, beside `skills` and `.codex-plugin`: `{schemaVersion: 1, release: <ReleaseRef>, source: "native/codex/plugins/devlyn"}`. The reader verifies the named release and every projected file’s bytes/mode against that subtree (excluding only the marker). Symlinked skill invocations resolve through their real path. Standalone installs and existing unfinished unbound queues retain legacy behavior; new legacy packets use v2 with a null release, and their `defaults` name the invoking installation's own `_shared/runtime-principles.md` (`source: "skill"`). No engine applicability is inferred from instruction filenames: bound queues use verified release defaults with native instructions taking precedence, except a Devlyn managed block, which the defaults replace. Worker and runner paths point to the bound release, independent of a projection cache’s lifetime.
63
63
 
64
64
  ## Executor exchange
65
65
 
@@ -75,7 +75,7 @@ The packet (`<receipt dir>/packet.json`) carries everything a fresh executor nee
75
75
  | `shared_constraints` | The meta-prompt's `Constraints and exclusions`. |
76
76
  | `dependencies` | `{task, receipt, source_sha}` of each accepted prerequisite. |
77
77
  | `schema_version`, `harness` | New packets use version 2 and the bound release reference, or null for legacy unbound queues. Existing v1 packets execute unchanged. |
78
- | `methodology` | `projectInstructions`: `{path, sha256}` of instruction bytes at the inputs commit. `defaults`: `{source: "release", path, sha256}` from the verified binding; for an unbound queue `{source: "skill", path, sha256}` of the installation's own `_shared/runtime-principles.md`. A task whose executor would get no principles at all (no defaults and no committed CLAUDE.md/AGENTS.md) waits with that reason instead of running. Read the named defaults; explicit user and native owner/project instructions take precedence. No native engine applicability is inferred from filenames. |
78
+ | `methodology` | `projectInstructions`: `{path, sha256}` of instruction bytes at the inputs commit. `defaults`: `{source: "release", path, sha256}` from the verified binding; for an unbound queue `{source: "skill", path, sha256}` of the installation's own `_shared/runtime-principles.md`. A task whose executor would get no principles at all (no defaults and no committed CLAUDE.md/AGENTS.md) waits with that reason instead of running. Read the named defaults; explicit user and native owner/project instructions take precedence, except a Devlyn managed block (devlyn:instructions markers), which the defaults replace. No native engine applicability is inferred from filenames. |
79
79
  | `workerPrompt` | `{path, sha256}` of the task’s worker instructions, or null for legacy unbound queues. Read it before working. |
80
80
  | `delivery` | `local-only`, `auto` or `pr`. |
81
81
  | `evidence_dir`, `runner` | Where loop evidence lives (`<worktree>/.devlyn/loop`, ignored by Git) and the runner argv. |
@@ -1215,7 +1215,7 @@ def ensure_packet(v, row, path, receipt):
1215
1215
  packet["runner"][1] = str(bound_skills / "devlyn-ideate/scripts/acceptance.py")
1216
1216
  prompt = path.parent / "worker-prompt.md"
1217
1217
  body = (f"Execute task packet {packet_path}. Work only in {worktree} on its owned branch.\n"
1218
- f"Read {binding['defaults']['path']} and apply it as defaults; explicit user and native owner/project instructions win.\n"
1218
+ f"Read {binding['defaults']['path']} and apply it as defaults; explicit user and native owner/project instructions win, except a Devlyn managed block (devlyn:instructions markers), which these defaults replace.\n"
1219
1219
  f"Set DEVLYN_SKILL_DIR={bound_skills / 'devlyn-ideate'} and DEVLYN_SHARED_DIR={bound_skills / '_shared'}.\n"
1220
1220
  f"Follow Executor exchange and Review records in {bound_skills / 'devlyn-ideate/references/loop.md'}.\n"
1221
1221
  f"Meet the packet obligations, run its runner and write {packet['submission']}. Never push, open a pull request or merge.\n")
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devlyn",
3
- "version": "4.3.23",
3
+ "version": "4.3.25",
4
4
  "description": "Devlyn harness skills",
5
5
  "skills": "./skills/"
6
6
  }
@@ -1,4 +1,4 @@
1
- The following Devlyn instructions are defaults for this session. Applicable explicit user instructions and native owner/project instructions take precedence over these defaults. This delegation applies only to the Devlyn defaults below; it does not change other engine instructions or permissions.
1
+ The following Devlyn instructions are defaults for this session. Explicit user and native owner/project instructions take precedence, except a Devlyn managed block (devlyn:instructions markers), which these defaults replace. They change no other engine instructions or permissions.
2
2
 
3
3
  # Project Instructions
4
4
 
@@ -116,7 +116,7 @@ Follow [loop.md](references/loop.md). The executor is the configured route, the
116
116
  Another engine needs an argv that starts one fresh non-interactive session able, without prompts, to edit, run commands and commit in its task worktree and to write the packet's submission file. `<prompt>` carries the absolute binding values, while `{packet}` stays literal for the drain to fill:
117
117
 
118
118
  ```text
119
- Execute the devlyn loop task packet {packet}: read its workerPrompt when present; read methodology.defaults.path when named and apply those verified defaults, with explicit user and native owner/project instructions taking precedence; work only in its owned worktree under the installed instructions; meet its obligations, then write its submission and any review records as "Executor exchange" and "Review records" in <DEVLYN_SKILL_DIR>/references/loop.md specify. Use the literal DEVLYN_SKILL_DIR and DEVLYN_SHARED_DIR bindings in workerPrompt when present; otherwise set DEVLYN_SKILL_DIR=<DEVLYN_SKILL_DIR> and DEVLYN_SHARED_DIR=<DEVLYN_SHARED_DIR> from these literal values. Never use inherited environment values. Never push, open a pull request or merge: the drain delivers.
119
+ Execute the devlyn loop task packet {packet}: read its workerPrompt when present; read methodology.defaults.path when named and apply those verified defaults, with explicit user and native owner/project instructions taking precedence, except a Devlyn managed block (devlyn:instructions markers), which those defaults replace; work only in its owned worktree under the installed instructions; meet its obligations, then write its submission and any review records as "Executor exchange" and "Review records" in <DEVLYN_SKILL_DIR>/references/loop.md specify. Use the literal DEVLYN_SKILL_DIR and DEVLYN_SHARED_DIR bindings in workerPrompt when present; otherwise set DEVLYN_SKILL_DIR=<DEVLYN_SKILL_DIR> and DEVLYN_SHARED_DIR=<DEVLYN_SHARED_DIR> from these literal values. Never use inherited environment values. Never push, open a pull request or merge: the drain delivers.
120
120
  ```
121
121
 
122
122
  ```sh
@@ -59,7 +59,7 @@ Package cleanup, parent pruning and report failures appear as diagnostics under
59
59
 
60
60
  `queue.py inspect --repo <dir> --json` reads without cleanup, locks or writes. It returns the status summary plus `binding` (null for legacy) and `compatibility`: `compatible`, `legacy`, `reasons`, `recoveryArgv`, `taskPacket` ranges and `queueBinding` ranges. Incompatible or unreadable state is reported, never migrated. Apps should use this reader instead of `status`, which still reconciles cleanup. For a mismatch, `recoveryArgv` names the bound queue’s inspect invocation; use the same bound queue entry point for authorized add/drain operations.
61
61
 
62
- Codex projection writers place `.devlyn-harness-projection.json` at the copied plugin root, beside `skills` and `.codex-plugin`: `{schemaVersion: 1, release: <ReleaseRef>, source: "native/codex/plugins/devlyn"}`. The reader verifies the named release and every projected file’s bytes/mode against that subtree (excluding only the marker). Symlinked skill invocations resolve through their real path. Standalone installs and existing unfinished unbound queues retain legacy behavior; new legacy packets use v2 with a null release, and their `defaults` name the invoking installation's own `_shared/runtime-principles.md` (`source: "skill"`). No engine applicability is inferred from instruction filenames: bound queues use verified release defaults with native instructions taking precedence. Worker and runner paths point to the bound release, independent of a projection cache’s lifetime.
62
+ Codex projection writers place `.devlyn-harness-projection.json` at the copied plugin root, beside `skills` and `.codex-plugin`: `{schemaVersion: 1, release: <ReleaseRef>, source: "native/codex/plugins/devlyn"}`. The reader verifies the named release and every projected file’s bytes/mode against that subtree (excluding only the marker). Symlinked skill invocations resolve through their real path. Standalone installs and existing unfinished unbound queues retain legacy behavior; new legacy packets use v2 with a null release, and their `defaults` name the invoking installation's own `_shared/runtime-principles.md` (`source: "skill"`). No engine applicability is inferred from instruction filenames: bound queues use verified release defaults with native instructions taking precedence, except a Devlyn managed block, which the defaults replace. Worker and runner paths point to the bound release, independent of a projection cache’s lifetime.
63
63
 
64
64
  ## Executor exchange
65
65
 
@@ -75,7 +75,7 @@ The packet (`<receipt dir>/packet.json`) carries everything a fresh executor nee
75
75
  | `shared_constraints` | The meta-prompt's `Constraints and exclusions`. |
76
76
  | `dependencies` | `{task, receipt, source_sha}` of each accepted prerequisite. |
77
77
  | `schema_version`, `harness` | New packets use version 2 and the bound release reference, or null for legacy unbound queues. Existing v1 packets execute unchanged. |
78
- | `methodology` | `projectInstructions`: `{path, sha256}` of instruction bytes at the inputs commit. `defaults`: `{source: "release", path, sha256}` from the verified binding; for an unbound queue `{source: "skill", path, sha256}` of the installation's own `_shared/runtime-principles.md`. A task whose executor would get no principles at all (no defaults and no committed CLAUDE.md/AGENTS.md) waits with that reason instead of running. Read the named defaults; explicit user and native owner/project instructions take precedence. No native engine applicability is inferred from filenames. |
78
+ | `methodology` | `projectInstructions`: `{path, sha256}` of instruction bytes at the inputs commit. `defaults`: `{source: "release", path, sha256}` from the verified binding; for an unbound queue `{source: "skill", path, sha256}` of the installation's own `_shared/runtime-principles.md`. A task whose executor would get no principles at all (no defaults and no committed CLAUDE.md/AGENTS.md) waits with that reason instead of running. Read the named defaults; explicit user and native owner/project instructions take precedence, except a Devlyn managed block (devlyn:instructions markers), which the defaults replace. No native engine applicability is inferred from filenames. |
79
79
  | `workerPrompt` | `{path, sha256}` of the task’s worker instructions, or null for legacy unbound queues. Read it before working. |
80
80
  | `delivery` | `local-only`, `auto` or `pr`. |
81
81
  | `evidence_dir`, `runner` | Where loop evidence lives (`<worktree>/.devlyn/loop`, ignored by Git) and the runner argv. |
@@ -1215,7 +1215,7 @@ def ensure_packet(v, row, path, receipt):
1215
1215
  packet["runner"][1] = str(bound_skills / "devlyn-ideate/scripts/acceptance.py")
1216
1216
  prompt = path.parent / "worker-prompt.md"
1217
1217
  body = (f"Execute task packet {packet_path}. Work only in {worktree} on its owned branch.\n"
1218
- f"Read {binding['defaults']['path']} and apply it as defaults; explicit user and native owner/project instructions win.\n"
1218
+ f"Read {binding['defaults']['path']} and apply it as defaults; explicit user and native owner/project instructions win, except a Devlyn managed block (devlyn:instructions markers), which these defaults replace.\n"
1219
1219
  f"Set DEVLYN_SKILL_DIR={bound_skills / 'devlyn-ideate'} and DEVLYN_SHARED_DIR={bound_skills / '_shared'}.\n"
1220
1220
  f"Follow Executor exchange and Review records in {bound_skills / 'devlyn-ideate/references/loop.md'}.\n"
1221
1221
  f"Meet the packet obligations, run its runner and write {packet['submission']}. Never push, open a pull request or merge.\n")
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "devlyn-harness-v4-3-23",
2
+ "name": "devlyn-harness-v4-3-25",
3
3
  "plugins": [
4
4
  {
5
5
  "name": "devlyn",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devlyn",
3
- "version": "4.3.23",
3
+ "version": "4.3.25",
4
4
  "description": "Devlyn harness skills",
5
5
  "skills": "./skills/"
6
6
  }
@@ -1,4 +1,4 @@
1
- The following Devlyn instructions are defaults for this session. Applicable explicit user instructions and native owner/project instructions take precedence over these defaults. This delegation applies only to the Devlyn defaults below; it does not change other engine instructions or permissions.
1
+ The following Devlyn instructions are defaults for this session. Explicit user and native owner/project instructions take precedence, except a Devlyn managed block (devlyn:instructions markers), which these defaults replace. They change no other engine instructions or permissions.
2
2
 
3
3
  # Project Instructions
4
4
 
@@ -116,7 +116,7 @@ Follow [loop.md](references/loop.md). The executor is the configured route, the
116
116
  Another engine needs an argv that starts one fresh non-interactive session able, without prompts, to edit, run commands and commit in its task worktree and to write the packet's submission file. `<prompt>` carries the absolute binding values, while `{packet}` stays literal for the drain to fill:
117
117
 
118
118
  ```text
119
- Execute the devlyn loop task packet {packet}: read its workerPrompt when present; read methodology.defaults.path when named and apply those verified defaults, with explicit user and native owner/project instructions taking precedence; work only in its owned worktree under the installed instructions; meet its obligations, then write its submission and any review records as "Executor exchange" and "Review records" in <DEVLYN_SKILL_DIR>/references/loop.md specify. Use the literal DEVLYN_SKILL_DIR and DEVLYN_SHARED_DIR bindings in workerPrompt when present; otherwise set DEVLYN_SKILL_DIR=<DEVLYN_SKILL_DIR> and DEVLYN_SHARED_DIR=<DEVLYN_SHARED_DIR> from these literal values. Never use inherited environment values. Never push, open a pull request or merge: the drain delivers.
119
+ Execute the devlyn loop task packet {packet}: read its workerPrompt when present; read methodology.defaults.path when named and apply those verified defaults, with explicit user and native owner/project instructions taking precedence, except a Devlyn managed block (devlyn:instructions markers), which those defaults replace; work only in its owned worktree under the installed instructions; meet its obligations, then write its submission and any review records as "Executor exchange" and "Review records" in <DEVLYN_SKILL_DIR>/references/loop.md specify. Use the literal DEVLYN_SKILL_DIR and DEVLYN_SHARED_DIR bindings in workerPrompt when present; otherwise set DEVLYN_SKILL_DIR=<DEVLYN_SKILL_DIR> and DEVLYN_SHARED_DIR=<DEVLYN_SHARED_DIR> from these literal values. Never use inherited environment values. Never push, open a pull request or merge: the drain delivers.
120
120
  ```
121
121
 
122
122
  ```sh
@@ -59,7 +59,7 @@ Package cleanup, parent pruning and report failures appear as diagnostics under
59
59
 
60
60
  `queue.py inspect --repo <dir> --json` reads without cleanup, locks or writes. It returns the status summary plus `binding` (null for legacy) and `compatibility`: `compatible`, `legacy`, `reasons`, `recoveryArgv`, `taskPacket` ranges and `queueBinding` ranges. Incompatible or unreadable state is reported, never migrated. Apps should use this reader instead of `status`, which still reconciles cleanup. For a mismatch, `recoveryArgv` names the bound queue’s inspect invocation; use the same bound queue entry point for authorized add/drain operations.
61
61
 
62
- Codex projection writers place `.devlyn-harness-projection.json` at the copied plugin root, beside `skills` and `.codex-plugin`: `{schemaVersion: 1, release: <ReleaseRef>, source: "native/codex/plugins/devlyn"}`. The reader verifies the named release and every projected file’s bytes/mode against that subtree (excluding only the marker). Symlinked skill invocations resolve through their real path. Standalone installs and existing unfinished unbound queues retain legacy behavior; new legacy packets use v2 with a null release, and their `defaults` name the invoking installation's own `_shared/runtime-principles.md` (`source: "skill"`). No engine applicability is inferred from instruction filenames: bound queues use verified release defaults with native instructions taking precedence. Worker and runner paths point to the bound release, independent of a projection cache’s lifetime.
62
+ Codex projection writers place `.devlyn-harness-projection.json` at the copied plugin root, beside `skills` and `.codex-plugin`: `{schemaVersion: 1, release: <ReleaseRef>, source: "native/codex/plugins/devlyn"}`. The reader verifies the named release and every projected file’s bytes/mode against that subtree (excluding only the marker). Symlinked skill invocations resolve through their real path. Standalone installs and existing unfinished unbound queues retain legacy behavior; new legacy packets use v2 with a null release, and their `defaults` name the invoking installation's own `_shared/runtime-principles.md` (`source: "skill"`). No engine applicability is inferred from instruction filenames: bound queues use verified release defaults with native instructions taking precedence, except a Devlyn managed block, which the defaults replace. Worker and runner paths point to the bound release, independent of a projection cache’s lifetime.
63
63
 
64
64
  ## Executor exchange
65
65
 
@@ -75,7 +75,7 @@ The packet (`<receipt dir>/packet.json`) carries everything a fresh executor nee
75
75
  | `shared_constraints` | The meta-prompt's `Constraints and exclusions`. |
76
76
  | `dependencies` | `{task, receipt, source_sha}` of each accepted prerequisite. |
77
77
  | `schema_version`, `harness` | New packets use version 2 and the bound release reference, or null for legacy unbound queues. Existing v1 packets execute unchanged. |
78
- | `methodology` | `projectInstructions`: `{path, sha256}` of instruction bytes at the inputs commit. `defaults`: `{source: "release", path, sha256}` from the verified binding; for an unbound queue `{source: "skill", path, sha256}` of the installation's own `_shared/runtime-principles.md`. A task whose executor would get no principles at all (no defaults and no committed CLAUDE.md/AGENTS.md) waits with that reason instead of running. Read the named defaults; explicit user and native owner/project instructions take precedence. No native engine applicability is inferred from filenames. |
78
+ | `methodology` | `projectInstructions`: `{path, sha256}` of instruction bytes at the inputs commit. `defaults`: `{source: "release", path, sha256}` from the verified binding; for an unbound queue `{source: "skill", path, sha256}` of the installation's own `_shared/runtime-principles.md`. A task whose executor would get no principles at all (no defaults and no committed CLAUDE.md/AGENTS.md) waits with that reason instead of running. Read the named defaults; explicit user and native owner/project instructions take precedence, except a Devlyn managed block (devlyn:instructions markers), which the defaults replace. No native engine applicability is inferred from filenames. |
79
79
  | `workerPrompt` | `{path, sha256}` of the task’s worker instructions, or null for legacy unbound queues. Read it before working. |
80
80
  | `delivery` | `local-only`, `auto` or `pr`. |
81
81
  | `evidence_dir`, `runner` | Where loop evidence lives (`<worktree>/.devlyn/loop`, ignored by Git) and the runner argv. |
@@ -1215,7 +1215,7 @@ def ensure_packet(v, row, path, receipt):
1215
1215
  packet["runner"][1] = str(bound_skills / "devlyn-ideate/scripts/acceptance.py")
1216
1216
  prompt = path.parent / "worker-prompt.md"
1217
1217
  body = (f"Execute task packet {packet_path}. Work only in {worktree} on its owned branch.\n"
1218
- f"Read {binding['defaults']['path']} and apply it as defaults; explicit user and native owner/project instructions win.\n"
1218
+ f"Read {binding['defaults']['path']} and apply it as defaults; explicit user and native owner/project instructions win, except a Devlyn managed block (devlyn:instructions markers), which these defaults replace.\n"
1219
1219
  f"Set DEVLYN_SKILL_DIR={bound_skills / 'devlyn-ideate'} and DEVLYN_SHARED_DIR={bound_skills / '_shared'}.\n"
1220
1220
  f"Follow Executor exchange and Review records in {bound_skills / 'devlyn-ideate/references/loop.md'}.\n"
1221
1221
  f"Meet the packet obligations, run its runner and write {packet['submission']}. Never push, open a pull request or merge.\n")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devlyn-cli",
3
- "version": "4.3.23",
3
+ "version": "4.3.25",
4
4
  "description": "Loop & harness engineering for AI coding agents (Claude Code, Codex, and any AGENTS.md-driven agent) — core engineering principles for CLAUDE.md/AGENTS.md and a loop designer that splits one intent into tasks agents drain hands-free",
5
5
  "homepage": "https://github.com/fysoul17/devlyn-cli#readme",
6
6
  "bin": {
package/release.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "package": "devlyn-cli",
4
- "version": "4.3.23",
4
+ "version": "4.3.25",
5
5
  "principles": {
6
6
  "defaults": "config/skills/_shared/runtime-principles.md",
7
7
  "memory": {
@@ -134,12 +134,12 @@
134
134
  },
135
135
  {
136
136
  "path": "bin/engines.js",
137
- "sha256": "4761b73b9117dc2935180c0ad46f4a39649ba22156903d683ce6984ed086af4d",
137
+ "sha256": "f36fe59473e050a6d53fde7642058c67b4f3353816d62042f0fe6c1004715bfa",
138
138
  "executable": false
139
139
  },
140
140
  {
141
141
  "path": "bin/harness-activation.js",
142
- "sha256": "fecb47b04e7f6e72735a9ea50b6aa524eca611d42c6db1b03c1260ddf2259bbc",
142
+ "sha256": "b864c9e325e3e7bd6f800e51d456cb00d40925d6f0713a0c692cff207a9c6183",
143
143
  "executable": false
144
144
  },
145
145
  {
@@ -154,7 +154,7 @@
154
154
  },
155
155
  {
156
156
  "path": "bin/instructions.js",
157
- "sha256": "b04f60f4d925b9361f2d0221a12f125dd51f6e95075cf302803c69f6252aa27e",
157
+ "sha256": "b47b8748f7138788f9f39d62feaf71ea0db7d91d67ee6fc911d5abeb99939b5b",
158
158
  "executable": false
159
159
  },
160
160
  {
@@ -164,7 +164,7 @@
164
164
  },
165
165
  {
166
166
  "path": "bin/skill-history.json",
167
- "sha256": "3e5e0838642b335fc2b5f83b612aba576adad8ff5cac3b0bd045f25621f074db",
167
+ "sha256": "c137de72e6ca67b53800f1a5f777f44b00dcbdfa1e036ccf38174177f5fed69e",
168
168
  "executable": false
169
169
  },
170
170
  {
@@ -244,7 +244,7 @@
244
244
  },
245
245
  {
246
246
  "path": "config/skills/_shared/runtime-principles.md",
247
- "sha256": "475e02133dce22b4d71fde86f4493453e10dea48c791f6f2cca124ebb205687c",
247
+ "sha256": "ca6e84c50e8fbe22acdd447f012ff894e0ebe72ba381de33d9a94c18dfa9cc19",
248
248
  "executable": false
249
249
  },
250
250
  {
@@ -284,7 +284,7 @@
284
284
  },
285
285
  {
286
286
  "path": "config/skills/devlyn-ideate/SKILL.md",
287
- "sha256": "cf0d1f11475809471876cd4e909930d6032316c25b46859a26d6ab6e851631b3",
287
+ "sha256": "2901fa7cb69d41fd3321d3846b742b3bc1a77c760eb49bd874c4c745b550553d",
288
288
  "executable": false
289
289
  },
290
290
  {
@@ -294,7 +294,7 @@
294
294
  },
295
295
  {
296
296
  "path": "config/skills/devlyn-ideate/references/loop.md",
297
- "sha256": "5217e576537bf9a24d5069da8a07f2793840e4d896bc970a34f4413d749c1c91",
297
+ "sha256": "8dd5ac20d9513e9c0b309a02170deec8642ae5945f1fb082ccc8b04fbc2422bc",
298
298
  "executable": false
299
299
  },
300
300
  {
@@ -314,7 +314,7 @@
314
314
  },
315
315
  {
316
316
  "path": "config/skills/devlyn-ideate/scripts/queue.py",
317
- "sha256": "b7482e53fa309aa5c4e242dab3f1ae7cd73db0964d1c53029edd09a055e051d5",
317
+ "sha256": "afd2f9fc8e5be73641cfa46a9de40c38b573dff6b22714d1457babe04f5c1b2f",
318
318
  "executable": false
319
319
  },
320
320
  {
@@ -334,7 +334,7 @@
334
334
  },
335
335
  {
336
336
  "path": "native/claude/.claude-plugin/plugin.json",
337
- "sha256": "eae924c1ac79ca2f8e755b070cf26d95cef1679efca9a5d33985a4a855df30fb",
337
+ "sha256": "f2d0eb5a6bccfe88a6e5b8a5117f309f41f724d00acdeff4eac8a1b310ca907b",
338
338
  "executable": false
339
339
  },
340
340
  {
@@ -409,7 +409,7 @@
409
409
  },
410
410
  {
411
411
  "path": "native/claude/skills/_shared/runtime-principles.md",
412
- "sha256": "475e02133dce22b4d71fde86f4493453e10dea48c791f6f2cca124ebb205687c",
412
+ "sha256": "ca6e84c50e8fbe22acdd447f012ff894e0ebe72ba381de33d9a94c18dfa9cc19",
413
413
  "executable": false
414
414
  },
415
415
  {
@@ -449,7 +449,7 @@
449
449
  },
450
450
  {
451
451
  "path": "native/claude/skills/devlyn-ideate/SKILL.md",
452
- "sha256": "cf0d1f11475809471876cd4e909930d6032316c25b46859a26d6ab6e851631b3",
452
+ "sha256": "2901fa7cb69d41fd3321d3846b742b3bc1a77c760eb49bd874c4c745b550553d",
453
453
  "executable": false
454
454
  },
455
455
  {
@@ -459,7 +459,7 @@
459
459
  },
460
460
  {
461
461
  "path": "native/claude/skills/devlyn-ideate/references/loop.md",
462
- "sha256": "5217e576537bf9a24d5069da8a07f2793840e4d896bc970a34f4413d749c1c91",
462
+ "sha256": "8dd5ac20d9513e9c0b309a02170deec8642ae5945f1fb082ccc8b04fbc2422bc",
463
463
  "executable": false
464
464
  },
465
465
  {
@@ -479,17 +479,17 @@
479
479
  },
480
480
  {
481
481
  "path": "native/claude/skills/devlyn-ideate/scripts/queue.py",
482
- "sha256": "b7482e53fa309aa5c4e242dab3f1ae7cd73db0964d1c53029edd09a055e051d5",
482
+ "sha256": "afd2f9fc8e5be73641cfa46a9de40c38b573dff6b22714d1457babe04f5c1b2f",
483
483
  "executable": false
484
484
  },
485
485
  {
486
486
  "path": "native/codex/.agents/plugins/marketplace.json",
487
- "sha256": "4552486a2a3c2af4d75c2b1bd4cbd1d3d29429dd08aa983c7558f55dd2208661",
487
+ "sha256": "3fcaa2f62b36f4aab8475d2ab10910ba0d6a55045a152399d358e2abbf2f5934",
488
488
  "executable": false
489
489
  },
490
490
  {
491
491
  "path": "native/codex/plugins/devlyn/.codex-plugin/plugin.json",
492
- "sha256": "eae924c1ac79ca2f8e755b070cf26d95cef1679efca9a5d33985a4a855df30fb",
492
+ "sha256": "f2d0eb5a6bccfe88a6e5b8a5117f309f41f724d00acdeff4eac8a1b310ca907b",
493
493
  "executable": false
494
494
  },
495
495
  {
@@ -564,7 +564,7 @@
564
564
  },
565
565
  {
566
566
  "path": "native/codex/plugins/devlyn/skills/_shared/runtime-principles.md",
567
- "sha256": "475e02133dce22b4d71fde86f4493453e10dea48c791f6f2cca124ebb205687c",
567
+ "sha256": "ca6e84c50e8fbe22acdd447f012ff894e0ebe72ba381de33d9a94c18dfa9cc19",
568
568
  "executable": false
569
569
  },
570
570
  {
@@ -604,7 +604,7 @@
604
604
  },
605
605
  {
606
606
  "path": "native/codex/plugins/devlyn/skills/devlyn-ideate/SKILL.md",
607
- "sha256": "cf0d1f11475809471876cd4e909930d6032316c25b46859a26d6ab6e851631b3",
607
+ "sha256": "2901fa7cb69d41fd3321d3846b742b3bc1a77c760eb49bd874c4c745b550553d",
608
608
  "executable": false
609
609
  },
610
610
  {
@@ -614,7 +614,7 @@
614
614
  },
615
615
  {
616
616
  "path": "native/codex/plugins/devlyn/skills/devlyn-ideate/references/loop.md",
617
- "sha256": "5217e576537bf9a24d5069da8a07f2793840e4d896bc970a34f4413d749c1c91",
617
+ "sha256": "8dd5ac20d9513e9c0b309a02170deec8642ae5945f1fb082ccc8b04fbc2422bc",
618
618
  "executable": false
619
619
  },
620
620
  {
@@ -634,7 +634,7 @@
634
634
  },
635
635
  {
636
636
  "path": "native/codex/plugins/devlyn/skills/devlyn-ideate/scripts/queue.py",
637
- "sha256": "b7482e53fa309aa5c4e242dab3f1ae7cd73db0964d1c53029edd09a055e051d5",
637
+ "sha256": "afd2f9fc8e5be73641cfa46a9de40c38b573dff6b22714d1457babe04f5c1b2f",
638
638
  "executable": false
639
639
  },
640
640
  {
@@ -1779,12 +1779,12 @@
1779
1779
  },
1780
1780
  {
1781
1781
  "path": "package.json",
1782
- "sha256": "13b3d6b03f048bdbc3daa6269e76da455c99b50fab0dee664d757dc782888bd2",
1782
+ "sha256": "2448478a5c2d95bc1cddde475d3d091942f4685c982ca5e52a7d1a267636fd4c",
1783
1783
  "executable": false
1784
1784
  }
1785
1785
  ],
1786
1786
  "digest": {
1787
1787
  "algorithm": "sha256",
1788
- "value": "5293f7baeca5d357b186fd05ac546e8679e045cfe2b420c56bf52cd9bac298e6"
1788
+ "value": "2a0d645e703b3aa32324d5890cbb83d6bf9174074e12dbf3f16e00c2f1ba7cb1"
1789
1789
  }
1790
1790
  }