devlyn-cli 3.3.0 → 4.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 +8 -8
- package/CLAUDE.md +17 -17
- package/README.md +47 -34
- package/bin/devlyn.js +104 -67
- package/bin/instruction-templates.json +26 -0
- package/config/skills/_shared/adapters/README.md +1 -1
- package/config/skills/_shared/adapters/omp.md +2 -2
- package/config/skills/_shared/archive_run.py +2 -2
- package/config/skills/_shared/codex-config.md +3 -3
- package/config/skills/_shared/engine-doctor.sh +1 -1
- package/config/skills/_shared/engine-preflight.md +5 -5
- package/config/skills/_shared/finish-gate.py +1 -1
- package/config/skills/_shared/resolve-bootstrap.py +2 -2
- package/config/skills/_shared/runtime-principles.md +6 -6
- package/config/skills/_shared/spec-verify-check.py +3 -3
- package/config/skills/_shared/task-complete.py +32 -14
- package/config/skills/{devlyn:design-ui → devlyn-design-ui}/SKILL.md +1 -1
- package/config/skills/{devlyn:engines → devlyn-engines}/SKILL.md +2 -2
- package/config/skills/{devlyn:ideate → devlyn-ideate}/SKILL.md +10 -10
- package/config/skills/{devlyn:ideate → devlyn-ideate}/references/elicitation.md +2 -2
- package/config/skills/{devlyn:ideate → devlyn-ideate}/references/from-spec-mode.md +2 -2
- package/config/skills/{devlyn:ideate → devlyn-ideate}/references/project-mode.md +5 -5
- package/config/skills/{devlyn:ideate → devlyn-ideate}/references/spec-template.md +1 -1
- package/config/skills/{devlyn:queue → devlyn-queue}/SKILL.md +6 -6
- package/config/skills/{devlyn:resolve → devlyn-resolve}/SKILL.md +4 -4
- package/config/skills/{devlyn:resolve → devlyn-resolve}/references/free-form-mode.md +3 -3
- package/config/skills/{devlyn:resolve → devlyn-resolve}/references/outer-loop.md +1 -1
- package/config/skills/{devlyn:resolve → devlyn-resolve}/references/state-schema.md +1 -1
- package/config/skills/{devlyn:resolve → devlyn-resolve}/references/task-completion.md +5 -5
- package/config/templates/prompt-templates.md +2 -2
- package/optional-skills/{devlyn:pencil-pull → devlyn-pencil-pull}/SKILL.md +1 -1
- package/optional-skills/{devlyn:pencil-push → devlyn-pencil-push}/SKILL.md +1 -1
- package/optional-skills/{devlyn:reap → devlyn-reap}/SKILL.md +5 -5
- package/optional-skills/{devlyn:reap → devlyn-reap}/scripts/reap.sh +1 -1
- package/optional-skills/{devlyn:reap → devlyn-reap}/scripts/scan.sh +1 -1
- package/package.json +3 -1
- /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/decision.md +0 -0
- /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/roadmap.md +0 -0
- /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/vision.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/build-gate.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/cleanup.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/implement.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/plan.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/probe-derive.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/surface-close.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/verify.md +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Deterministic PHASE-0 bootstrap for /devlyn
|
|
2
|
+
"""Deterministic PHASE-0 bootstrap for /devlyn-resolve."""
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import contextlib
|
|
@@ -1027,7 +1027,7 @@ def self_test() -> int:
|
|
|
1027
1027
|
"phases": {name: None for name in PHASE_NAMES},
|
|
1028
1028
|
"verify": {"coverage_failed": False, "pair_trigger": None},
|
|
1029
1029
|
}
|
|
1030
|
-
schema_dirs = [script_shared.parent / name for name in ("devlyn
|
|
1030
|
+
schema_dirs = [script_shared.parent / name for name in ("devlyn-resolve",)
|
|
1031
1031
|
if (script_shared.parent / name).is_dir()]
|
|
1032
1032
|
assert len(schema_dirs) == 1, schema_dirs
|
|
1033
1033
|
schema = (schema_dirs[0] / "references" / "state-schema.md").read_text(encoding="utf-8")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Runtime principles — sub-agent contract
|
|
2
2
|
|
|
3
|
-
The runtime contract every sub-agent inside `/devlyn
|
|
3
|
+
The runtime contract every sub-agent inside `/devlyn-resolve` (PLAN / IMPLEMENT / BUILD_GATE / CLEANUP / VERIFY) and `/devlyn-ideate` must satisfy. Source of truth for sub-agent behavior on user tasks. NOT for autoresearch-loop / harness-developer concerns (see `autoresearch/PRINCIPLES.md`).
|
|
4
4
|
|
|
5
5
|
The three sections below mirror the corresponding CLAUDE.md sections (Subtractive-first editing, Goal-locked execution, Evidence over claim). No-workaround discipline lives in CLAUDE.md Core Principle #1 only — it does not have a runtime-mirror block. Each mirrored section is wrapped in `<!-- runtime-principles:section=NAME:begin -->` / `:end -->` markers in BOTH this file and CLAUDE.md; lint Check 12 extracts each named block from both files and diffs to detect drift.
|
|
6
6
|
|
|
@@ -58,7 +58,7 @@ This rule exists because LLMs (including you) are trained to be helpful, compreh
|
|
|
58
58
|
|
|
59
59
|
**The single drift test before any deviation from the stated goal:** *"Did the user ask for this, OR does the user's stated goal strictly require it?"* If the answer to both is no, do not do it. Surface it as a note (commit message, end-of-turn summary, finding) and continue on the original path.
|
|
60
60
|
|
|
61
|
-
**Creative-mode is the narrow exception, not the default.** Creative-mode applies only when (a) the user explicitly invoked an ideation/exploration surface (`/devlyn
|
|
61
|
+
**Creative-mode is the narrow exception, not the default.** Creative-mode applies only when (a) the user explicitly invoked an ideation/exploration surface (`/devlyn-ideate`, `/devlyn-design-ui`, "let's brainstorm", "explore options for"), OR (b) the goal is genuinely under-specified and a clarifying question is impossible (extremely rare — usually you should ask). For everything else — bug fixes, feature work, refactors, doc updates, pipeline runs, code review, debugging — execution-mode is the default and drift is a defect, not a feature.
|
|
62
62
|
|
|
63
63
|
**Anti-rationalization clause** — explicitly guarding against LLM hedging:
|
|
64
64
|
|
|
@@ -67,7 +67,7 @@ This rule exists because LLMs (including you) are trained to be helpful, compreh
|
|
|
67
67
|
- "It would be incomplete without this" is **not** a justification. The user defines completeness, not your sense of it.
|
|
68
68
|
- "I'm being thorough" is **not** a justification. Thoroughness on the requested goal is required; thoroughness extending past the goal is drift.
|
|
69
69
|
|
|
70
|
-
**When in doubt, ask — outside hands-free pipelines.** In interactive sessions a short clarification ("the requested fix touches the X code path; I notice Y also looks broken — should I fix it in this change or surface it as a follow-up?") is always cheaper than a wrong-scope diff. Asking is not a weakness; silently expanding scope is. **Inside hands-free pipelines** (`/devlyn
|
|
70
|
+
**When in doubt, ask — outside hands-free pipelines.** In interactive sessions a short clarification ("the requested fix touches the X code path; I notice Y also looks broken — should I fix it in this change or surface it as a follow-up?") is always cheaper than a wrong-scope diff. Asking is not a weakness; silently expanding scope is. **Inside hands-free pipelines** (`/devlyn-resolve`, scheduled remote agents, autonomous skill runs) the contract forbids mid-pipeline prompts — there asking is unsafe because there is no user to answer. The substitute is: stay strictly on the requested goal, do not expand scope, and log the question/assumption explicitly in the final report (or `.devlyn/runs/<run_id>/` artifacts) so the user can adjudicate after the run completes. Choosing scope creep over logging-and-staying-on-path is always wrong.
|
|
71
71
|
|
|
72
72
|
**Stopping rule.** A task is done when the user's stated goal is closed AND no off-path work was added. If you find yourself hesitating because "I should also do Z" — Z is drift. Note it for follow-up, do not execute.
|
|
73
73
|
<!-- runtime-principles:section=goal-locked:end -->
|
|
@@ -82,7 +82,7 @@ Every finding cites concrete evidence. Vague claims are speculation; exclude the
|
|
|
82
82
|
- **Doc findings**: quote of the stale text + section/line reference.
|
|
83
83
|
- **Browser findings**: screenshot reference + URL/route.
|
|
84
84
|
|
|
85
|
-
**Negative existence claims** ("X lacks Y", "X cannot Z", "X is Y-specific") are the highest-risk shape — they feel like recall but fail to any single counter-example. They require active search at write time, not absence-of-memory. This rule applies to conversational answers and comparison-table cells, not only `/devlyn
|
|
85
|
+
**Negative existence claims** ("X lacks Y", "X cannot Z", "X is Y-specific") are the highest-risk shape — they feel like recall but fail to any single counter-example. They require active search at write time, not absence-of-memory. This rule applies to conversational answers and comparison-table cells, not only `/devlyn-resolve` findings — every cell of a trade-off table is a falsifiable claim.
|
|
86
86
|
|
|
87
87
|
**A position reversal is itself a claim.** In an oracle-less debate — design, strategy, trade-off, any decision with no spec or verifier to check against — changing your mind after a critique requires a NAMED DELTA: cite the specific prior claim, evidence, or criterion that changed, not a post-hoc rationale invented to justify a flip you were already going to make. Before reversing or choosing between contested positions, state each side's strongest form and the decisive criterion; the chosen outcome may adopt one side wholesale — synthesis means the best decision, not a forced blend. Flipping to whoever spoke last without a cited delta is capitulation, not reasoning; genuinely unresolved disagreement is escalated to the user, never closed by deferring to the last speaker. When you commission an adversarial review, require the critic to return the strongest counter, the strongest form of your own position, AND a synthesis — a refute-only mandate produces debate, not better decisions.
|
|
88
88
|
|
|
@@ -94,8 +94,8 @@ A finding without one of these forms is excluded. Vague findings produce vague f
|
|
|
94
94
|
## Consumption
|
|
95
95
|
|
|
96
96
|
**Consumers**:
|
|
97
|
-
- `devlyn
|
|
98
|
-
- `devlyn
|
|
97
|
+
- `devlyn-resolve/SKILL.md` `<harness_principles>` points here as the contract source. Phase prompt bodies inline the operational excerpt needed for each phase.
|
|
98
|
+
- `devlyn-ideate/SKILL.md` consumes this file for spec-shaping and conversation discipline through its own `<harness_principles>` block.
|
|
99
99
|
|
|
100
100
|
**Codex routing**: Codex-routed phases must inline the contract excerpt directly into the prompt body. Bounded read-only Codex critique, probe, or judge calls must also follow `_shared/codex-config.md` isolation rules.
|
|
101
101
|
<!-- runtime-principles:consumption:end -->
|
|
@@ -48,7 +48,7 @@ Check mode (`--check <markdown_path>`):
|
|
|
48
48
|
its own inline validation and must not use this sibling-precedence route.
|
|
49
49
|
|
|
50
50
|
Expected-contract check mode (`--check-expected <json_path>`):
|
|
51
|
-
- Used by /devlyn
|
|
51
|
+
- Used by /devlyn-ideate after writing sibling `spec.expected.json`.
|
|
52
52
|
- Exits 0 if the file is valid JSON and matches `_shared/expected.schema.json`
|
|
53
53
|
shape, and if sibling `spec.md` has supported `complexity` frontmatter.
|
|
54
54
|
Exits 2 on unreadable, malformed, unsupported fields, or unsupported sibling
|
|
@@ -605,7 +605,7 @@ def validate_expected_shape(data) -> str | None:
|
|
|
605
605
|
"""Return None if shape matches the sibling spec.expected.json schema.
|
|
606
606
|
|
|
607
607
|
Keep this dependency-free: it mirrors `_shared/expected.schema.json` enough
|
|
608
|
-
to catch malformed ideate output before /devlyn
|
|
608
|
+
to catch malformed ideate output before /devlyn-resolve consumes it.
|
|
609
609
|
"""
|
|
610
610
|
if not isinstance(data, dict):
|
|
611
611
|
return "top-level must be a JSON object"
|
|
@@ -4667,7 +4667,7 @@ def main() -> int:
|
|
|
4667
4667
|
external_diff,
|
|
4668
4668
|
fix_hint=(
|
|
4669
4669
|
"Remove `.devlyn/external-diff.patch` for ordinary runs, or use "
|
|
4670
|
-
"`/devlyn
|
|
4670
|
+
"`/devlyn-resolve --verify-only <diff-or-PR-ref> --spec <path>` only when "
|
|
4671
4671
|
"intentionally verifying an external patch."
|
|
4672
4672
|
),
|
|
4673
4673
|
)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""Outer-owner delivery and recoverable cleanup (task-completion spec, R1–7).
|
|
3
3
|
|
|
4
4
|
No phase/state writer, staging, scheduler, or implicit adoption. See
|
|
5
|
-
devlyn
|
|
5
|
+
devlyn-resolve/references/task-completion.md for the owner acceptance contract.
|
|
6
6
|
"""
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
@@ -172,7 +172,7 @@ def policy(receipt, override):
|
|
|
172
172
|
require(config.returncode == 1 or config.stdout in {"auto\n", "pr\n"}, "invalid local devlyn.completionMode; use auto|pr")
|
|
173
173
|
value = config.stdout.strip() if config.returncode == 0 else None
|
|
174
174
|
require(override is None or override in {"auto", "pr"}, "invalid task mode; use auto|pr")
|
|
175
|
-
return override or value or "
|
|
175
|
+
return override or value or "auto"
|
|
176
176
|
|
|
177
177
|
|
|
178
178
|
def allocate(args):
|
|
@@ -685,8 +685,6 @@ def complete(args):
|
|
|
685
685
|
inspect_workspace(current)
|
|
686
686
|
bind_acceptance(receipt, path, args.acceptance)
|
|
687
687
|
info = repo_policy(receipt)
|
|
688
|
-
if mode == "auto":
|
|
689
|
-
require(info["mergeCommitAllowed"], "repository disallows merge commits; use pr mode or resolve policy with repository owner")
|
|
690
688
|
if receipt.get("pr_number"):
|
|
691
689
|
pr = json.loads(gh(receipt, "pr", "view", str(receipt["pr_number"]), "--json", PR_FIELDS))
|
|
692
690
|
else:
|
|
@@ -725,12 +723,18 @@ def complete(args):
|
|
|
725
723
|
inspect_workspace(receipt)
|
|
726
724
|
verify_files(Path(receipt["worktree"]), receipt["files"])
|
|
727
725
|
require(remote_head(receipt, receipt["branch"]) == receipt["publish_sha"], "remote head race before merge")
|
|
726
|
+
refused = None
|
|
728
727
|
if not pr.get("autoMergeRequest"):
|
|
729
|
-
|
|
728
|
+
try:
|
|
729
|
+
require(info["mergeCommitAllowed"], "repository disallows merge commits")
|
|
730
|
+
gh(receipt, "pr", "merge", str(pr["number"]), "--auto", "--merge", "--match-head-commit", receipt["publish_sha"])
|
|
731
|
+
except CompletionError as error:
|
|
732
|
+
refused = str(error)
|
|
730
733
|
pr = json.loads(gh(receipt, "pr", "view", str(pr["number"]), "--json", PR_FIELDS))
|
|
731
734
|
validate_pr(receipt, pr)
|
|
732
735
|
if pr["state"] != "MERGED":
|
|
733
|
-
|
|
736
|
+
# Merge only when the repository allows it; a refused request leaves the PR for a person.
|
|
737
|
+
return dict(result("PR"), merge_refused=refused) if refused and not pr.get("autoMergeRequest") else result("PENDING")
|
|
734
738
|
receipt["merge"] = pr
|
|
735
739
|
atomic_json(path, receipt)
|
|
736
740
|
cleanup(receipt, path, pr)
|
|
@@ -1242,7 +1246,7 @@ class CompletionTests(unittest.TestCase):
|
|
|
1242
1246
|
self.g("config", "url." + alias + ".insteadOf", intended)
|
|
1243
1247
|
self.allocate(); self.accept()
|
|
1244
1248
|
self.assertEqual(json.loads(self.receipt.read_text(encoding="utf-8"))["remote_url"], {"literal": intended, "fetch": alias, "push": alias})
|
|
1245
|
-
result, _ = self.complete()
|
|
1249
|
+
result, _ = self.complete("--mode", "pr")
|
|
1246
1250
|
self.assertEqual(result["status"], "PR")
|
|
1247
1251
|
self.assertEqual(self.g("ls-remote", "origin", "refs/heads/task/fixture").split()[0], self.sha)
|
|
1248
1252
|
d = json.loads(self.data.read_text(encoding="utf-8"))
|
|
@@ -1374,6 +1378,15 @@ class CompletionTests(unittest.TestCase):
|
|
|
1374
1378
|
self.assertEqual(result["status"], "COMPLETE")
|
|
1375
1379
|
self.assertFalse(self.task.exists())
|
|
1376
1380
|
|
|
1381
|
+
@unittest.skipUnless(sys.platform == "darwin" or sys.platform.startswith("linux"), "writer observation requires POSIX")
|
|
1382
|
+
def test_default_mode_merges_when_possible_and_cleans(self):
|
|
1383
|
+
self.allocate(); self.accept()
|
|
1384
|
+
result, _ = self.complete("--writers-stopped")
|
|
1385
|
+
self.assertEqual(result["status"], "COMPLETE")
|
|
1386
|
+
self.assertEqual(json.loads(self.data.read_text(encoding="utf-8")).get("merges"), 1)
|
|
1387
|
+
self.assertFalse(self.task.exists())
|
|
1388
|
+
self.assertEqual(self.g("branch", "--list", "task/fixture"), "")
|
|
1389
|
+
|
|
1377
1390
|
def test_release_is_refused_from_inside_the_task_tree(self):
|
|
1378
1391
|
self.allocate(); self.accept()
|
|
1379
1392
|
_, r = self.complete("--mode", "pr", "--writers-stopped", cwd=self.task, success=False)
|
|
@@ -1710,10 +1723,12 @@ class CompletionTests(unittest.TestCase):
|
|
|
1710
1723
|
|
|
1711
1724
|
def test_interrupted_external_effects(self):
|
|
1712
1725
|
self.allocate(); self.accept()
|
|
1713
|
-
for effect in ("push", "create"
|
|
1726
|
+
for effect in ("push", "create"):
|
|
1714
1727
|
self.configure(**{"interrupt_"+effect: True})
|
|
1715
1728
|
_, r = self.complete("--writers-stopped", "--mode", "auto", success=False)
|
|
1716
1729
|
self.assertNotEqual(r.returncode, 0)
|
|
1730
|
+
# A merge command that fails after merging is judged by the PR state, not its exit code.
|
|
1731
|
+
self.configure(interrupt_merge=True)
|
|
1717
1732
|
result, _ = self.complete("--writers-stopped")
|
|
1718
1733
|
self.assertEqual(result["status"], "COMPLETE")
|
|
1719
1734
|
d = json.loads(self.data.read_text(encoding="utf-8"))
|
|
@@ -1801,16 +1816,19 @@ class CompletionTests(unittest.TestCase):
|
|
|
1801
1816
|
def test_policy_and_local_only_persist(self):
|
|
1802
1817
|
self.allocate(); self.accept()
|
|
1803
1818
|
self.configure(merge_allowed=False)
|
|
1804
|
-
|
|
1805
|
-
self.
|
|
1806
|
-
|
|
1819
|
+
result, _ = self.complete()
|
|
1820
|
+
self.assertEqual((result["status"], result["merge_refused"]), ("PR", "repository disallows merge commits"))
|
|
1821
|
+
d = json.loads(self.data.read_text(encoding="utf-8"))
|
|
1822
|
+
self.assertEqual((d.get("pushs", 0), d.get("merges", 0)), (1, 0))
|
|
1807
1823
|
self.complete("--mode", "pr")
|
|
1808
1824
|
result, _ = self.complete()
|
|
1809
1825
|
self.assertEqual(result["status"], "PR")
|
|
1826
|
+
self.assertNotIn("merge_refused", result)
|
|
1810
1827
|
self.configure(merge_allowed=True, auto_allowed=False, pending=True)
|
|
1811
|
-
|
|
1812
|
-
self.
|
|
1813
|
-
self.assertIn("disallows auto merge",
|
|
1828
|
+
result, _ = self.complete("--mode", "auto")
|
|
1829
|
+
self.assertEqual(result["status"], "PR")
|
|
1830
|
+
self.assertIn("disallows auto merge", result["merge_refused"])
|
|
1831
|
+
self.assertTrue(self.task.exists())
|
|
1814
1832
|
result, _ = self.complete("--no-push")
|
|
1815
1833
|
self.assertEqual(result["status"], "LOCAL_ONLY")
|
|
1816
1834
|
result, _ = self.complete(acceptance=False)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: devlyn
|
|
2
|
+
name: devlyn-design-ui
|
|
3
3
|
description: Assemble a world-class design team (Creative Director, Product Designer, Visual Designer, Interaction Designer, Accessibility Designer) to generate N (default 5) radically distinct, portfolio-worthy UI style explorations. Pass a leading integer or `count:N` in the brief to change the count.
|
|
4
4
|
source: project
|
|
5
5
|
---
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: devlyn
|
|
2
|
+
name: devlyn-engines
|
|
3
3
|
description: Show and pin engine roles for the devlyn pipeline (executor / pair judge). Reads and writes machine-local .devlyn/engines.json with fail-closed validation. Use when the user asks which models are configured, wants to force a specific model for pipeline work ("수동모드", "engine 고정", "use codex to implement"), or wants to clear pins back to auto-detection.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Utility front-end for the role-resolution contract in `_shared/engine-preflight.md#role-resolution`. It adds no semantics of its own: everything it writes is exactly what `/devlyn
|
|
6
|
+
Utility front-end for the role-resolution contract in `_shared/engine-preflight.md#role-resolution`. It adds no semantics of its own: everything it writes is exactly what `/devlyn-resolve` PHASE 0 reads.
|
|
7
7
|
|
|
8
8
|
<args>
|
|
9
9
|
$ARGUMENTS
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: devlyn
|
|
3
|
-
description: Extract a verifiable spec from a user's idea by driving the conversation with focused questions. Output is a single-feature `spec.md` + `spec.expected.json` that `/devlyn
|
|
2
|
+
name: devlyn-ideate
|
|
3
|
+
description: Extract a verifiable spec from a user's idea by driving the conversation with focused questions. Output is a single-feature `spec.md` + `spec.expected.json` that `/devlyn-resolve --spec` consumes directly. Use when the user has an idea but not a spec, or wants AI to elicit the missing engineering context. Modes — default (single spec, AI drives Q&A), `--quick` (assume-and-confirm from one-line goal), `--from-spec <path>` (normalize external spec), `--project` (plan.md index + N specs). Optional in the pipeline — `/devlyn-resolve` works standalone via free-form mode for users who skip ideate.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Spec-elicitation surface for users who have ideas but not engineering specifications. AI drives the conversation with focused questions until a structurally-valid, verifiable spec exists. Output consumed directly by `/devlyn
|
|
6
|
+
Spec-elicitation surface for users who have ideas but not engineering specifications. AI drives the conversation with focused questions until a structurally-valid, verifiable spec exists. Output consumed directly by `/devlyn-resolve --spec`.
|
|
7
7
|
|
|
8
8
|
<elicit_config>
|
|
9
9
|
$ARGUMENTS
|
|
10
10
|
</elicit_config>
|
|
11
11
|
|
|
12
12
|
<orchestrator_context>
|
|
13
|
-
This skill is OPTIONAL. `/devlyn
|
|
13
|
+
This skill is OPTIONAL. `/devlyn-resolve` is standalone-capable: free-form mode handles trivial/medium tasks without a spec, `--spec` mode accepts handwritten specs from any source. Use ideate when the user wants AI to do the elicitation work.
|
|
14
14
|
</orchestrator_context>
|
|
15
15
|
|
|
16
16
|
<elicitation_contract>
|
|
@@ -24,7 +24,7 @@ The user does not know context engineering. They will under-specify and over-ass
|
|
|
24
24
|
</elicitation_contract>
|
|
25
25
|
|
|
26
26
|
<harness_principles>
|
|
27
|
-
Read `_shared/runtime-principles.md` (Subtractive-first / Goal-locked / Evidence-over-claim). The principles bind the spec content as well as your conversation. A spec that says "for future flexibility" is a Subtractive-first violation. A spec that asks for `try { ... } catch { return null }` is a No-workaround violation. AI flags these in elicitation, not after `/devlyn
|
|
27
|
+
Read `_shared/runtime-principles.md` (Subtractive-first / Goal-locked / Evidence-over-claim). The principles bind the spec content as well as your conversation. A spec that says "for future flexibility" is a Subtractive-first violation. A spec that asks for `try { ... } catch { return null }` is a No-workaround violation. AI flags these in elicitation, not after `/devlyn-resolve` has built them.
|
|
28
28
|
</harness_principles>
|
|
29
29
|
|
|
30
30
|
<runtime_paths>
|
|
@@ -64,8 +64,8 @@ Four modes, selected by flag:
|
|
|
64
64
|
<spec_kind_escape_hatch>
|
|
65
65
|
The spec carries `spec.kind ∈ {feature, spike, prototype}` in its frontmatter. The kind changes downstream behavior:
|
|
66
66
|
|
|
67
|
-
- **feature** — production-quality implementation expected. `/devlyn
|
|
68
|
-
- **spike** — exploratory work; deliverable is learning, evidence, or a disposable demo. `/devlyn
|
|
67
|
+
- **feature** — production-quality implementation expected. `/devlyn-resolve --spec` runs the full pipeline (PLAN → IMPLEMENT → BUILD_GATE → CLEANUP → VERIFY).
|
|
68
|
+
- **spike** — exploratory work; deliverable is learning, evidence, or a disposable demo. `/devlyn-resolve --spec` proceeds but VERIFY's quality bar is relaxed for code that the spike says is throwaway.
|
|
69
69
|
- **prototype** — between feature and spike. Production-shape but not production-grade. CLEANUP runs; VERIFY's quality bar is stricter than spike, looser than feature.
|
|
70
70
|
|
|
71
71
|
The user picks the kind during elicitation. Default = feature when not specified. `--quick` infers from the goal text (verbs like "explore", "investigate", "spike" → spike; "implement", "ship", "add" → feature).
|
|
@@ -112,7 +112,7 @@ After lint passes:
|
|
|
112
112
|
2. Generate `<spec-dir>/<id>-<slug>/spec.expected.json` following `references/spec-template.md` § "Sibling file: `spec.expected.json`" for constraint coverage, diff scope and guard controls.
|
|
113
113
|
3. Run `python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py" --check <spec-path>` to validate the actual spec's carrier shape and supported `complexity` frontmatter. Sibling `spec.expected.json` takes precedence over the legacy inline `## Verification` JSON carrier. If exit 2, fix the file named in the error and re-run.
|
|
114
114
|
4. Run `python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py" --check-expected <expected-path>` to validate sibling `spec.expected.json` against `_shared/expected.schema.json` plus sibling spec `complexity` frontmatter. If exit 2, fix the JSON/frontmatter and re-run.
|
|
115
|
-
5. Print: `spec ready — /devlyn
|
|
115
|
+
5. Print: `spec ready — /devlyn-resolve --spec <spec-path>`.
|
|
116
116
|
|
|
117
117
|
## PHASE 1Q: QUICK MODE
|
|
118
118
|
|
|
@@ -145,9 +145,9 @@ Prompt body: `references/project-mode.md`.
|
|
|
145
145
|
2. AI decomposes the project into 3-7 feature specs. Each feature is independently shippable; cross-feature dependencies surface explicitly in the spec frontmatter `depends_on:` field.
|
|
146
146
|
3. AI writes `<spec-dir>/plan.md` — index file with: project name, decomposition rationale, list of feature specs with id + title + dependency, suggested implementation order.
|
|
147
147
|
4. AI writes one `<spec-dir>/<id>/spec.md` + `<spec-dir>/<id>/spec.expected.json` per feature, each lint-validated.
|
|
148
|
-
5. Announce: `project ready — N specs at <spec-dir>/. Start with /devlyn
|
|
148
|
+
5. Announce: `project ready — N specs at <spec-dir>/. Start with /devlyn-resolve --spec <first-spec-path>`.
|
|
149
149
|
|
|
150
|
-
`/devlyn
|
|
150
|
+
`/devlyn-resolve` consumes one spec at a time; the user works through `plan.md`'s suggested order. Multi-feature parallel runs are Mission 2 work.
|
|
151
151
|
|
|
152
152
|
## State management
|
|
153
153
|
|
|
@@ -84,7 +84,7 @@ Two files in `<spec-dir>/<id>-<slug>/`:
|
|
|
84
84
|
- `spec.md` (per template).
|
|
85
85
|
- `spec.expected.json` (per `_shared/expected.schema.json`).
|
|
86
86
|
|
|
87
|
-
Final announcement (one line): `spec ready — /devlyn
|
|
87
|
+
Final announcement (one line): `spec ready — /devlyn-resolve --spec <spec-path>`.
|
|
88
88
|
|
|
89
89
|
Do NOT include the conversation transcript in the output. The spec stands alone.
|
|
90
90
|
</output>
|
|
@@ -105,6 +105,6 @@ Quick mode trades thoroughness for speed. Use it for trivial-medium tasks where
|
|
|
105
105
|
|
|
106
106
|
- Walls of questions in one turn.
|
|
107
107
|
- Open-ended "what would you like" questions when the user clearly does not know.
|
|
108
|
-
- Adding "for future flexibility" or "just in case" sections to the spec — the user did not ask for those, and the principles binding `/devlyn
|
|
108
|
+
- Adding "for future flexibility" or "just in case" sections to the spec — the user did not ask for those, and the principles binding `/devlyn-resolve` reject them.
|
|
109
109
|
- Saving the conversation transcript alongside the spec.
|
|
110
110
|
- Stalling when the user gives a vague answer — re-ask with a more specific shape ("should it be A, B, or other?") rather than re-asking the same question.
|
|
@@ -43,7 +43,7 @@ You must NOT:
|
|
|
43
43
|
9. If the preserved Requirements combine state mutation with ordering/priority,
|
|
44
44
|
idempotency, auth/error priority, or exact output shape but the Verification
|
|
45
45
|
section lacks a compound end-to-end scenario, do not rewrite the author's
|
|
46
|
-
content. Add a final warning that `/devlyn
|
|
46
|
+
content. Add a final warning that `/devlyn-resolve` may need default-mode
|
|
47
47
|
ideation or a stronger Verification section before pair-relevant risks are
|
|
48
48
|
measurable.
|
|
49
49
|
</flow>
|
|
@@ -51,7 +51,7 @@ You must NOT:
|
|
|
51
51
|
<output>
|
|
52
52
|
Same as default mode: `<spec-dir>/<id>-<slug>/spec.md` + `<spec-dir>/<id>-<slug>/spec.expected.json`.
|
|
53
53
|
|
|
54
|
-
Final announcement: `spec normalized — /devlyn
|
|
54
|
+
Final announcement: `spec normalized — /devlyn-resolve --spec <spec-path>`. If the spec was lint-passing with no changes needed, announce: `spec already canonical — /devlyn-resolve --spec <spec-path>`. If step 9 applies, append: `warning: Verification may need one compound end-to-end scenario before pair-relevant risks are measurable`.
|
|
55
55
|
|
|
56
56
|
If lint failed unfixably: print the specific failure, exit non-zero. Do not write a partial output.
|
|
57
57
|
</output>
|
|
@@ -21,7 +21,7 @@ The user wants to build a project, not a single feature. Your job is to elicit t
|
|
|
21
21
|
- Features should be vertically sliced (full stack of one user-facing capability), not horizontally sliced (a layer that other features will need to consume).
|
|
22
22
|
- A feature whose ONLY value is enabling other features (pure infrastructure) belongs in the first feature that needs it OR as an explicit "enabling spec" with the dependent feature waiting on it.
|
|
23
23
|
- Feature dependencies are explicit in `frontmatter.depends_on`. The plan's suggested implementation order respects topological dependency.
|
|
24
|
-
- Aim for features that take 1-3 hours of `/devlyn
|
|
24
|
+
- Aim for features that take 1-3 hours of `/devlyn-resolve --spec` work each. Larger → split. Trivial → merge with neighbor.
|
|
25
25
|
</decomposition_rules>
|
|
26
26
|
|
|
27
27
|
<plan_md_shape>
|
|
@@ -48,8 +48,8 @@ Output `<spec-dir>/plan.md`:
|
|
|
48
48
|
|
|
49
49
|
## Suggested implementation order
|
|
50
50
|
|
|
51
|
-
1. `/devlyn
|
|
52
|
-
2. `/devlyn
|
|
51
|
+
1. `/devlyn-resolve --spec <spec-dir>/<id-1>/spec.md`
|
|
52
|
+
2. `/devlyn-resolve --spec <spec-dir>/<id-2>/spec.md`
|
|
53
53
|
3. ...
|
|
54
54
|
|
|
55
55
|
The order respects `depends_on`. Topologically equivalent specs can be parallelized later (Mission 2 work); single-task L1 ships them sequentially.
|
|
@@ -59,7 +59,7 @@ The order respects `depends_on`. Topologically equivalent specs can be paralleli
|
|
|
59
59
|
Anything binding all features (e.g. "no new top-level dependencies", "all CLI output must support `NO_COLOR`"). Each feature spec inherits these in its Constraints section.
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
`plan.md` is the **index**, not a hidden dependency. `/devlyn
|
|
62
|
+
`plan.md` is the **index**, not a hidden dependency. `/devlyn-resolve` reads only the spec it is invoked with; it does not parse `plan.md`.
|
|
63
63
|
</plan_md_shape>
|
|
64
64
|
|
|
65
65
|
<output>
|
|
@@ -69,7 +69,7 @@ Anything binding all features (e.g. "no new top-level dependencies", "all CLI ou
|
|
|
69
69
|
|
|
70
70
|
Each per-feature spec is structurally lint-validated using `python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py" --check <spec-path>`, including supported `complexity` frontmatter, and each sibling expected contract plus sibling spec `complexity` frontmatter is validated using `python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py" --check-expected <expected-path>`.
|
|
71
71
|
|
|
72
|
-
Final announcement: `project ready — N specs at <spec-dir>/. Start with /devlyn
|
|
72
|
+
Final announcement: `project ready — N specs at <spec-dir>/. Start with /devlyn-resolve --spec <first-spec-path>`.
|
|
73
73
|
</output>
|
|
74
74
|
|
|
75
75
|
<anti_patterns>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Canonical spec template
|
|
2
2
|
|
|
3
|
-
The shape `/devlyn
|
|
3
|
+
The shape `/devlyn-ideate` writes and `/devlyn-resolve --spec` consumes. Single source of truth for spec structure.
|
|
4
4
|
|
|
5
5
|
## Frontmatter
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: devlyn
|
|
3
|
-
description: Manage and drain the intent queue (docs/specs/queue.md) — the backlog the loop-engineering contract runs on. Use when the user wants to stack work for later ("큐에 넣어줘", "queue this"), see what is queued, or start an unattended serial drain ("큐 드레인 시작", "밤새 돌려줘", "drain the queue") that takes each item through spec → /devlyn
|
|
2
|
+
name: devlyn-queue
|
|
3
|
+
description: Manage and drain the intent queue (docs/specs/queue.md) — the backlog the loop-engineering contract runs on. Use when the user wants to stack work for later ("큐에 넣어줘", "queue this"), see what is queued, or start an unattended serial drain ("큐 드레인 시작", "밤새 돌려줘", "drain the queue") that takes each item through spec → /devlyn-resolve → verified done.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Utility front-end for the intent-queue contract in the project instructions ("Intent queue" in CLAUDE.md / AGENTS.md — whichever this CLI loads). It adds no semantics of its own: the queue file is the API, and the drain loop follows the documented contract exactly — a runner (this skill, a long session, or a future devlyn-os daemon) is a replaceable driver over the same file.
|
|
@@ -21,7 +21,7 @@ DEVLYN_SHARED_DIR="$(cd "$DEVLYN_SKILL_DIR/../_shared" && pwd)"
|
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Before processing the first item, read and obey
|
|
24
|
-
`../devlyn
|
|
24
|
+
`../devlyn-resolve/references/outer-loop.md`. Its scoped-commit order is binding.
|
|
25
25
|
|
|
26
26
|
Queue state is base's `docs/specs/queue.md` plus owned receipts: for each `$(git rev-parse --git-common-dir)/devlyn-completion/*/receipt.json`, read `git show <ref>:docs/specs/queue.md` from its `recovery_ref` if that ref exists (it is never deleted), else its `branch` if it still exists; an item unmarked on base but `[x]`/`[F]` there counts as that mark. Two different terminal marks for one item stop for inspection. Status and drain selection both use this view.
|
|
27
27
|
|
|
@@ -33,11 +33,11 @@ Read the queue state above (absent `docs/specs/queue.md` → report "queue empty
|
|
|
33
33
|
|
|
34
34
|
- `add <intent text>` — append `- [ ] <intent>` to `docs/specs/queue.md` (create the file with its header if missing). If the intent came out of a conversation that already produced a spec, link it: `- [ ] (spec: docs/specs/<id>/spec.md) <intent>`.
|
|
35
35
|
- `drain` — serial drain per the project-instructions contract. For each pending item, in order:
|
|
36
|
-
1. Allocate the owner's absent task branch with `--worktree <absent path>` per `../devlyn
|
|
37
|
-
2. Bring only the current queue-item delta and linked spec bundle from the queue view into the task worktree, which starts from the fetched remote base with no other local commits, commit that scoped owner baseline, then run `/devlyn
|
|
36
|
+
1. Allocate the owner's absent task branch with `--worktree <absent path>` per `../devlyn-resolve/references/task-completion.md`, and work in that worktree; then spec it if unspecced (the queue entry is the user's go-ahead). Unattended assumptions may only take scope-narrowing, reversible, non-user-visible defaults; material ambiguity (user-visible behavior, data/state semantics, new files/scripts/flags, implementation surface) → mark `[F] needs-review: <question>`, commit that queue transition, and continue.
|
|
37
|
+
2. Bring only the current queue-item delta and linked spec bundle from the queue view into the task worktree, which starts from the fetched remote base with no other local commits, commit that scoped owner baseline, then run `/devlyn-resolve --spec <path>` hands-free.
|
|
38
38
|
After every resolve invocation, run `python3 "$DEVLYN_SHARED_DIR/terminal-claim-check.py" .`; exit 79 marks `[F] FAILED-INCOMPLETE` from the predicate, never from the session self-report.
|
|
39
39
|
3. Outer loop on the terminal verdict: PASS → mark `[x]`. Findings-backed verdicts (NEEDS_WORK, BLOCKED:repair-budget-exhausted) → amend the spec, commit that scoped amendment, then re-run — at most 3 outer iterations. Infrastructure / invalid-input / engine-availability / implement-empty BLOCKED verdicts are not spec-amendable → mark `[F] <verdict>` immediately.
|
|
40
40
|
4. Commit each terminal `[x]` / `[F]` queue transition before advancing. For successfully archived normal runs, invoke owner completion once after that declared queue-file-only commit; bind it separately from the verified source. Honor local-only/no-push. Failed products never publish. Retain PR/pending workspaces and use a separate owned branch from base for the next item; unavailable safe placement stops the drain with remaining items pending. A product-blocked item otherwise never halts the queue.
|
|
41
41
|
5. When the queue is drained (or the session must stop), emit the drain report: per-item product verdict and separate delivery status/URL/resume, every logged assumption, and the commit range produced.
|
|
42
42
|
|
|
43
|
-
Strictly SERIAL — one `/devlyn
|
|
43
|
+
Strictly SERIAL — one `/devlyn-resolve` run at a time, never parallel. Never invent queue items, never reorder them, and never delete an item — only mark `[x]` / `[F]`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: devlyn
|
|
2
|
+
name: devlyn-resolve
|
|
3
3
|
description: Default to direct work after inspecting scope and verification, including bounded multi-file changes. Use full resolve for explicit resolve/spec-mode requests, queue drains, or concrete interacting requirements or verification too complex for the current context. Domain labels, file count, spec documents and quoted skill paths alone do not trigger it. Investigate or clarify missing intent/access first. Explicit small resolve retains all phases and independent verification. Verify dual-judge is default-when-available.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -62,9 +62,9 @@ Each model-invoked phase routes to an engine and prepends the per-engine adapter
|
|
|
62
62
|
<modes>
|
|
63
63
|
Three input shapes:
|
|
64
64
|
|
|
65
|
-
1. **Free-form**: `/devlyn
|
|
66
|
-
2. **Spec**: `/devlyn
|
|
67
|
-
3. **Verify-only**: `/devlyn
|
|
65
|
+
1. **Free-form**: `/devlyn-resolve "fix the login bug"` (inline goal) or `/devlyn-resolve --goal-file <path>` (goal text read from a file — the devlynd `ResolveAdapter` launcher path, injection-safe). PHASE 0 runs the complexity classifier and either proceeds with an internal mini-spec (trivial), drafts focused questions for in-prompt resolution (medium), or synthesizes a best-effort spec with a logged `## Assumptions` block (large; zero-scope-signal goals halt). No mid-pipeline prompts in any branch.
|
|
66
|
+
2. **Spec**: `/devlyn-resolve --spec docs/roadmap/phase-N/X.md`. Supplied specs and expected files are read-only inside resolve. Stage verification commands from sibling `spec.expected.json`; if absent, use the legacy `## Verification` JSON block.
|
|
67
|
+
3. **Verify-only**: `/devlyn-resolve --verify-only <diff-or-PR-ref> --spec <path>`. Skips PHASE 1-4. Runs PHASE 5 (VERIFY) on the supplied diff against the spec.
|
|
68
68
|
</modes>
|
|
69
69
|
|
|
70
70
|
<post_implement_invariant>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Free-form mode — complexity classifier
|
|
2
2
|
|
|
3
|
-
When `/devlyn
|
|
3
|
+
When `/devlyn-resolve` is invoked with a free-form goal (no `--spec`) — whether the goal is the inline positional text or the content of `--goal-file <path>` (PHASE 0 resolves `goal_text` from either source before classifying) — PHASE 0 runs this classifier to set `state.complexity ∈ {trivial, medium, large}` and either proceeds with an internal mini-spec, drafts focused questions for in-prompt resolution, synthesizes a best-effort spec with logged assumptions, or halts zero-scope-signal goals with `/devlyn-ideate` guidance.
|
|
4
4
|
|
|
5
5
|
The classifier is rules-based / deterministic — not an LLM judgment call. Decision rules below.
|
|
6
6
|
|
|
@@ -49,7 +49,7 @@ Conditions (any one):
|
|
|
49
49
|
- The goal mentions a new feature whose surface area requires design decisions the harness cannot make from a one-shot prompt.
|
|
50
50
|
|
|
51
51
|
Action:
|
|
52
|
-
- Default: synthesize a best-effort spec from the goal with an explicit `## Assumptions` block (every assumption scope-narrowing and reversible — when in doubt, narrower); log `recommend: /devlyn
|
|
52
|
+
- Default: synthesize a best-effort spec from the goal with an explicit `## Assumptions` block (every assumption scope-narrowing and reversible — when in doubt, narrower); log `recommend: /devlyn-ideate first` in `.devlyn/criteria.generated.md` AND the final report; proceed to PHASE 1; the final report flags every assumption for user review.
|
|
53
53
|
- Zero-signal exception: if the large classification includes `file_scope_signals == 0` (classifier cannot pick scope), halt with terminal verdict `BLOCKED:large-needs-ideation` — assumptions there would be scope-invention, not narrowing.
|
|
54
54
|
|
|
55
55
|
## Anti-pattern: drift to LLM judgment
|
|
@@ -63,7 +63,7 @@ When the rules are silent (rare — pathological goal text), default to `medium`
|
|
|
63
63
|
The internal mini-spec written for trivial / medium / large-assumptions paths must satisfy:
|
|
64
64
|
|
|
65
65
|
- `## Requirements` non-empty, each bullet testable (CLI command, test command, observable file change).
|
|
66
|
-
- For executable generated verification, put one fenced `json` object with `verification_commands` inside the sentinel-marked `## Verification` section. Generated mode reads this inline carrier, not a sibling file. Follow `../../devlyn
|
|
66
|
+
- For executable generated verification, put one fenced `json` object with `verification_commands` inside the sentinel-marked `## Verification` section. Generated mode reads this inline carrier, not a sibling file. Follow `../../devlyn-ideate/references/spec-template.md` § "Sibling file: `spec.expected.json`" for constraint coverage, diff scope and violating/allowed controls, but express those checks as executable `verification_commands`; sibling-only fields such as `required_files` and `forbidden_patterns` are rejected inline. Preserve uncovered semantics as source-review obligations.
|
|
67
67
|
- `## Verification` is preceded by a `<!-- devlyn:verification -->` sentinel on its own line directly above the heading — the machine locator `spec-verify-check.py` uses; the heading text itself is decorative and may be any language. When all Requirements are pure-design with no runnable acceptance check, put `{"pure_design": true, "verification_commands": []}` in the fenced `json` block and retain semantic source-review obligations. This explicit declaration is required; a missing block, an unmarked empty list, or `pure_design: true` with commands is invalid.
|
|
68
68
|
- Free-form mode mini-specs are written to `.devlyn/criteria.generated.md` (not to a roadmap path) — this is run-scoped artifact, not a documented spec.
|
|
69
69
|
- After writing `.devlyn/criteria.generated.md`, set `state.source.type = "generated"`, `state.source.spec_path = null`, `state.source.spec_sha256 = null`, `state.source.criteria_path = ".devlyn/criteria.generated.md"`, and `state.source.criteria_sha256` to the raw-byte SHA-256 of the generated criteria file. Downstream PLAN/IMPLEMENT/VERIFY phases and `spec-verify-check.py --include-risk-probes` depend on this pointer; do not rely on the file existing by convention.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Outer-loop commit boundary
|
|
2
2
|
|
|
3
|
-
A full `/devlyn
|
|
3
|
+
A full `/devlyn-resolve` run starts only from committed owner inputs. Keep each
|
|
4
4
|
commit scoped: no unrelated paths or queue-item changes.
|
|
5
5
|
|
|
6
6
|
1. Before task writes, the outer owner allocates an absent task branch per
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pipeline.state.json schema
|
|
2
2
|
|
|
3
|
-
Single authoritative verdict source for `/devlyn
|
|
3
|
+
Single authoritative verdict source for `/devlyn-resolve`. The orchestrator branches on `state.phases.<name>.verdict` directly — never parses `.devlyn/*.findings.jsonl` for routing. Living document; bump `version` on a breaking change.
|
|
4
4
|
|
|
5
5
|
## Top-level shape
|
|
6
6
|
|
|
@@ -101,19 +101,19 @@ python3 "$DEVLYN_SHARED_DIR/task-complete.py" complete \
|
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
Project policy is `git config --local devlyn.completionMode auto|pr`; absent means
|
|
104
|
-
`
|
|
104
|
+
`auto`. Invalid local values fail before external effects, even with an override.
|
|
105
105
|
Use `complete --mode auto|pr` for one task, or `--local-only`/`--no-push` to honor
|
|
106
106
|
the user's delivery restriction. These per-task choices persist in the receipt.
|
|
107
107
|
|
|
108
108
|
`pr` pushes the exact accepted task ref and creates/reuses its exact repository,
|
|
109
109
|
head and base PR. While OPEN, it cancels owned auto-merge, reports `PR` with its URL
|
|
110
110
|
and retains the checkout. Once MERGED by any method, either mode cleans released
|
|
111
|
-
resources in that call, on resume or at the next allocation.
|
|
111
|
+
resources in that call, on resume or at the next allocation. Default `auto` also requests
|
|
112
112
|
`gh pr merge --auto --merge --match-head-commit <accepted SHA>`. Repository
|
|
113
113
|
checks/reviews and merge policy remain authoritative: no admin bypass, strategy
|
|
114
|
-
fallback or repository setting changes.
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
fallback or repository setting changes. When the repository disallows merge
|
|
115
|
+
commits or refuses the request, delivery reports `PR` with `merge_refused` and
|
|
116
|
+
the PR waits for a person. Command success alone never proves merge.
|
|
117
117
|
|
|
118
118
|
`PENDING` retains the workspace and reports a receipt-based resume command.
|
|
119
119
|
`BLOCKED` includes the cause and the same retry path; repair the reported condition
|
|
@@ -23,7 +23,7 @@ Start with the minimum viable solution, then improve if time allows.
|
|
|
23
23
|
|
|
24
24
|
## Debugging
|
|
25
25
|
|
|
26
|
-
For systematic debugging, use `/devlyn
|
|
26
|
+
For systematic debugging, use `/devlyn-resolve [issue description]` which includes:
|
|
27
27
|
- Code path mapping
|
|
28
28
|
- Hypothesis-driven analysis
|
|
29
29
|
- Test-driven fix validation
|
|
@@ -61,7 +61,7 @@ have working, tested code ready for PR.
|
|
|
61
61
|
For complex bugs that need autonomous resolution:
|
|
62
62
|
|
|
63
63
|
```
|
|
64
|
-
/devlyn
|
|
64
|
+
/devlyn-resolve [bug description]
|
|
65
65
|
|
|
66
66
|
After analysis, implement the fix autonomously:
|
|
67
67
|
- Write failing test first
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: devlyn
|
|
2
|
+
name: devlyn-reap
|
|
3
3
|
description: Safely count and kill orphaned child processes (PPID=1) left behind by Claude Code MCP plugins, Superset terminal tabs, and codex wrappers. Use this whenever the user says "too many processes", "can't open terminals", "pty/process limit", "hundreds of bun/codex/workerd piling up", "clean up orphans", "reap processes", or reports new terminals failing to spawn on macOS. Also use proactively after long Claude sessions to prevent hitting kern.maxprocperuid or kern.tty.ptmx_max limits. ONLY touches a conservative whitelist of known leaks — never guesses on unknown processes.
|
|
4
4
|
allowed-tools: Read, Bash(ps:*), Bash(lsof:*), Bash(pgrep:*), Bash(awk:*), Bash(id:*), Bash(sysctl:*), Bash(bash:*)
|
|
5
5
|
argument-hint: [scan | kill | kill --force | kill --include workerd | kill --only telegram-bun]
|
|
@@ -38,10 +38,10 @@ If the user's intent is ambiguous (e.g., they say "지워줘" but didn't specify
|
|
|
38
38
|
|
|
39
39
|
Always run scan first — even in KILL mode — so the user sees what is about to happen.
|
|
40
40
|
|
|
41
|
-
Run the bundled scanner. The skill is installed at `~/.claude/skills/devlyn
|
|
41
|
+
Run the bundled scanner. The skill is installed at `~/.claude/skills/devlyn-reap/`:
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
bash ~/.claude/skills/devlyn
|
|
44
|
+
bash ~/.claude/skills/devlyn-reap/scripts/scan.sh
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
Report the output verbatim to the user. Then add your own 2-line summary:
|
|
@@ -60,7 +60,7 @@ sysctl kern.maxprocperuid kern.tty.ptmx_max 2>/dev/null
|
|
|
60
60
|
Run the reap script with the parsed flags:
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
bash ~/.claude/skills/devlyn
|
|
63
|
+
bash ~/.claude/skills/devlyn-reap/scripts/reap.sh [flags]
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
Show the output verbatim. The script re-verifies `PPID==1 && user==current` for every PID right before signaling — a process that was legitimately adopted since the scan will be skipped, not killed.
|
|
@@ -72,7 +72,7 @@ After kill, re-run scan to confirm the counts dropped. If any whitelisted PIDs a
|
|
|
72
72
|
If `telegram-bun` count > 10 OR oldest whitelisted orphan > 24h, tell the user this is a recurring leak and suggest one of:
|
|
73
73
|
|
|
74
74
|
1. **Patch the telegram plugin** — add `process.stdin.on('end', () => process.exit(0))` to `server.ts` so the child dies when Claude Code exits.
|
|
75
|
-
2. **Schedule this skill** — run `/devlyn
|
|
75
|
+
2. **Schedule this skill** — run `/devlyn-reap kill` periodically (e.g., via the `/loop` skill or a launchd agent).
|
|
76
76
|
3. **Update Superset** — newer versions may SIGTERM process groups on tab close.
|
|
77
77
|
|
|
78
78
|
Do NOT apply these automatically. Recommend and let the user choose.
|