@miroshnik/elves 2026.1022.0 → 2026.1026.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +55 -54
- package/README.md +23 -17
- package/bin/elves.ts +255 -50
- package/bin/shims/timeout +1 -1
- package/claude/CLAUDE.md +8 -8
- package/dist/github/scripts/github.js +1101 -147
- package/dist/spec/scripts/architecture.js +2 -2
- package/dist/spec/scripts/eslint-config.js +2 -2
- package/dist/spec/scripts/{harness-3yk31f52.js → harness-4hzx0w5b.js} +19 -10
- package/dist/spec/scripts/{harness-xxye8g22.js → harness-bysg15km.js} +41 -23
- package/dist/spec/scripts/harness.js +1 -1
- package/dist/spec/scripts/knip-hints.cjs +1 -1
- package/docs/ci-concurrency.md +1 -0
- package/docs/cloud-sessions.md +4 -4
- package/docs/judgment.md +25 -2
- package/docs/parallel-checkouts.md +14 -3
- package/docs/pr-checks.md +16 -0
- package/docs/production.md +15 -0
- package/docs/prompt-cache.md +78 -0
- package/docs/testing.md +15 -0
- package/package.json +3 -2
- package/skills/ci-wait/scripts/wait-ci.sh +14 -6
- package/skills/dashboard/SKILL.md +3 -1
- package/skills/dashboard/scripts/dashboard.ts +2 -2
- package/skills/dashboard/scripts/sessions.ts +1 -1
- package/skills/est/SKILL.md +6 -3
- package/skills/est/reference.md +68 -11
- package/skills/est/scripts/est.ts +240 -19
- package/skills/github/SKILL.md +38 -40
- package/skills/github/locales/en.json +17 -11
- package/skills/github/locales/ru.json +17 -11
- package/skills/github/reference.md +233 -38
- package/skills/github/scripts/github.ts +1320 -181
- package/skills/github/scripts/locale.ts +36 -5
- package/skills/slot/SKILL.md +4 -3
- package/skills/slot/scripts/slot.ts +3 -3
- package/skills/spec/SKILL.md +16 -12
- package/skills/spec/canon.md +84 -39
- package/skills/spec/checks.md +7 -4
- package/skills/spec/ci.md +19 -1
- package/skills/spec/migration.md +41 -5
- package/skills/spec/reference.md +43 -17
- package/skills/spec/scripts/architecture.ts +1 -1
- package/skills/spec/scripts/eslint-config.ts +9 -7
- package/skills/spec/scripts/harness.ts +25 -12
- package/skills/spec/scripts/knip-hints.cjs +1 -1
- package/skills/spec/scripts/package-run.ts +1 -1
- package/skills/spec/scripts/precommit.ts +202 -0
- package/skills/spec/scripts/spec-break.ts +1 -1
- package/skills/spec/scripts/spec-claims.ts +12 -9
- package/skills/spec/scripts/spec-diff.ts +206 -35
- package/skills/spec/scripts/spec-doc.ts +176 -125
- package/skills/spec/scripts/spec-exceptions.ts +4 -4
- package/skills/spec/scripts/spec-publish.ts +1 -1
- package/skills/spec/scripts/spec-run.ts +1 -1
- package/skills/spec/scripts/spec-shard.ts +1 -1
- package/skills/spec/scripts/speclib.ts +44 -9
- package/skills/task-env/SKILL.md +8 -4
- package/skills/task-env/reference.md +21 -17
- package/skills/task-env/scripts/task-env.ts +89 -23
package/AGENTS.md
CHANGED
|
@@ -14,10 +14,13 @@ repository and its project hook's flow — `docs/cloud-sessions.md`.
|
|
|
14
14
|
repositories, no colleagues or clients — in files, git history or issues;
|
|
15
15
|
examples are made up. **A skill is `SKILL.md` + `scripts/`** in TypeScript
|
|
16
16
|
under Bun (Agent Skills; README, "Repository layout"); repository specifics
|
|
17
|
-
— local configuration. **Budgets** (standard `canon-size`): this file ≤
|
|
17
|
+
— local configuration. **Budgets** (standard `documents#canon-size`): this file ≤
|
|
18
18
|
3,000 words, `SKILL.md` ≤ 2,500, a description ≤ 1,024 characters.
|
|
19
19
|
Here — what always applies and where to go; reference — a skill's
|
|
20
|
-
`reference.md` or `--help`, `docs/`.
|
|
20
|
+
`reference.md` or `--help`, `docs/`. **A file at its budget** — a detail
|
|
21
|
+
moves whole, by its topic, into that topic's md (an existing one, else
|
|
22
|
+
`docs/<topic>.md`), here — a link to it; a fact is never deleted for the
|
|
23
|
+
budget.
|
|
21
24
|
|
|
22
25
|
## First step of a session — an up-to-date flow
|
|
23
26
|
|
|
@@ -28,8 +31,8 @@ follow Elves releases (tags `vYYYY.MM.DD`), a `--link` clone — `origin/main`;
|
|
|
28
31
|
a release is the owner's step (`elves release`), I cut one only when asked.
|
|
29
32
|
|
|
30
33
|
- **The machine is behind** — `update -g`. After an `update` I tell the user
|
|
31
|
-
briefly what changed
|
|
32
|
-
|
|
34
|
+
briefly what changed and re-read what was updated: it applies, not what
|
|
35
|
+
loaded at the start.
|
|
33
36
|
- **The project is behind** (or git ignores flow files) — `update` in the
|
|
34
37
|
task's branch: the package version.
|
|
35
38
|
- **The project keeps a copy of the flow** — the move is a task of its own,
|
|
@@ -41,9 +44,9 @@ a release is the owner's step (`elves release`), I cut one only when asked.
|
|
|
41
44
|
## Language
|
|
42
45
|
|
|
43
46
|
The flow is written in English; the agent works in the **project language**
|
|
44
|
-
(`language` in `.agents/elves.json`, printed by `check
|
|
45
|
-
user, issues, prompts to subagents, documents, commit
|
|
46
|
-
descriptions, `describe`/`it` names, decision docs — briefly. Always English:
|
|
47
|
+
(`language` in `.agents/elves.json`, printed by `check`; none — `github` won't
|
|
48
|
+
write): talking with the user, issues, prompts to subagents, documents, commit
|
|
49
|
+
and PR descriptions, `describe`/`it` names, decision docs — briefly. Always English:
|
|
47
50
|
code, identifiers, terms, conventional-commit type and scope. Names that
|
|
48
51
|
scripts write to GitHub and parse back are exact — `locales/<language>.json`
|
|
49
52
|
of the `github` skill; in `en`: sections `## Questions`, `## Scenarios`;
|
|
@@ -82,17 +85,19 @@ done. The session can be closed.` · `All done, but there are questions: …` ·
|
|
|
82
85
|
external still needs a "yes".
|
|
83
86
|
- **Secrets and bypassing protections — only with permission** (tokens, keys,
|
|
84
87
|
bypass secrets, disabling checks at the host, CI or cloud): first — what
|
|
85
|
-
will appear and where; a secret from chat — never stored,
|
|
88
|
+
will appear and where; a secret from chat — never stored (memory, files,
|
|
89
|
+
commits, issues, logs), rotation reminded.
|
|
86
90
|
|
|
87
91
|
## Task tracking
|
|
88
92
|
|
|
89
93
|
- Tasks live in the repository's **GitHub Issues**, not in local lists, TODO
|
|
90
94
|
files or the agent's task list; no repository — ask where. The `github`
|
|
91
|
-
skill runs them, not `gh issue view`/`edit
|
|
95
|
+
skill runs them, not `gh issue view`/`edit` (descriptions — `task
|
|
96
|
+
edit`); how tasks, the project and
|
|
92
97
|
milestones are organized — its `reference.md`.
|
|
93
98
|
- **Types:** Task (one PR), Bug, Epic (a personal account — the `epic`
|
|
94
|
-
label). **Decision labels** — the
|
|
95
|
-
carries those of the
|
|
99
|
+
label). **Decision labels** — the topic's name, kind by color: a task
|
|
100
|
+
carries those of the topics it introduces or changes, an epic — their
|
|
96
101
|
union.
|
|
97
102
|
- **A big task is an epic** (prefix `<Prefix> · …`) **+ subtasks**
|
|
98
103
|
(sub-issues); **dependencies only as GitHub links** (sub-issue, blocked
|
|
@@ -106,13 +111,13 @@ done. The session can be closed.` · `All done, but there are questions: …` ·
|
|
|
106
111
|
- **One GitHub Project per repository**, named after it, with all tasks;
|
|
107
112
|
`project check` once per session, ❌ — `project fix`. `Status` I set
|
|
108
113
|
**myself, immediately**: `task status`, `task close`, `task drop` (won't
|
|
109
|
-
do, duplicate).
|
|
110
|
-
|
|
111
|
-
with its last task.
|
|
114
|
+
do, duplicate). An organization's open task requires `Priority`
|
|
115
|
+
(`Urgent` → `Low`, `Medium` by default). A milestone (an epic stage, a
|
|
116
|
+
release) I close with its last task.
|
|
112
117
|
|
|
113
118
|
### Session and branch — one task
|
|
114
119
|
|
|
115
|
-
- **Session = task:** `task
|
|
120
|
+
- **Session = task:** `task start` won't take a second one. Another task —
|
|
116
121
|
Backlog and the question «queue #263?», "yes" — its executor at once;
|
|
117
122
|
"do it here" — a one-sentence refusal and the question again. My own
|
|
118
123
|
regression and a fix for a green PR are the same task. A new epic — the
|
|
@@ -135,7 +140,7 @@ done. The session can be closed.` · `All done, but there are questions: …` ·
|
|
|
135
140
|
build`) plus `research` (a spike without code); slug — Latin kebab-case,
|
|
136
141
|
2–5 words; a repository's area prefix goes first (`backend/fix/263-…`). A
|
|
137
142
|
branch named by a worktree tool is renamed before the first push by `github
|
|
138
|
-
task
|
|
143
|
+
task start <N> --branch <type>/<N>-<slug>`.
|
|
139
144
|
- **PR:** the title is a conventional commit, the body has `Closes #263`.
|
|
140
145
|
**Merge — rebase only** (`gh pr merge <N> --rebase`): commits land in
|
|
141
146
|
`main`, each a conventional commit with `Refs #263` (`Closes` would close
|
|
@@ -145,8 +150,8 @@ done. The session can be closed.` · `All done, but there are questions: …` ·
|
|
|
145
150
|
|
|
146
151
|
A task's questions are a numbered list in its description's questions
|
|
147
152
|
section. Taking a task, **first of all** — before the plan, code and
|
|
148
|
-
subagents — I read the questions of the task and its epic (`github task
|
|
149
|
-
<N>`) and **ask the user all open ones (without ✅)** in one message, each
|
|
153
|
+
subagents — I read the questions of the task and its epic (`github task start
|
|
154
|
+
<N>` prints them) and **ask the user all open ones (without ✅)** in one message, each
|
|
150
155
|
with options and a recommendation. What already has an answer (comments,
|
|
151
156
|
code) gets ✅; my own questions join the message and the section. While
|
|
152
157
|
waiting I do what doesn't depend on the answer — what does, I neither start
|
|
@@ -168,10 +173,9 @@ and worktrees") — otherwise there is no actual.
|
|
|
168
173
|
### Epic refresh on task close
|
|
169
174
|
|
|
170
175
|
Once a task is merged, **a subagent with a fresh context** brings the open
|
|
171
|
-
tasks of its epic in line with what shipped: `github task
|
|
172
|
-
does the mechanics
|
|
173
|
-
|
|
174
|
-
`github task close`. The last line of the answer ("Language"): merged,
|
|
176
|
+
tasks of its epic in line with what shipped: `github task finish <N>` waits
|
|
177
|
+
for the deploy, does the mechanics, prints its prompt and, after its report,
|
|
178
|
+
closes the task. The last line of the answer ("Language"): merged,
|
|
175
179
|
deployed, branch removed, epic refreshed, no questions — "all done, can be
|
|
176
180
|
closed"; the owner has something to decide — "all done, but there are
|
|
177
181
|
questions" with them; otherwise — "remaining" with what the session waits for
|
|
@@ -182,16 +186,16 @@ it.
|
|
|
182
186
|
## Git, PRs and merging
|
|
183
187
|
|
|
184
188
|
- **Taking a task** — check it isn't going in a parallel session (an open PR,
|
|
185
|
-
a branch `*/<N>-*`
|
|
189
|
+
a branch `*/<N>-*` — `task start` checks): continue that work or wait
|
|
186
190
|
for the merge; two PRs for one thing — close the second.
|
|
187
191
|
- **Branch only from a fresh `origin/main`** (`git fetch origin && git switch
|
|
188
192
|
-c feat/263-slug origin/main --no-track`). Into `main` only via a PR; a
|
|
189
193
|
direct push and `gh pr merge --admin` — only on the owner's direct
|
|
190
194
|
instruction. **Before a push** — is it my branch.
|
|
191
195
|
- **Commits in logical steps**, tests in the same commit as the code.
|
|
192
|
-
**Before a commit — `lint`, `typecheck`,
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
**Before a commit — one `elves precommit`** (`lint`, `typecheck`,
|
|
197
|
+
`test:spec`, the affected tests; the full run — CI, `docs/testing.md`);
|
|
198
|
+
before merging — a full build; `set -o pipefail` in
|
|
195
199
|
`script | tail`.
|
|
196
200
|
- **I merge as soon as all checks are green** (the preview deploy too; an
|
|
197
201
|
empty list is "not registered yet") and GitHub merges without a conflict;
|
|
@@ -200,9 +204,9 @@ it.
|
|
|
200
204
|
PR's CI — fast checks on the merge with it; red — I fix the root cause;
|
|
201
205
|
`main` red by itself (code 3) — no merge and no fix in the PR: one bug
|
|
202
206
|
for it, I wait for it to close.
|
|
203
|
-
-
|
|
204
|
-
|
|
205
|
-
|
|
207
|
+
- **The tail — `github task finish <N>`**, resumable: push, PR, labels, CI,
|
|
208
|
+
`premerge`, the merge command, the deploy on the merge SHA, refresh, close;
|
|
209
|
+
a batch's — its release's.
|
|
206
210
|
Others' branches, stacks and unmerged work I don't touch.
|
|
207
211
|
- Mechanics — the `github` skill, "Git, PRs and merging — mechanics";
|
|
208
212
|
waiting for checks and deploys — `docs/pr-checks.md`.
|
|
@@ -213,20 +217,20 @@ A project's spec is **decisions with a mechanical check**, functional and
|
|
|
213
217
|
non-functional (a test, a lint rule, an architecture check).
|
|
214
218
|
**No check — no decision; no decision — no code:** behavior without a check
|
|
215
219
|
may change without agreement; code no decision claims is dead. A check is
|
|
216
|
-
written before the code
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
`capabilities
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
section (future `it` names) before
|
|
226
|
-
|
|
227
|
-
recurring decision — a "Standard · …"
|
|
228
|
-
repeats in a PR's files — fixed in it.
|
|
229
|
-
skill (`canon.md`, `checks.md`).
|
|
220
|
+
written before the code, red first (acceptance), then holds the decision
|
|
221
|
+
(regression); a decision changes through its check: the check first, red,
|
|
222
|
+
then the code. Three `tests/` roots by the reader's question:
|
|
223
|
+
`capabilities` (what the system does), `architecture` (what it's made of),
|
|
224
|
+
`standards` (code rules, qualities); a decision is a topic with sections
|
|
225
|
+
(`<topic>.md` — the subject, `<section>.md` — why, `<section>.test.ts` —
|
|
226
|
+
statements), a new rule — a section of the topic covering it.
|
|
227
|
+
Exceptions — a mark in the element's file or an `exceptions/` file, with an
|
|
228
|
+
`#issue`, a reason, a ratchet. Test names are statements in the project
|
|
229
|
+
language; a code task gets the scenarios section (future `it` names) before
|
|
230
|
+
code. The PR body is `spec-diff` output; docs — generated (`spec-doc`),
|
|
231
|
+
published by CI to the `spec` branch. A recurring decision — a "Standard · …"
|
|
232
|
+
issue, a rule in `tests/standards/`; repeats in a PR's files — fixed in it.
|
|
233
|
+
The full canon and wiring — the `spec` skill (`canon.md`, `checks.md`).
|
|
230
234
|
|
|
231
235
|
## Tests and code
|
|
232
236
|
|
|
@@ -261,8 +265,7 @@ skill (`canon.md`, `checks.md`).
|
|
|
261
265
|
permissions, audited settings) never changes by direct DML: the audit is
|
|
262
266
|
lost, guards bypassed; what the UI lacks is a code change.
|
|
263
267
|
- **Production numbers — first, is it the right database:** the first query
|
|
264
|
-
checks it against the repository
|
|
265
|
-
magnitude); no match — stop.
|
|
268
|
+
checks it against the repository; no match — stop (`docs/production.md`).
|
|
266
269
|
- **"Didn't help" → production facts, not a third reproduction:** the logs
|
|
267
270
|
of the real request and production data (the row, `updated_at`, audit),
|
|
268
271
|
then a new hypothesis.
|
|
@@ -273,16 +276,15 @@ skill (`canon.md`, `checks.md`).
|
|
|
273
276
|
packages or touch files "to try" gets an isolated worktree or "read-only
|
|
274
277
|
plus a temp folder" without `git checkout/restore/stash/clean`: its cleanup
|
|
275
278
|
reverts everything uncommitted, others' work too. Several agents in one
|
|
276
|
-
worktree
|
|
277
|
-
a fan-out — `git status` and `git diff --stat`.
|
|
278
|
-
subagents get a unique prefix (task, role).
|
|
279
|
+
worktree, temp files shared with subagents — `docs/parallel-checkouts.md`.
|
|
280
|
+
After a fan-out — `git status` and `git diff --stat`.
|
|
279
281
|
- **A fresh worktree lacks untracked files** (dependencies, `.env*.local`) —
|
|
280
282
|
I install and copy them. Paths in commands and assignments lead into the
|
|
281
283
|
worktree (a path "from memory" edits `main`); `git -C <worktree> status`
|
|
282
284
|
before and after an edit.
|
|
283
285
|
- **Parallel checkouts share ports, databases and `.git/config`:** the task's
|
|
284
286
|
apps, databases and e2e run in its own Compose stack (the `task-env`
|
|
285
|
-
skill)
|
|
287
|
+
skill), not on a host port; others'
|
|
286
288
|
stacks and busy ports I don't touch — `docs/parallel-checkouts.md`.
|
|
287
289
|
- **A subagent gets a unit of ~50 turns** (one decision's folder, one file,
|
|
288
290
|
one spec), not a 200-turn zone: its price is quadratic in turns. The
|
|
@@ -294,7 +296,7 @@ skill (`canon.md`, `checks.md`).
|
|
|
294
296
|
## Project knowledge and handoff
|
|
295
297
|
|
|
296
298
|
- **Shared — in Elves, project-specific — in the project's spec**
|
|
297
|
-
("Specification — decisions"), the reason in the
|
|
299
|
+
("Specification — decisions"), the reason in the section's `<section>.md`;
|
|
298
300
|
neither shared nor checkable (operations, product documents) — the
|
|
299
301
|
project's `docs/`. The project's `AGENTS.md` — the install block and how
|
|
300
302
|
its mode differs. An agreement from chat goes there too, not into the
|
|
@@ -307,7 +309,6 @@ skill (`canon.md`, `checks.md`).
|
|
|
307
309
|
|
|
308
310
|
## CI: parallel tasks
|
|
309
311
|
|
|
310
|
-
Branches and PRs
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
Workflow fragments — `docs/ci-concurrency.md`.
|
|
312
|
+
Branches and PRs in parallel, `main`, tags and manual runs never cancelled,
|
|
313
|
+
deploy — a queue per environment; others' runs I don't cancel or re-run —
|
|
314
|
+
`docs/ci-concurrency.md`.
|
package/README.md
CHANGED
|
@@ -72,14 +72,16 @@ Any decision can be changed — through its check: the check changes first and g
|
|
|
72
72
|
|
|
73
73
|
| Decision | Kind | Check | Lives in | Label |
|
|
74
74
|
|---|---|---|---|---|
|
|
75
|
-
| An order survives a restart, a cart lives for the session | functional | an integration test | `tests/capabilities/orders` | 🟩 |
|
|
76
|
-
| The manager sees overdue invoices first | functional | an e2e test | `tests/capabilities/invoices` | 🟩 |
|
|
77
|
-
| Search answers in under 300 ms at 100 requests per second | non-functional, a quality of one capability | a load test with the budget | `tests/capabilities/search` | 🟩 |
|
|
78
|
-
| Every input form has a reset button | non-functional, a standard | "registry + invariant": every form from the code, a test per form | `tests/standards/
|
|
79
|
-
| Code is TypeScript | non-functional, a standard | "registry + invariant": every source file is `.ts` | `tests/standards/typescript` | 🟦 |
|
|
75
|
+
| An order survives a restart, a cart lives for the session | functional | an integration test | `tests/capabilities/orders/persistence` | 🟩 |
|
|
76
|
+
| The manager sees overdue invoices first | functional | an e2e test | `tests/capabilities/invoices/overdue-first` | 🟩 |
|
|
77
|
+
| Search answers in under 300 ms at 100 requests per second | non-functional, a quality of one capability | a load test with the budget | `tests/capabilities/search/latency` | 🟩 |
|
|
78
|
+
| Every input form has a reset button | non-functional, a standard | "registry + invariant": every form from the code, a test per form | `tests/standards/forms/reset-button` | 🟦 |
|
|
79
|
+
| Code is TypeScript | non-functional, a standard | "registry + invariant": every source file is `.ts` | `tests/standards/source-files/typescript` | 🟦 |
|
|
80
80
|
| Data is stored in Postgres | non-functional, architecture | the model: the `db` container and its `pg` client; a module importing another driver — red | `tests/architecture/model.ts` | 🟧 |
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
A decision is a **topic with sections**: the topic is the subject (`orders`, `forms`) — a product area as the user names it, or the kind of element a rule applies to; a section is one statement about it (`reset-button`), with its reason in `<section>.md` and its checks in `<section>.test.ts`. A new rule goes in as a section of the topic whose subject covers it, not as a new folder.
|
|
83
|
+
|
|
84
|
+
**Decision labels** on GitHub tasks name the topic — a section has none — and show its kind by color: 🟩 green — a capability, functional; 🟦 blue — a standard and 🟧 orange — architecture, both non-functional. The color follows where the decision lives, so the budget of one capability, kept next to its behavior, is green; `github project check` holds the colors. How each check is wired — the [`spec`](skills/spec/SKILL.md) skill.
|
|
83
85
|
|
|
84
86
|
## Why
|
|
85
87
|
|
|
@@ -99,7 +101,7 @@ The full canon is [`AGENTS.md`](AGENTS.md); here is what it stands on.
|
|
|
99
101
|
|
|
100
102
|
1. **Code is what is; checks are what should be.** Code describes what the system does and how it is built now, so it can't be the source of what should be. What should be lives in checks, and they are all of the project's decisions: functional — tests in `tests/capabilities`, what the system must do; non-functional — architectural (`tests/architecture`, what it's made of) and standards (`tests/standards`, which rules the code obeys and which qualities it holds: performance, security, accessibility). They diverge — red CI, not silent drift. A decision changes through its check: the check first, red on the current code, then the code. → [Specification — decisions](AGENTS.md#specification--decisions)
|
|
101
103
|
2. **No check — no decision.** A decision exists while a mechanism checks it: behavior — a test, a convention — a lint rule or "registry + invariant", architecture — a C1–C3 model with checks over it, a quality — a budget in a capability's test or a standard's cross-cutting invariant. Prose doesn't have to be true; a test fails. Documentation is generated from test names and published to the `spec` branch after merge: it isn't in `main` — a derived copy would conflict in every parallel PR, and review goes by the diff of requirements (`spec-diff`). → [Specification — decisions](AGENTS.md#specification--decisions)
|
|
102
|
-
3. **One fact — one home.** Every fact has exactly one place: a cross-cutting rule — in a standard, behavior — in its capability, a decision's reason — in `<
|
|
104
|
+
3. **One fact — one home.** Every fact has exactly one place: a cross-cutting rule — in a standard, behavior — in its capability, a decision's reason — in `<section>.md` next to its check, the plan and agreements — in the issue, what is shared by all projects — in Elves, what is project-specific — in the project's spec. A copy goes stale silently: that's how specs diverge — a change edits one place out of several. → [Project knowledge and handoff](AGENTS.md#project-knowledge-and-handoff)
|
|
103
105
|
4. **A check must be able to fail.** A test is written before the code and is red first — the decision's acceptance test; green after the code, it stays as its regression test. A regression test is proven by reverting the fix, an inherited one — by breaking the code. A test that can't fail is worse than a missing one. → [Tests and code](AGENTS.md#tests-and-code)
|
|
104
106
|
5. **The issue is the only place to agree.** The goal, `## Questions` with answers, `## Scenarios` — future test names, visible before code; status and links — GitHub fields and relationships. No proposal files, no TODOs, no plan in the agent's memory. → [Task tracking](AGENTS.md#task-tracking)
|
|
105
107
|
6. **Session = task = branch = PR.** Session `#42 …`, branch `feat/42-slug`, `Closes #42` in the PR: work is always linked to a task, so the task has an actual. A session won't take a second task into work (`task status` refuses): it goes to Backlog, and on the owner's "yes" to queuing it — to a background executor, a subagent in its own worktree. → [Session and branch](AGENTS.md#session-and-branch--one-task)
|
|
@@ -120,7 +122,7 @@ The full canon is [`AGENTS.md`](AGENTS.md); here is what it stands on.
|
|
|
120
122
|
| **Agreement before code** | `proposal.md`, `tasks.md` | the issue: `## Questions` and `## Scenarios` — future test names |
|
|
121
123
|
| **A change in the PR** | ADDED / MODIFIED / REMOVED deltas — separate files written by the agent | `spec-diff` from git: added, changed and removed requirements |
|
|
122
124
|
| **Code standards** | text in `config.yaml` and specs | `tests/standards/`: a lint rule or an invariant with a violator, exceptions with a ratchet |
|
|
123
|
-
| **Architecture and "why"** | `design.md` in the change archive | a C1–C3 model with checks; the reason — in `<
|
|
125
|
+
| **Architecture and "why"** | `design.md` in the change archive | a C1–C3 model with checks; the reason — in `<section>.md` next to the check |
|
|
124
126
|
| **Qualities** (performance, security, accessibility) | a prose requirement, like behavior | cross-cutting — a "registry + invariant" standard (audit on every mutation, axe on every page); one capability's budget — in its tests |
|
|
125
127
|
| **Documentation** | the specs themselves — read them whole | generated from test names into the `spec` branch |
|
|
126
128
|
| **Uncheckable (look, copy)** | a prose requirement | not called spec — acceptance in the issue; where the look is a decision, it's a screenshot test |
|
|
@@ -178,7 +180,7 @@ An agent's sandbox (Claude Code's Bash) doesn't let bunx write its cache `~/.bun
|
|
|
178
180
|
|
|
179
181
|
`install` in a project asks in the terminal whether the agent should do tasks entirely by itself — answer the task's questions by recommendation, commit, push, fix CI and merge its own PR on green checks (default — no); without a terminal — `install --auto` or `--no-auto`, without the flag the previous answer stays. `check` names the mode. With auto, `install` prints a hint for Claude Code: the app rejects a merge without human review in this mode too, and the session merges its own PR by itself only with the `Bash(gh pr merge *)` rule in `permissions.allow` of `~/.claude/settings.json` — the owner adds it, `install` doesn't change the machine's permission settings.
|
|
180
182
|
|
|
181
|
-
`install` also asks for the **project language** the agent works in: dialog, tasks, prompts, documents; the flow itself is written in English. It is stored in `.agents/elves.json` as `language` — a BCP 47 code (`ru`, `en`, `pt-BR`), `en` by default; an install of a previous version without the field counts as `ru`. Without a terminal — `install --lang <code>`, without the flag the previous language stays; `update` keeps it, `check` and the hook name it. The flow isn't installed into an Elves clone — `install` writes only its manifest there: these two settings, `env` and `batch` carried over as in a project.
|
|
183
|
+
`install` also asks for the **project language** the agent works in: dialog, tasks, prompts, documents; the flow itself is written in English. It is stored in `.agents/elves.json` as `language` — a BCP 47 code (`ru`, `en`, `pt-BR`), `en` by default; an install of a previous version without the field counts as `ru`. Without a terminal — `install --lang <code>`, without the flag the previous language stays; `update` keeps it, `check` and the hook name it. The manifest belongs in git: in a checkout without it the language is unknown, the `github` skill refuses to write names to GitHub, and `check` names a manifest that is missing or not committed. The flow isn't installed into an Elves clone — `install` writes only its manifest there: these two settings, `env` and `batch` carried over as in a project.
|
|
182
184
|
|
|
183
185
|
<details>
|
|
184
186
|
<summary><b>What goes where</b></summary>
|
|
@@ -193,7 +195,7 @@ An agent's sandbox (Claude Code's Bash) doesn't let bunx write its cache `~/.bun
|
|
|
193
195
|
- Updating — `update` bumps the package version: `package.json` and the lockfile, a few lines in the branch of the current task, committed with it. A project with a copy of the flow of an earlier release moves to a thin one in a task of its own, one per project — `check` prints its `task new`, the hook names the canon loaded twice until then: `update` removes the copy, its symlinks and `.gitignore` exceptions, leads imports and CI paths into the copy to the package and lists what else names `.agents`.
|
|
194
196
|
- The manifest, the shim, `AGENTS.md` and `.claude/settings.json`, if the project's git ignores them (`.agents/` or `.claude/` in `.gitignore`), are un-ignored by `install` and `update` with exceptions — an Elves block at the end of `.gitignore`, committed with them; what an exception can't un-ignore (a directory excluded by a personal rule), and anything ignored until `update` runs — `❌` in the output of the install and of `check`.
|
|
195
197
|
|
|
196
|
-
**Onto the machine** (`-g`) — the canon, the skills and the hook: a thin project keeps no copy of the canon, its agents get it from the machine. `~/.agents/elves/` — `AGENTS.md`, `claude/CLAUDE.md`, `docs/*.md`; Claude Code — symlinks `~/.claude/rules/elves.md` and `elves-claude.md` (loaded in every project without the external-imports dialog), `~/.claude/skills` and the `SessionStart` hook in `~/.claude/settings.json`; the other agents — their global rule file linking to the canon (`~/.codex/AGENTS.md`, `~/.gemini/GEMINI.md`, `~/.copilot/copilot-instructions.md`, `~/.config/opencode/AGENTS.md`, `~/.config/amp/AGENTS.md`; a file of your own stays) and `~/.agents/skills`. The `elves` command in the terminal — in `~/.local/bin`, or the first directory of `PATH` inside the home directory you can write to: with `--link` a symlink to the clone's `bin/elves.ts`, otherwise a shim to the machine's release (`bunx @miroshnik/elves@<version>`) that `update -g` rewrites; an `elves` of your own (a script, a global package) stays, and `check -g` names it, a command missing or of another release, an `elves` earlier on `PATH`, and — when `PATH` has no such directory — the command that adds `~/.local/bin`. A project that still has a copy loads the canon twice until it moves — the hook names it. A Claude Code cloud session gets the same from the project's `SessionStart` hook — `npm i -g bun@latest @miroshnik/elves && elves install -g` (the image's Bun lags behind), then `check --hook`; the machine's hook isn't there (the cloud doesn't read `~/.claude/settings.json`), an environment's setup script with the same command is an optional faster start. In an Elves clone with `--link` the machine's canon is the clone; its `.claude/CLAUDE.md` keeps the root `AGENTS.md` from loading a second time.
|
|
198
|
+
**Onto the machine** (`-g`) — the canon, the skills and the hook: a thin project keeps no copy of the canon, its agents get it from the machine. `~/.agents/elves/` — `AGENTS.md`, `claude/CLAUDE.md`, `docs/*.md`; Claude Code — symlinks `~/.claude/rules/elves.md` and `elves-claude.md` (loaded in every project without the external-imports dialog), `~/.claude/skills` and the `SessionStart` hook in `~/.claude/settings.json`; the other agents — their global rule file linking to the canon (`~/.codex/AGENTS.md`, `~/.gemini/GEMINI.md`, `~/.copilot/copilot-instructions.md`, `~/.config/opencode/AGENTS.md`, `~/.config/amp/AGENTS.md`; a file of your own stays) and `~/.agents/skills`. The `elves` command in the terminal — in `~/.local/bin`, or the first directory of `PATH` inside the home directory you can write to: with `--link` a symlink to the clone's `bin/elves.ts`, otherwise a shim to the machine's release (`bunx @miroshnik/elves@<version>`) that `update -g` rewrites; an `elves` of your own (a script, a global package) stays, and `check -g` names it, a command missing or of another release, an `elves` earlier on `PATH`, and — when `PATH` has no such directory — the command that adds `~/.local/bin`. A project that still has a copy loads the canon twice until it moves — the hook names it. Claude Code's prompt cache TTL is your choice: with `promptCacheTtl` or `subagentPromptCacheTtl` not set, `install -g`, `update -g`, `check -g` and the hook print a command that sets the hour and leaves the rest of `~/.claude/settings.json` alone — none of them edits the file (`docs/prompt-cache.md`). A Claude Code cloud session gets the same from the project's `SessionStart` hook — `npm i -g bun@latest @miroshnik/elves && elves install -g` (the image's Bun lags behind), then `check --hook`; the machine's hook isn't there (the cloud doesn't read `~/.claude/settings.json`), an environment's setup script with the same command is an optional faster start. In an Elves clone with `--link` the machine's canon is the clone; its `.claude/CLAUDE.md` keeps the root `AGENTS.md` from loading a second time.
|
|
197
199
|
|
|
198
200
|
**Personal configuration** — everything that depends on specific repositories (the registry for `est`, your own model prices, notes) — lives outside the flow, in `~/.config/elves`; another directory — the `ELVES_CONFIG_DIR` variable, read by all skill scripts. `ELVES_PRIVATE=<path>` with `install -g` makes `~/.config/elves` a symlink to a private checkout; with `ELVES_CONFIG_DIR` the symlink isn't needed — the variable points at the checkout itself.
|
|
199
201
|
|
|
@@ -207,7 +209,7 @@ An agent's sandbox (Claude Code's Bash) doesn't let bunx write its cache `~/.bun
|
|
|
207
209
|
The machine's copy and a project's package version follow the flow **by releases** — tags `vYYYY.MM.DD` (a same-day patch is `.N`), not the head of `main`: a commit to the rules without a release doesn't make anyone "behind". The commands go through the npm package, and it is a release itself: `install` from it installs its own version without the network, `check` and `update` restart from the latest release's npm package. The fallback `bunx github:miroshnik/elves` installs the package from `main` once and then runs it from its cache, so that package is only a launcher: it finds the latest tag and restarts from it by its SHA; in a cloud session `github:` gives 403.
|
|
208
210
|
|
|
209
211
|
- `check` changes nothing: it compares the machine's copy and a project's package version with the latest release (the same install as a dry run, a list of differences), a `--link` clone — with `origin/main` after `git fetch`.
|
|
210
|
-
- `update`: the machine's copy — reinstalled from the latest release; a project — the package version raised to it, a copy of the flow moved to a thin project; a clone — `git pull --ff-only` and links to new skills (a clone not on `main` or with edits — refused with a reason). It says what changed, and the agent tells the user: the machine's copy and a project — every release after the installed one (a project — after its package version), newest first, with the list of its GitHub Release; a clone — the flow commits it pulled.
|
|
212
|
+
- `update`: the machine's copy — reinstalled from the latest release; a project — the package version raised to it, a copy of the flow moved to a thin project; a clone — `git pull --ff-only` and links to new skills (a clone not on `main` or with edits — refused with a reason). It says what changed, and the agent tells the user: the machine's copy and a project — every release after the installed one (a project — after its package version), newest first, with the list of its GitHub Release; a clone — the flow commits it pulled. A project's update is committed into the current task's branch; no task branch — the agent doesn't commit it and says so.
|
|
211
213
|
- `release` — the owner's step: a date tag (UTC) on `origin/main` and a GitHub Release listing the flow and installer changes since the previous release (the same list `update` prints); nothing to release — refused.
|
|
212
214
|
- The npm package `@miroshnik/elves` — CI publishes every release tag (`.github/workflows/npm.yml`): `elves pack <tag>` sets the version (`vYYYY.MM.DD.N` → `YYYY.MMDD.N`, versions sort as tags) and builds for Node the modules a project imports, then `npm publish` by npm [trusted publishing](https://docs.npmjs.com/trusted-publishers) — OIDC, no token or secret, provenance included. The owner's step, once: a trusted publisher is set in the package settings on npmjs.com, so first a `0.0.0` stub published from the owner's machine; then the trusted publisher — GitHub Actions, `miroshnik/elves`, workflow `npm.yml` — no earlier than 2 days before the first release from CI (a new one expires without a successful publish within 2 days: delete it and create it again); after that release the stub may be `npm deprecate`d.
|
|
213
215
|
|
|
@@ -227,6 +229,7 @@ bun --no-env-file node_modules/.bin/elves spec-shard …
|
|
|
227
229
|
bun --no-env-file node_modules/.bin/elves spec-claims …
|
|
228
230
|
bun --no-env-file node_modules/.bin/elves spec-break …
|
|
229
231
|
bun --no-env-file node_modules/.bin/elves spec-exceptions …
|
|
232
|
+
bun --no-env-file node_modules/.bin/elves precommit
|
|
230
233
|
bun --no-env-file node_modules/.bin/elves task-env up …
|
|
231
234
|
bun --no-env-file node_modules/.bin/elves task-env e2e …
|
|
232
235
|
bun --no-env-file node_modules/.bin/elves task-env down …
|
|
@@ -258,9 +261,9 @@ Skills are installed onto the machine with the flow (`install -g`); the agent pi
|
|
|
258
261
|
|
|
259
262
|
### [`github`](skills/github/SKILL.md) — project and tasks by the canon
|
|
260
263
|
|
|
261
|
-
Checks and fixes the GitHub project and the default-branch rule, creates a task with all its fields in one command, shows a task with its epic, blockers, PR checks and open questions in one output
|
|
264
|
+
Checks and fixes the GitHub project and the default-branch rule, creates a task with all its fields in one command, takes a task's tail from its branch to its close by one resumable command — the PR, CI, the merge command, the deploy, the refresh, the close — shows a task with its epic, blockers, PR checks and open questions in one output, puts an answer at a question and edits the description in one call, before merging checks the PR's merge with a fresh `main` if it moved after CI, after merging does the epic refresh's mechanics and prints the subagent's prompt, and closes the task in one call — `est` actual, Done, epic and milestone, the task's Compose stacks and its merged branches removed — sets decision labels from the PR diff, names the wait a resumed session restarts for its task, and gives a dispatcher its queue — the ready tasks of an epic or a filter in order, each with the prompt of its executor, a background subagent; in a batch project the executors merge into the dispatcher's batch branch, and it reaches `main` by one release PR — one full CI run and one deploy.
|
|
262
265
|
|
|
263
|
-
`github project check` · `github project fix` · `github task new | show | answer | status | drop | actualize | close | resume | queue` · `github batch release` · `github pr labels | premerge`
|
|
266
|
+
`github project check` · `github project fix` · `github task new | show | answer | edit | start | status | drop | actualize | close | finish | resume | queue` · `github batch release` · `github pr labels | premerge`
|
|
264
267
|
|
|
265
268
|
### [`est`](skills/est/SKILL.md) — estimates from actuals
|
|
266
269
|
|
|
@@ -284,6 +287,7 @@ A browser page the script serves itself, with two tabs. Sessions (the main one)
|
|
|
284
287
|
- `spec-claims` — every entry point (route, page, job, command) is called by a capability test — by the call log, not by coverage.
|
|
285
288
|
- `spec-break` — proves that a check catches a breakage: break the code → run the test → always revert.
|
|
286
289
|
- `spec-exceptions` — moves a project's exceptions from `exceptions.ts` into the `exceptions/` directory of the decision folder, one file per exception: parallel PRs paying down debt don't conflict.
|
|
290
|
+
- `precommit` — the checks before a commit in one call: `lint`, `typecheck`, `test:spec` and `test --changed=<base>` at once, each through the machine queue; a line per check, the details of the failed ones only; a test check's result — by the runner's summary, not the exit code.
|
|
287
291
|
- `harness.ts` — mechanical checks: "registry + invariant" with a violator, lint-rule examples, exceptions with a ratchet (an element with its own file — a mark in it), the C1–C3 architecture model, dead code (knip, with the config-hints reporter `knip-hints.cjs`), the environment.
|
|
288
292
|
|
|
289
293
|
### [`ci-wait`](skills/ci-wait/SKILL.md) — wait for checks, don't guess
|
|
@@ -311,9 +315,11 @@ Mechanics the canon refers to; the agent reads them as needed.
|
|
|
311
315
|
- [`docs/pr-checks.md`](docs/pr-checks.md) — waiting for PR and commit checks: polling, `bucket`, empty-list traps; a deploy — by the commit status, not by polling the site; the app's Auto-fix — off: a conflict is a rebase, not a merge of the base.
|
|
312
316
|
- [`docs/ci-concurrency.md`](docs/ci-concurrency.md) — CI for parallel tasks: the full rule and workflow fragments — parallel branches, a non-cancellable `main`, a deploy queue.
|
|
313
317
|
- [`docs/testing.md`](docs/testing.md) — how to test: what runs before a commit and what is left to CI, a run's result by the runner's summary, the environment, e2e without flakes, UI state in the agent's browser — by the DOM, not a screenshot.
|
|
314
|
-
- [`docs/judgment.md`](docs/judgment.md) — judgment and edits: a mistake disguised as an outcome, a label is a claim, editing exactly what was named, "it doesn't reproduce for me".
|
|
315
|
-
- [`docs/
|
|
318
|
+
- [`docs/judgment.md`](docs/judgment.md) — judgment and edits: a mistake disguised as an outcome, a label is a claim, editing exactly what was named, "it doesn't reproduce for me", what reached the human, the reasons behind the tool-documentation and memory rules.
|
|
319
|
+
- [`docs/production.md`](docs/production.md) — production: a migration by the regular deploy, how the first query checks it is the right database, why "didn't help" goes to production facts.
|
|
320
|
+
- [`docs/parallel-checkouts.md`](docs/parallel-checkouts.md) — parallel worktrees: the full rule, several agents in one worktree and shared temp files, the task's Compose stack and what the compose file holds, callbacks of external services, the runner config, a shared `.git/config` — no `git config` in copies.
|
|
316
321
|
- [`docs/cloud-sessions.md`](docs/cloud-sessions.md) — the Claude Code cloud session: what it can't access, its brief (`#` only for its task) and how to close a task from there.
|
|
322
|
+
- [`docs/prompt-cache.md`](docs/prompt-cache.md) — Claude Code's prompt cache TTL: 5 minutes for subagents and on usage credits, the keys and variables for the hour, when it pays off, the owner's command `check -g` prints, how to see it in a transcript.
|
|
317
323
|
|
|
318
324
|
## Repository layout
|
|
319
325
|
|
|
@@ -327,7 +333,7 @@ bin/elves.ts the installer — TypeScript under Bun too: bunx runs it from
|
|
|
327
333
|
bin/shims/ commands the SessionStart hook adds to the end of the agent's Bash PATH: timeout
|
|
328
334
|
```
|
|
329
335
|
|
|
330
|
-
**A skill = a prompt + scripts** following the open [Agent Skills](https://agentskills.io) standard: a `<name>/` folder with `SKILL.md` (frontmatter `name` = the folder name, `description` ≤ 1,024 characters; at most 2,500 words — when and what to call) and `scripts/` for the deterministic part; reference material — `reference.md` and the scripts' `--help`. The canon is budgeted too: `AGENTS.md` ≤ 3,000 words, `claude/CLAUDE.md` ≤ 700 (standard `canon-size`). Scripts are TypeScript under Bun (`bun script.ts`: no build, no dependencies, imports with `.ts`); script tests — `bun test` in the `tests/` of Elves over the same tree. The installer `bin/elves.ts` is the same TypeScript under Bun and imports skill modules instead of copying their values. A skill imports another skill only by the dependency list of the `skill-imports` standard: `github`, `task-env`, `ci-wait` and `slot` import none, so a rule they share with a neighbor lives as a copy in each script, and a standard keeps the copies identical. Third-party code a script can't do without goes into `scripts/vendor/` as a file with its license, not as a dependency: that's how `spec` carries a TypeScript parser (`@babel/parser`). Everything that depends on specific repositories goes into local configuration, not into the skill.
|
|
336
|
+
**A skill = a prompt + scripts** following the open [Agent Skills](https://agentskills.io) standard: a `<name>/` folder with `SKILL.md` (frontmatter `name` = the folder name, `description` ≤ 1,024 characters; at most 2,500 words — when and what to call) and `scripts/` for the deterministic part; reference material — `reference.md` and the scripts' `--help`. The canon is budgeted too: `AGENTS.md` ≤ 3,000 words, `claude/CLAUDE.md` ≤ 700 (standard `documents#canon-size`). Scripts are TypeScript under Bun (`bun script.ts`: no build, no dependencies, imports with `.ts`); script tests — `bun test` in the `tests/` of Elves over the same tree. The installer `bin/elves.ts` is the same TypeScript under Bun and imports skill modules instead of copying their values. A skill imports another skill only by the dependency list of the `scripts#skill-imports` standard: `github`, `task-env`, `ci-wait` and `slot` import none, so a rule they share with a neighbor lives as a copy in each script, and a standard keeps the copies identical. Third-party code a script can't do without goes into `scripts/vendor/` as a file with its license, not as a dependency: that's how `spec` carries a TypeScript parser (`@babel/parser`). Everything that depends on specific repositories goes into local configuration, not into the skill.
|
|
331
337
|
|
|
332
338
|
Elves lives by its own flow: the spec — [`tests/`](tests), its documentation — in the [`spec` branch](https://github.com/miroshnik/elves/tree/spec), tasks — in [Issues](https://github.com/miroshnik/elves/issues) and the [elves project](https://github.com/users/miroshnik/projects/6).
|
|
333
339
|
|
|
@@ -349,4 +355,4 @@ bun run typecheck # bun check — Bun's own type checker, Bun 1.4.3 from pac
|
|
|
349
355
|
bun run spec:doc # docs/spec from test names, --strict
|
|
350
356
|
```
|
|
351
357
|
|
|
352
|
-
The README is part of the flow too: a new skill, command, script subcommand or reference without a line here fails `bun test`, and so does a link to a renamed canon section ([`tests/standards/readme`](tests/standards/
|
|
358
|
+
The README is part of the flow too: a new skill, command, script subcommand or reference without a line here fails `bun test`, and so does a link to a renamed canon section ([`tests/standards/documents/readme`](tests/standards/documents/readme.md)).
|