devlyn-cli 4.1.0 → 4.2.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 +10 -94
- package/CLAUDE.md +7 -160
- package/LICENSE +21 -0
- package/README.md +65 -110
- package/bin/devlyn.js +95 -116
- package/bin/instruction-templates.json +43 -2
- package/bin/instructions.js +29 -8
- package/config/skills/_shared/adapters/claude.md +8 -85
- package/config/skills/_shared/adapters/codex.md +14 -34
- package/config/skills/_shared/adapters/omp.md +1 -11
- package/config/skills/_shared/codex-monitored.sh +4 -54
- package/config/skills/_shared/engine-doctor.sh +18 -58
- package/config/skills/_shared/engine-preflight.md +15 -47
- package/config/skills/_shared/expected-contract.py +242 -0
- package/config/skills/_shared/expected.schema.json +10 -95
- package/config/skills/_shared/invocation-receipt.py +26 -757
- package/config/skills/_shared/platform-support.py +37 -28
- package/config/skills/_shared/role-config.py +100 -421
- package/config/skills/_shared/runtime-principles.md +17 -93
- package/config/skills/_shared/task-complete.py +490 -285
- package/config/skills/_shared/task-completion.md +235 -0
- package/config/skills/devlyn-engines/SKILL.md +9 -11
- package/config/skills/devlyn-ideate/SKILL.md +57 -107
- package/config/skills/devlyn-ideate/references/elicitation.md +18 -99
- package/config/skills/devlyn-ideate/references/loop.md +110 -0
- package/config/skills/devlyn-ideate/references/package-format.md +128 -0
- package/config/skills/devlyn-ideate/scripts/acceptance.py +795 -0
- package/config/skills/devlyn-ideate/scripts/queue.py +1780 -0
- package/package.json +2 -3
- package/config/commit-conventions.md +0 -28
- package/config/skills/_shared/adapters/README.md +0 -74
- package/config/skills/_shared/adapters/grok.md +0 -100
- package/config/skills/_shared/archive_run.py +0 -969
- package/config/skills/_shared/codex-config.md +0 -81
- package/config/skills/_shared/collect-codex-findings.py +0 -505
- package/config/skills/_shared/finish-gate.py +0 -521
- package/config/skills/_shared/grok-anchor-guard.py +0 -78
- package/config/skills/_shared/judge-output-parser.py +0 -200
- package/config/skills/_shared/judge-role-evidence.py +0 -320
- package/config/skills/_shared/phase-prompt-render.py +0 -183
- package/config/skills/_shared/process-evidence.py +0 -926
- package/config/skills/_shared/resolve-bootstrap.py +0 -1410
- package/config/skills/_shared/resolve-stop-hook.py +0 -359
- package/config/skills/_shared/run-bounded.py +0 -58
- package/config/skills/_shared/spec-verify-check.py +0 -5083
- package/config/skills/_shared/state-phase-write.py +0 -5989
- package/config/skills/_shared/terminal-claim-check.py +0 -777
- package/config/skills/_shared/verify-merge-findings.py +0 -3528
- package/config/skills/devlyn-design-ui/SKILL.md +0 -650
- package/config/skills/devlyn-ideate/references/from-spec-mode.md +0 -61
- package/config/skills/devlyn-ideate/references/project-mode.md +0 -80
- package/config/skills/devlyn-ideate/references/spec-template.md +0 -121
- package/config/skills/devlyn-ideate/references/templates/decision.md +0 -48
- package/config/skills/devlyn-ideate/references/templates/roadmap.md +0 -50
- package/config/skills/devlyn-ideate/references/templates/vision.md +0 -44
- package/config/skills/devlyn-queue/SKILL.md +0 -66
- package/config/skills/devlyn-queue/scripts/append.py +0 -94
- package/config/skills/devlyn-resolve/SKILL.md +0 -379
- package/config/skills/devlyn-resolve/references/free-form-mode.md +0 -71
- package/config/skills/devlyn-resolve/references/outer-loop.md +0 -23
- package/config/skills/devlyn-resolve/references/phases/build-gate.md +0 -75
- package/config/skills/devlyn-resolve/references/phases/cleanup.md +0 -33
- package/config/skills/devlyn-resolve/references/phases/implement.md +0 -44
- package/config/skills/devlyn-resolve/references/phases/plan.md +0 -39
- package/config/skills/devlyn-resolve/references/phases/probe-derive.md +0 -261
- package/config/skills/devlyn-resolve/references/phases/surface-close.md +0 -34
- package/config/skills/devlyn-resolve/references/phases/verify.md +0 -310
- package/config/skills/devlyn-resolve/references/state-schema.md +0 -152
- package/config/skills/devlyn-resolve/references/task-completion.md +0 -160
- package/config/templates/prompt-templates.md +0 -71
- package/config/templates/template-feature.spec.md +0 -255
- package/config/templates/template-product-spec.md +0 -680
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# `--from-spec` mode
|
|
2
|
-
|
|
3
|
-
Per-engine adapter prepended at runtime.
|
|
4
|
-
|
|
5
|
-
<role>
|
|
6
|
-
The user already wrote a spec (or has one from elsewhere — a teammate, a previous project, a copy-paste from a doc). Your job is to lint and normalize it to the canonical shape — without reshaping the user's substantive intent.
|
|
7
|
-
</role>
|
|
8
|
-
|
|
9
|
-
<input>
|
|
10
|
-
- `<path>` — the external spec markdown file.
|
|
11
|
-
- `_shared/expected.schema.json` — the schema for the sibling `spec.expected.json`.
|
|
12
|
-
- `references/spec-template.md` — the canonical shape.
|
|
13
|
-
</input>
|
|
14
|
-
|
|
15
|
-
<allowed_changes>
|
|
16
|
-
You may:
|
|
17
|
-
1. Add missing frontmatter fields (id from filename, kind=feature default, status=planned, complexity=medium default; set complexity=high only when preserved Requirements clearly combine state/order/failure/output-shape risks).
|
|
18
|
-
2. Rename non-canonical section headings to canonical (`## Goals` → `## Requirements`, `## Notes` ignored unless they clearly belong in Constraints).
|
|
19
|
-
3. Add a missing `## Out of Scope` section with `- (no explicit non-goals provided by author)`.
|
|
20
|
-
4. Add a missing `## Verification` section if Requirements imply observable runtime checks — best-effort one-command-per-Requirement, then surface to user for review.
|
|
21
|
-
5. Generate `spec.expected.json` from the `## Verification` block if the file is absent.
|
|
22
|
-
6. Fix structurally invalid `spec.expected.json` (malformed JSON, missing required keys per `_shared/expected.schema.json`).
|
|
23
|
-
</allowed_changes>
|
|
24
|
-
|
|
25
|
-
<forbidden_changes>
|
|
26
|
-
You must NOT:
|
|
27
|
-
- Reshape Requirements content. The user's substantive intent is preserved verbatim.
|
|
28
|
-
- Add new Constraints the user did not write.
|
|
29
|
-
- Move items between Requirements and Out of Scope.
|
|
30
|
-
- "Improve" the prose in Context. Author voice stays.
|
|
31
|
-
- Add `## Assumptions` or `## Open questions` sections — that is default-mode work, not normalization.
|
|
32
|
-
</forbidden_changes>
|
|
33
|
-
|
|
34
|
-
<flow>
|
|
35
|
-
1. Read `<path>`. Parse frontmatter. Identify present sections.
|
|
36
|
-
2. Lint structurally (same checks as default mode).
|
|
37
|
-
3. For each missing/malformed piece: apply the smallest allowed fix.
|
|
38
|
-
4. Write the normalized spec. Default location: `<spec-dir>/<id>-<slug>/spec.md`. With `--in-place` flag: write to `<path>` directly (overwrites the original).
|
|
39
|
-
5. Generate or fix `spec.expected.json` per the rules above. Same dir as the spec.
|
|
40
|
-
6. 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.
|
|
41
|
-
7. Run `python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py" --check-expected <expected-path>` to validate sibling `spec.expected.json` plus sibling spec `complexity` frontmatter.
|
|
42
|
-
8. If lint still fails after allowed fixes (e.g. Requirements section is empty in the source), surface the issue and exit non-zero — do NOT invent Requirements.
|
|
43
|
-
9. If the preserved Requirements combine state mutation with ordering/priority,
|
|
44
|
-
idempotency, auth/error priority, or exact output shape but the Verification
|
|
45
|
-
section lacks a compound end-to-end scenario, do not rewrite the author's
|
|
46
|
-
content. Add a final warning that `/devlyn-resolve` may need default-mode
|
|
47
|
-
ideation or a stronger Verification section before pair-relevant risks are
|
|
48
|
-
measurable.
|
|
49
|
-
</flow>
|
|
50
|
-
|
|
51
|
-
<output>
|
|
52
|
-
Same as default mode: `<spec-dir>/<id>-<slug>/spec.md` + `<spec-dir>/<id>-<slug>/spec.expected.json`.
|
|
53
|
-
|
|
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
|
-
|
|
56
|
-
If lint failed unfixably: print the specific failure, exit non-zero. Do not write a partial output.
|
|
57
|
-
</output>
|
|
58
|
-
|
|
59
|
-
<rationale>
|
|
60
|
-
`--from-spec` exists for power users with external context. Adding friction by forcing them through default-mode elicitation defeats the purpose. The mode trades elicitation depth for normalization speed; the user accepts that any author-side under-specification stays under-specified.
|
|
61
|
-
</rationale>
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# `--project` mode
|
|
2
|
-
|
|
3
|
-
Per-engine adapter prepended at runtime.
|
|
4
|
-
|
|
5
|
-
<role>
|
|
6
|
-
The user wants to build a project, not a single feature. Your job is to elicit the project description, decompose it into 3-7 independently-shippable features, and write `plan.md` (the index) plus one spec.md + spec.expected.json per feature.
|
|
7
|
-
</role>
|
|
8
|
-
|
|
9
|
-
<conversation_rules>
|
|
10
|
-
1. Project elicitation warrants more turns than single-spec mode. Hard ceiling: 12 turns instead of 8.
|
|
11
|
-
2. Ask the same question categories as default mode (input/output/failure/scope/constraints/verification) but at the project level first, then drill into each feature.
|
|
12
|
-
3. Decompose into 3-7 features. Fewer = the project is actually one big feature; recommend default mode. More = the project is too large; recommend splitting into separate ideate runs.
|
|
13
|
-
4. Each feature must be independently shippable: a feature whose verification depends on another feature's runtime behavior is a dependency, not a feature.
|
|
14
|
-
5. When a feature combines state mutation with ordering/priority, idempotency,
|
|
15
|
-
auth/error priority, or exact output shape, its per-feature Verification must
|
|
16
|
-
include one compound end-to-end scenario; do not hide the interaction in
|
|
17
|
-
project-level prose.
|
|
18
|
-
</conversation_rules>
|
|
19
|
-
|
|
20
|
-
<decomposition_rules>
|
|
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
|
-
- 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
|
-
- 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-resolve --spec` work each. Larger → split. Trivial → merge with neighbor.
|
|
25
|
-
</decomposition_rules>
|
|
26
|
-
|
|
27
|
-
<plan_md_shape>
|
|
28
|
-
Output `<spec-dir>/plan.md`:
|
|
29
|
-
|
|
30
|
-
```markdown
|
|
31
|
-
# <Project name>
|
|
32
|
-
|
|
33
|
-
## Goal
|
|
34
|
-
|
|
35
|
-
2-4 sentences from the elicitation — what the project delivers and why.
|
|
36
|
-
|
|
37
|
-
## Decomposition rationale
|
|
38
|
-
|
|
39
|
-
1-2 paragraphs explaining how the project was sliced into features. Includes the order rationale.
|
|
40
|
-
|
|
41
|
-
## Feature specs
|
|
42
|
-
|
|
43
|
-
| ID | Title | Depends on | Status |
|
|
44
|
-
|----|-------|-----------|--------|
|
|
45
|
-
| <id-1> | ... | (none) | planned |
|
|
46
|
-
| <id-2> | ... | <id-1> | planned |
|
|
47
|
-
| ... | ... | ... | ... |
|
|
48
|
-
|
|
49
|
-
## Suggested implementation order
|
|
50
|
-
|
|
51
|
-
1. `/devlyn-resolve --spec <spec-dir>/<id-1>/spec.md`
|
|
52
|
-
2. `/devlyn-resolve --spec <spec-dir>/<id-2>/spec.md`
|
|
53
|
-
3. ...
|
|
54
|
-
|
|
55
|
-
The order respects `depends_on`. Topologically equivalent specs can be parallelized later (Mission 2 work); single-task L1 ships them sequentially.
|
|
56
|
-
|
|
57
|
-
## Project-level constraints
|
|
58
|
-
|
|
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
|
-
```
|
|
61
|
-
|
|
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
|
-
</plan_md_shape>
|
|
64
|
-
|
|
65
|
-
<output>
|
|
66
|
-
- `<spec-dir>/plan.md` (the index above).
|
|
67
|
-
- `<spec-dir>/<id-N>/spec.md` for each feature (per `references/spec-template.md`).
|
|
68
|
-
- `<spec-dir>/<id-N>/spec.expected.json` for each feature (per `_shared/expected.schema.json`).
|
|
69
|
-
|
|
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
|
-
|
|
72
|
-
Final announcement: `project ready — N specs at <spec-dir>/. Start with /devlyn-resolve --spec <first-spec-path>`.
|
|
73
|
-
</output>
|
|
74
|
-
|
|
75
|
-
<anti_patterns>
|
|
76
|
-
- Decomposing into more features than the project needs (10+ features for a CLI). Project mode is for genuine multi-feature work.
|
|
77
|
-
- Hidden coupling between features (feature B's spec assumes feature A's internal data structure). Surface as `depends_on` or fold into one spec.
|
|
78
|
-
- Vision documents, roadmaps as separate tiers, market-positioning paragraphs. The locked design is index + specs. Anything else is context pollution.
|
|
79
|
-
- Continuing past 12 turns of elicitation. If the project is still not clear at turn 12, recommend the user split it into 2-3 separate ideate runs.
|
|
80
|
-
</anti_patterns>
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# Canonical spec template
|
|
2
|
-
|
|
3
|
-
The shape `/devlyn-ideate` writes and `/devlyn-resolve --spec` consumes. Single source of truth for spec structure.
|
|
4
|
-
|
|
5
|
-
## Frontmatter
|
|
6
|
-
|
|
7
|
-
```yaml
|
|
8
|
-
---
|
|
9
|
-
id: "<spec-id>" # kebab-case, unique per spec-dir; auto-generated if user omits
|
|
10
|
-
title: "<short title>" # one line, descriptive
|
|
11
|
-
kind: feature # feature | spike | prototype
|
|
12
|
-
status: planned # planned → in_progress → done. ideate writes "planned"; resolve's CLEANUP flips to "done".
|
|
13
|
-
complexity: medium # trivial | medium | high. Use high when Verification needs compound state/order/failure checks.
|
|
14
|
-
depends_on: [] # list of spec ids this depends on (empty for standalone). project mode populates this.
|
|
15
|
-
---
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## `# <title>`
|
|
19
|
-
|
|
20
|
-
Top-level heading repeats the title.
|
|
21
|
-
|
|
22
|
-
## `## Context`
|
|
23
|
-
|
|
24
|
-
2-4 sentences answering "why does this exist." User-facing motivation, not implementation detail. Traceable to whatever the user said during elicitation.
|
|
25
|
-
|
|
26
|
-
## `## Requirements`
|
|
27
|
-
|
|
28
|
-
Checklist of testable, scoped statements. Each bullet is one observable behavior:
|
|
29
|
-
|
|
30
|
-
```markdown
|
|
31
|
-
- [ ] `node bin/cli.js doctor` exits 0 on a clean machine.
|
|
32
|
-
- [ ] `--verbose` lists installed plugins by name.
|
|
33
|
-
- [ ] Exit code is `1` when any FAIL line appears.
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Rules:
|
|
37
|
-
- Each Requirement is independently verifiable (a command, a test, a file existence check).
|
|
38
|
-
- Avoid implementation language ("use a try/catch") in Requirements; that goes in Constraints.
|
|
39
|
-
- Pure-design Requirements are allowed (e.g. "follow the existing pattern in `lib/X.js`") but mark them "design-only" so Verification can be omitted for that bullet.
|
|
40
|
-
|
|
41
|
-
## `## Constraints`
|
|
42
|
-
|
|
43
|
-
Concrete rules with reasoning. Examples:
|
|
44
|
-
|
|
45
|
-
```markdown
|
|
46
|
-
- **Zero new npm dependencies.** Use only Node.js built-ins.
|
|
47
|
-
- **No silent error catches.** Errors must be visible to the user with actionable messages.
|
|
48
|
-
- **Touch only `bin/cli.js` and `tests/cli.test.js`.** Other files are out of scope.
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Each constraint has a one-line "why" — bare constraints invite negotiation; reasoned constraints lock the contract.
|
|
52
|
-
|
|
53
|
-
## `## Out of Scope`
|
|
54
|
-
|
|
55
|
-
Explicit "must NOT build" list. Examples:
|
|
56
|
-
|
|
57
|
-
```markdown
|
|
58
|
-
- Auto-repair (report only).
|
|
59
|
-
- Checking remote/registry state.
|
|
60
|
-
- Refactoring the argument parser.
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
If the spec is small enough that nothing meaningful is out of scope, write `- (nothing notable; see Constraints for boundary)`.
|
|
64
|
-
|
|
65
|
-
## `## Verification`
|
|
66
|
-
|
|
67
|
-
Immediately precede the heading with a `<!-- devlyn:verification -->` sentinel
|
|
68
|
-
comment on its own line, directly above `## Verification` (no blank line
|
|
69
|
-
between them). This is the machine locator `spec-verify-check.py` uses to
|
|
70
|
-
find the section — it is language-neutral: the heading text after the
|
|
71
|
-
sentinel may be any human language (`## Verification`, `## 검증`, etc.); only
|
|
72
|
-
the sentinel itself is fixed ASCII and must never be translated.
|
|
73
|
-
|
|
74
|
-
Concrete commands or tests whose expected behavior names the success condition:
|
|
75
|
-
|
|
76
|
-
```markdown
|
|
77
|
-
<!-- devlyn:verification -->
|
|
78
|
-
## Verification
|
|
79
|
-
|
|
80
|
-
- `node bin/cli.js doctor` exits 0; output contains `doctor:`.
|
|
81
|
-
- `HOME=/nonexistent node bin/cli.js doctor` exits 1; output mentions `/nonexistent`.
|
|
82
|
-
- `node --test tests/cli.test.js` passes.
|
|
83
|
-
- `git diff -- package.json` is empty.
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
The body of the section reads humanely; the machine-readable contract lives in `spec.expected.json` (sibling file). Each command in this section maps to one entry in `spec.expected.json.verification_commands`.
|
|
87
|
-
|
|
88
|
-
If all Requirements are pure-design (no observable runtime check), the body of this section is a single descriptive line (e.g. `- No runtime verification commands.`) and sibling `spec.expected.json` declares `"pure_design": true` instead of listing `verification_commands`.
|
|
89
|
-
|
|
90
|
-
## Sibling file: `spec.expected.json`
|
|
91
|
-
|
|
92
|
-
Schema: `_shared/expected.schema.json`. Required when Requirements have observable checks; optional when all Requirements are pure-design (declare `"pure_design": true` in that case — contradictory, and rejected, alongside a non-empty `verification_commands`). Validate before announcing ready:
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py" --check-expected <expected-path>
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Map mechanically decidable rules from both Requirements and Constraints into the existing carriers. Preserve specific file constraints even when narrower than PLAN's scope. During BUILD_GATE, with `state.base_ref.sha` recorded, the existing `authorized_surface` check enforces PLAN's scope over the run diff and newly created untracked paths (`.devlyn/` exempt). Do not duplicate this general scope check with a whole-checkout scan in `verification_commands`. Validate each new guard with a violating control that fails and an allowed control that passes, preserving permitted pre-existing occurrences:
|
|
99
|
-
- `verification_commands` ← `## Verification` + commands the conversation surfaced. While drafting both Verification and its carrier, omit a standalone check only when another command in that contract runs the same assertions under the required execution conditions and propagates failure; describe that shared coverage in Verification and keep both carriers aligned. Preserve distinct scenarios and required independent verification; execute approved commands unchanged without reusing results across phases. For semantic annotation or exception-handling rules, use a suitable syntax-aware/executable check, preferring existing checks; explicitly retain uncovered semantics as source-review obligations.
|
|
100
|
-
- `forbidden_patterns` ← literal syntax constraints only; regex does not prove semantics. It scans the whole file-filtered diff, including context, deletions and headers; additions-only guards must exclude those lines.
|
|
101
|
-
- `required_files` / `forbidden_files` ← literal file paths from Requirements, Constraints and Out of Scope; `forbidden_files` matches exact Git-relative paths (forward slashes, no leading `./`), including both names of a rename. Globs and directory-wide exclusions need a suitable `verification_commands` guard, not wildcard-looking entries in these lists.
|
|
102
|
-
- `max_deps_added` ← dependency limits in Requirements or Constraints (default 0 unless the spec licenses a new dep).
|
|
103
|
-
- `required_risk_probe_requirements` ← for specs where a Requirement combines ordering/priority, rollback, idempotency, auth/signature, concurrency, atomic-batch, or exact output shape, declare the risk categories PHASE 1.5 risk-probe derivation must cover directly, e.g. `[{"tag": "rollback_state", "derived_from": "<exact ## Verification bullet>"}]`. This replaces guessing the required categories from prose — declare them explicitly so the check works in any language. `tag` must be one of the risk-probe tags in `_shared/spec-verify-check.py:RISK_PROBE_TAGS`; `derived_from` must be an exact substring of `## Verification`. Omit entirely when the spec has no ordering/rollback/idempotency/auth/concurrency/atomic-batch/exact-shape risk.
|
|
104
|
-
|
|
105
|
-
## Quality bar (lint enforces structurally; ideate enforces substantively)
|
|
106
|
-
|
|
107
|
-
Structural (lint):
|
|
108
|
-
- Frontmatter valid + required fields present.
|
|
109
|
-
- All five H2 sections present.
|
|
110
|
-
- Requirements has ≥ 1 bullet.
|
|
111
|
-
- Verification has either commands or `spec.expected.json` declares `"pure_design": true`.
|
|
112
|
-
|
|
113
|
-
Substantive (ideate's job during elicitation):
|
|
114
|
-
- Each Requirement is independently verifiable.
|
|
115
|
-
- Each Constraint has a reasoning clause.
|
|
116
|
-
- Out of Scope explicitly enumerates non-goals.
|
|
117
|
-
- Verification commands actually verify the Requirement they map to (no "looks plausible" verification).
|
|
118
|
-
- Avoid formatter-sensitive single-line adjacency checks — prefer a semantic test (runtime, DB, or CLI check), or grep one stable line-local anchor and assert required context separately.
|
|
119
|
-
- Frontmatter `complexity` reflects verification shape: `high` when the spec combines state mutation with ordering/priority, idempotency, auth/error priority, rollback/failure handling, or exact output shape; `medium` for ordinary multi-step work; `trivial` only for a single localized behavior.
|
|
120
|
-
- When Requirements combine state mutation, ordering/priority, idempotency, auth/error priority, or exact output shape, Verification includes at least one compound scenario that exercises the interaction end-to-end; isolated happy paths are insufficient.
|
|
121
|
-
- Spec text is plain language — no jargon walls, no "for future flexibility" hedging.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# Decision Record Template
|
|
2
|
-
|
|
3
|
-
Generate decision records at `docs/roadmap/decisions/{NNN}-{title}.md`. These capture WHY a decision was made, preventing future agents and collaborators from re-debating settled questions.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
```markdown
|
|
8
|
-
---
|
|
9
|
-
id: [NNN]
|
|
10
|
-
title: "[Decision Title]"
|
|
11
|
-
date: [YYYY-MM-DD]
|
|
12
|
-
status: accepted
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
# [NNN] [Decision Title]
|
|
16
|
-
|
|
17
|
-
## Context
|
|
18
|
-
<!-- What situation or question prompted this decision? -->
|
|
19
|
-
|
|
20
|
-
## Decision
|
|
21
|
-
<!-- What was decided. Be specific and concrete. -->
|
|
22
|
-
|
|
23
|
-
## Alternatives Considered
|
|
24
|
-
|
|
25
|
-
### [Alternative 1]
|
|
26
|
-
- Pros: [advantages]
|
|
27
|
-
- Cons: [disadvantages]
|
|
28
|
-
- Why rejected: [the deciding factor]
|
|
29
|
-
|
|
30
|
-
### [Alternative 2]
|
|
31
|
-
- Pros: [advantages]
|
|
32
|
-
- Cons: [disadvantages]
|
|
33
|
-
- Why rejected: [the deciding factor]
|
|
34
|
-
|
|
35
|
-
## Consequences
|
|
36
|
-
<!-- What changes as a result. Include both positive tradeoffs and costs accepted. -->
|
|
37
|
-
- [Positive consequence]
|
|
38
|
-
- [Cost or constraint accepted]
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Guidelines
|
|
42
|
-
|
|
43
|
-
- Status values: `proposed` | `accepted` | `superseded`
|
|
44
|
-
- Number decisions sequentially: 001, 002, 003...
|
|
45
|
-
- Focus on the "why rejected" for alternatives — this is what prevents re-debating
|
|
46
|
-
- Keep decisions atomic: one decision per record
|
|
47
|
-
- If a decision is later reversed, mark the original as `superseded` and create a new record referencing it
|
|
48
|
-
- Only create decision records for choices that affect multiple roadmap items or have non-obvious reasoning. Don't document every small implementation choice.
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# ROADMAP.md Template
|
|
2
|
-
|
|
3
|
-
Generate `docs/ROADMAP.md` using this structure. This is an **index** — brief descriptions and links to detail specs. No inline specifications.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
```markdown
|
|
8
|
-
# [Project Name] Roadmap
|
|
9
|
-
|
|
10
|
-
> **North Star**: [One sentence from VISION.md]
|
|
11
|
-
|
|
12
|
-
## Phase 1: [Theme Name] (Target: [date or timeframe])
|
|
13
|
-
<!-- 1-2 sentences: what this phase delivers and why it comes first -->
|
|
14
|
-
|
|
15
|
-
| # | Feature | Status | Priority | Complexity | Spec |
|
|
16
|
-
|---|---------|--------|----------|-----------|------|
|
|
17
|
-
| 1.1 | [Feature Name] | Planned | High | Medium | [spec](roadmap/phase-1/1.1-feature-name.md) |
|
|
18
|
-
| 1.2 | [Feature Name] | Planned | High | High | [spec](roadmap/phase-1/1.2-feature-name.md) |
|
|
19
|
-
| 1.3 | [Feature Name] | Planned | Medium | Low | [spec](roadmap/phase-1/1.3-feature-name.md) |
|
|
20
|
-
|
|
21
|
-
## Phase 2: [Theme Name] (Target: [date or timeframe])
|
|
22
|
-
<!-- 1-2 sentences -->
|
|
23
|
-
|
|
24
|
-
| # | Feature | Status | Priority | Complexity | Spec |
|
|
25
|
-
|---|---------|--------|----------|-----------|------|
|
|
26
|
-
| 2.1 | [Feature Name] | Planned | High | Medium | [spec](roadmap/phase-2/2.1-feature-name.md) |
|
|
27
|
-
|
|
28
|
-
## Backlog
|
|
29
|
-
<!-- Ideas acknowledged but not yet phased -->
|
|
30
|
-
| Feature | Reason Deferred | Revisit |
|
|
31
|
-
|---------|----------------|---------|
|
|
32
|
-
| [Name] | [Why not now] | [When to reconsider] |
|
|
33
|
-
|
|
34
|
-
## Decisions
|
|
35
|
-
| # | Decision | Date | Record |
|
|
36
|
-
|---|----------|------|--------|
|
|
37
|
-
| 1 | [Decision Title] | [YYYY-MM-DD] | [record](roadmap/decisions/001-decision-title.md) |
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Guidelines
|
|
41
|
-
|
|
42
|
-
- Status values: `Planned` | `In Progress` | `Done` | `Blocked`
|
|
43
|
-
- Priority: `High` | `Medium` | `Low`
|
|
44
|
-
- Complexity: `Low` | `Medium` | `High`
|
|
45
|
-
- Feature names should be user-facing descriptions, not technical jargon
|
|
46
|
-
- Each phase should deliver usable value — no "infrastructure only" phases
|
|
47
|
-
- The spec column links to the detail spec in `roadmap/phase-N/`
|
|
48
|
-
- Numbering: `{phase}.{item}` (e.g., 1.1, 1.2, 2.1)
|
|
49
|
-
- Slug format for filenames: `{id}-{kebab-case-name}.md` (e.g., `1.1-user-auth.md`)
|
|
50
|
-
- Keep this file under 150 lines — it's an index, not a specification
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# VISION.md Template
|
|
2
|
-
|
|
3
|
-
Generate `docs/VISION.md` using this structure. Keep it under 100 lines — this is a strategic orientation document, not a detailed specification.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
```markdown
|
|
8
|
-
# [Project Name] Vision
|
|
9
|
-
|
|
10
|
-
## North Star
|
|
11
|
-
<!-- One sentence. Every decision should move toward this. -->
|
|
12
|
-
|
|
13
|
-
## Principles
|
|
14
|
-
<!-- 3-5 guiding principles. Each includes what it means AND the tradeoff it implies. -->
|
|
15
|
-
1. **[Principle Name]** — [What this means in practice]. Tradeoff: [what we accept giving up].
|
|
16
|
-
2. ...
|
|
17
|
-
|
|
18
|
-
## Target Users
|
|
19
|
-
### Primary
|
|
20
|
-
<!-- Be specific: role, context, pain, what they need -->
|
|
21
|
-
**[Who]** — [Their situation]. Pain: [what frustrates them]. Need: [what they want].
|
|
22
|
-
|
|
23
|
-
### Not For
|
|
24
|
-
<!-- Explicitly state who this isn't for, so the team doesn't try to serve everyone -->
|
|
25
|
-
- [Audience] — Why: [reason this isn't our focus]
|
|
26
|
-
|
|
27
|
-
## Anti-Goals
|
|
28
|
-
<!-- Things we deliberately choose NOT to do, even if they seem like good ideas -->
|
|
29
|
-
- **[Anti-goal]** — Why: [the reasoning behind this constraint]
|
|
30
|
-
- ...
|
|
31
|
-
|
|
32
|
-
## Success Metrics
|
|
33
|
-
<!-- How we know this is working. Outcomes, not outputs. -->
|
|
34
|
-
- [Metric]: [target] (e.g., "Time to first value: under 5 minutes")
|
|
35
|
-
- ...
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Guidelines
|
|
39
|
-
|
|
40
|
-
- North Star should be ambitious but specific — "make X better" is too vague
|
|
41
|
-
- Principles without tradeoffs are platitudes — if a principle has no cost, it's not a real principle
|
|
42
|
-
- Anti-goals prevent scope creep downstream — be generous with these
|
|
43
|
-
- Success metrics should be measurable, even if the measurement is qualitative
|
|
44
|
-
- Write in the user's primary language, technical terms in English
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
---
|
|
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
|
-
---
|
|
5
|
-
|
|
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.
|
|
7
|
-
|
|
8
|
-
<args>
|
|
9
|
-
$ARGUMENTS
|
|
10
|
-
</args>
|
|
11
|
-
|
|
12
|
-
Before `add` or `drain`, establish the bindings below.
|
|
13
|
-
|
|
14
|
-
Resolve bundled resources from the SKILL.md loaded for this invocation.
|
|
15
|
-
|
|
16
|
-
Reader-rendered directory hint:
|
|
17
|
-
```text
|
|
18
|
-
${CLAUDE_SKILL_DIR}
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Treat the hint as literal path data, never shell code. If the reader
|
|
22
|
-
replaced it with an absolute directory, use that directory. Otherwise
|
|
23
|
-
use the filesystem path or base directory reported for this loaded
|
|
24
|
-
SKILL.md. Resolve virtual URIs through the reader's native filesystem
|
|
25
|
-
mapping. If available source locations name different directories,
|
|
26
|
-
stop.
|
|
27
|
-
|
|
28
|
-
Bind DEVLYN_SKILL_DIR to that absolute directory. Do not obtain this
|
|
29
|
-
binding from an environment variable, cwd, or another installation.
|
|
30
|
-
Verify its SKILL.md before proceeding. Missing or conflicting source
|
|
31
|
-
identity is BLOCKED:skill-source-unresolved; include the failed path
|
|
32
|
-
when known.
|
|
33
|
-
|
|
34
|
-
Resolve bundled references against this directory. These bindings are
|
|
35
|
-
workflow values: establish them explicitly using each tool or shell's
|
|
36
|
-
literal-path rules, and include their absolute values in every fresh
|
|
37
|
-
worker's prompt, telling it to set them from those values, never from
|
|
38
|
-
its inherited environment. Do not rely on shell state surviving between
|
|
39
|
-
calls.
|
|
40
|
-
|
|
41
|
-
Resolve directory symlinks on DEVLYN_SKILL_DIR before deriving its
|
|
42
|
-
sibling _shared. Bind that directory as DEVLYN_SHARED_DIR. References
|
|
43
|
-
written as _shared/... use this binding. Verify the directory and each
|
|
44
|
-
required resource before use; failure is BLOCKED:shared-dir-unresolved
|
|
45
|
-
with the failed path. Never search another installation.
|
|
46
|
-
|
|
47
|
-
For `add`, verify DEVLYN_SKILL_DIR/scripts/append.py before invoking it. For `drain`, before processing the first item, read and obey ../devlyn-resolve/references/outer-loop.md (its scoped-commit order is binding) and read ../devlyn-resolve/references/task-completion.md, both relative to this bound skill directory. Load the sibling devlyn-resolve/SKILL.md by its absolute path and preserve this bundle's DEVLYN_SHARED_DIR when handing off; do not rediscover resolve by its bare command name. In omp, use `printf '%s\n' skill://devlyn-queue`.
|
|
48
|
-
|
|
49
|
-
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.
|
|
50
|
-
|
|
51
|
-
## No args — status
|
|
52
|
-
|
|
53
|
-
Read the queue state above (absent `docs/specs/queue.md` → report "queue empty — nothing staged" and how to add). Print pending `[ ]`, done `[x]`, and blocked `[F]` counts, the next item up, and one usage line per subcommand.
|
|
54
|
-
|
|
55
|
-
## Subcommands
|
|
56
|
-
|
|
57
|
-
- `add <intent text>` — use the Write tool to place the exact full intent in a new file `.devlyn/queue-intent-<unique>.txt` (a name no other add uses, made of letters, digits, `.`, `_` and `-`, e.g. `queue-intent-20261001-1230-k3x9.txt`); do not create it with shell syntax. If the conversation already produced a spec, the file must start with `(spec: docs/specs/<id>/spec.md)`. Then, from the project root, run exactly once `python3 "$DEVLYN_SKILL_DIR/scripts/append.py" .devlyn/queue-intent-<unique>.txt`. The helper consumes that file, appends one `- [ ] <intent>` line at the physical end of `docs/specs/queue.md` under a lock (creating it with its header if missing), and is the sole queue writer. Never edit `docs/specs/queue.md` directly for `add` and never use a direct-edit fallback; a nonzero helper exit is shown to the user and stops the `add`.
|
|
58
|
-
- `drain` — serial drain per the project-instructions contract. For each pending item, in order:
|
|
59
|
-
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.
|
|
60
|
-
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 the sibling devlyn-resolve loaded above with `--spec <path>` hands-free.
|
|
61
|
-
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.
|
|
62
|
-
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.
|
|
63
|
-
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.
|
|
64
|
-
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.
|
|
65
|
-
|
|
66
|
-
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,94 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Append one normalized pending intent to the project queue."""
|
|
3
|
-
|
|
4
|
-
import argparse
|
|
5
|
-
import os
|
|
6
|
-
import re
|
|
7
|
-
import sys
|
|
8
|
-
from contextlib import contextmanager
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
QUEUE_PATH = Path("docs/specs/queue.md")
|
|
13
|
-
LOCK_PATH = Path(".devlyn/queue.lock")
|
|
14
|
-
# Each `add` writes its own handoff file, so two adds never consume each other's intent.
|
|
15
|
-
HANDOFF = re.compile(r"\.devlyn/queue-intent-[A-Za-z0-9._-]+\.txt")
|
|
16
|
-
MINIMAL_HEADER = b"# Intent Queue\n\n"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@contextmanager
|
|
20
|
-
def queue_lock():
|
|
21
|
-
"""One writer at a time across processes, from creating the queue to the append."""
|
|
22
|
-
LOCK_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
23
|
-
fd = os.open(LOCK_PATH, os.O_RDWR | os.O_CREAT, 0o644)
|
|
24
|
-
try:
|
|
25
|
-
if os.name == "nt":
|
|
26
|
-
import msvcrt
|
|
27
|
-
|
|
28
|
-
# The CRT locks bytes that exist; give a new lock file its one byte.
|
|
29
|
-
if os.fstat(fd).st_size == 0:
|
|
30
|
-
os.write(fd, b"\0")
|
|
31
|
-
os.lseek(fd, 0, os.SEEK_SET)
|
|
32
|
-
msvcrt.locking(fd, msvcrt.LK_LOCK, 1)
|
|
33
|
-
try:
|
|
34
|
-
yield
|
|
35
|
-
finally:
|
|
36
|
-
os.lseek(fd, 0, os.SEEK_SET)
|
|
37
|
-
msvcrt.locking(fd, msvcrt.LK_UNLCK, 1)
|
|
38
|
-
else:
|
|
39
|
-
import fcntl
|
|
40
|
-
|
|
41
|
-
fcntl.flock(fd, fcntl.LOCK_EX)
|
|
42
|
-
yield
|
|
43
|
-
finally:
|
|
44
|
-
os.close(fd)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def append_intent(queue_path, intent):
|
|
48
|
-
normalized = " ".join(intent.split())
|
|
49
|
-
if not normalized:
|
|
50
|
-
raise ValueError("intent must contain non-whitespace text")
|
|
51
|
-
item = f"- [ ] {normalized}\n".encode("utf-8")
|
|
52
|
-
|
|
53
|
-
with queue_lock():
|
|
54
|
-
if not queue_path.exists():
|
|
55
|
-
queue_path.parent.mkdir(parents=True, exist_ok=True)
|
|
56
|
-
queue_path.write_bytes(MINIMAL_HEADER + item)
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
with queue_path.open("rb") as queue:
|
|
60
|
-
queue.seek(0, 2)
|
|
61
|
-
size = queue.tell()
|
|
62
|
-
if size:
|
|
63
|
-
queue.seek(-1, 2)
|
|
64
|
-
ends_with_newline = queue.read(1) == b"\n"
|
|
65
|
-
else:
|
|
66
|
-
ends_with_newline = True
|
|
67
|
-
|
|
68
|
-
prefix = b"" if ends_with_newline else b"\n"
|
|
69
|
-
with queue_path.open("ab") as queue:
|
|
70
|
-
queue.write(prefix + item)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def main(argv=None):
|
|
74
|
-
parser = argparse.ArgumentParser(
|
|
75
|
-
description="Append one pending intent at the physical end of docs/specs/queue.md."
|
|
76
|
-
)
|
|
77
|
-
parser.add_argument("handoff", type=Path, help=".devlyn/queue-intent-<unique>.txt, relative to the project root")
|
|
78
|
-
args = parser.parse_args(argv)
|
|
79
|
-
if not HANDOFF.fullmatch(args.handoff.as_posix()):
|
|
80
|
-
parser.error(f"handoff must be .devlyn/queue-intent-<unique>.txt: {args.handoff}")
|
|
81
|
-
try:
|
|
82
|
-
try:
|
|
83
|
-
intent = args.handoff.read_text(encoding="utf-8")
|
|
84
|
-
finally:
|
|
85
|
-
args.handoff.unlink(missing_ok=True)
|
|
86
|
-
append_intent(QUEUE_PATH, intent)
|
|
87
|
-
except (OSError, UnicodeError, ValueError) as error:
|
|
88
|
-
print(f"queue add failed: {error}", file=sys.stderr)
|
|
89
|
-
return 1
|
|
90
|
-
return 0
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if __name__ == "__main__":
|
|
94
|
-
raise SystemExit(main())
|