@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.
Files changed (60) hide show
  1. package/AGENTS.md +55 -54
  2. package/README.md +23 -17
  3. package/bin/elves.ts +255 -50
  4. package/bin/shims/timeout +1 -1
  5. package/claude/CLAUDE.md +8 -8
  6. package/dist/github/scripts/github.js +1101 -147
  7. package/dist/spec/scripts/architecture.js +2 -2
  8. package/dist/spec/scripts/eslint-config.js +2 -2
  9. package/dist/spec/scripts/{harness-3yk31f52.js → harness-4hzx0w5b.js} +19 -10
  10. package/dist/spec/scripts/{harness-xxye8g22.js → harness-bysg15km.js} +41 -23
  11. package/dist/spec/scripts/harness.js +1 -1
  12. package/dist/spec/scripts/knip-hints.cjs +1 -1
  13. package/docs/ci-concurrency.md +1 -0
  14. package/docs/cloud-sessions.md +4 -4
  15. package/docs/judgment.md +25 -2
  16. package/docs/parallel-checkouts.md +14 -3
  17. package/docs/pr-checks.md +16 -0
  18. package/docs/production.md +15 -0
  19. package/docs/prompt-cache.md +78 -0
  20. package/docs/testing.md +15 -0
  21. package/package.json +3 -2
  22. package/skills/ci-wait/scripts/wait-ci.sh +14 -6
  23. package/skills/dashboard/SKILL.md +3 -1
  24. package/skills/dashboard/scripts/dashboard.ts +2 -2
  25. package/skills/dashboard/scripts/sessions.ts +1 -1
  26. package/skills/est/SKILL.md +6 -3
  27. package/skills/est/reference.md +68 -11
  28. package/skills/est/scripts/est.ts +240 -19
  29. package/skills/github/SKILL.md +38 -40
  30. package/skills/github/locales/en.json +17 -11
  31. package/skills/github/locales/ru.json +17 -11
  32. package/skills/github/reference.md +233 -38
  33. package/skills/github/scripts/github.ts +1320 -181
  34. package/skills/github/scripts/locale.ts +36 -5
  35. package/skills/slot/SKILL.md +4 -3
  36. package/skills/slot/scripts/slot.ts +3 -3
  37. package/skills/spec/SKILL.md +16 -12
  38. package/skills/spec/canon.md +84 -39
  39. package/skills/spec/checks.md +7 -4
  40. package/skills/spec/ci.md +19 -1
  41. package/skills/spec/migration.md +41 -5
  42. package/skills/spec/reference.md +43 -17
  43. package/skills/spec/scripts/architecture.ts +1 -1
  44. package/skills/spec/scripts/eslint-config.ts +9 -7
  45. package/skills/spec/scripts/harness.ts +25 -12
  46. package/skills/spec/scripts/knip-hints.cjs +1 -1
  47. package/skills/spec/scripts/package-run.ts +1 -1
  48. package/skills/spec/scripts/precommit.ts +202 -0
  49. package/skills/spec/scripts/spec-break.ts +1 -1
  50. package/skills/spec/scripts/spec-claims.ts +12 -9
  51. package/skills/spec/scripts/spec-diff.ts +206 -35
  52. package/skills/spec/scripts/spec-doc.ts +176 -125
  53. package/skills/spec/scripts/spec-exceptions.ts +4 -4
  54. package/skills/spec/scripts/spec-publish.ts +1 -1
  55. package/skills/spec/scripts/spec-run.ts +1 -1
  56. package/skills/spec/scripts/spec-shard.ts +1 -1
  57. package/skills/spec/scripts/speclib.ts +44 -9
  58. package/skills/task-env/SKILL.md +8 -4
  59. package/skills/task-env/reference.md +21 -17
  60. 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 (it lists the skipped releases) and re-read what was
32
- updated: it applies, not what loaded at the start.
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`): talking with the
45
- user, issues, prompts to subagents, documents, commit and PR
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, rotation reminded.
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`; how tasks, the project and
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 decision's name, kind by color: a task
95
- carries those of the decisions it introduces or changes, an epic — their
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). `Priority` (`Urgent` → `Low`, `Medium` by default) is
110
- required on an open task. A milestone (an epic stage, a release) I close
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 status` won't take a second one. Another 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 status <N> "In progress" --branch <type>/<N>-<slug>`.
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 show
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 actualize <N>`
172
- does the mechanics and prints its prompt,
173
- in the same turn as `ci-wait merged <PR>` in the background; after both —
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>-*` after `git fetch --prune`): continue that work or wait
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`, `test:spec`; `test` — what the
193
- change affects** (`--changed=origin/main`; the full run — CI,
194
- `docs/testing.md`); before merging — a full build; `set -o pipefail` in
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
- - A merge that deploys — I wait for the deploy on the merge SHA (`ci-wait`)
204
- **before `github task close`**; a batch's — its release's CI, deploy,
205
- refresh.
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 and is red first (acceptance), then holds the
217
- decision (regression); a decision changes through its check — first the
218
- check, red, then the code. Three roots of `tests/` by the reader's question:
219
- `capabilities/<name>` (what the system does), `architecture` (what it's made
220
- of — a model with checks), `standards/<name>` (code rules, cross-cutting
221
- qualities); a decision is a folder (`<folder>.md` — why, `<folder>.test.ts` —
222
- the statements). Exceptions — a mark in the element's file or a file in the
223
- folder's `exceptions/`, with an `#issue`, a reason and a ratchet. Test names
224
- are statements in the project language; a code task gets the scenarios
225
- section (future `it` names) before code. The PR body is `spec-diff` output;
226
- docs are generated (`spec-doc`) and published by CI to the `spec` branch. A
227
- recurring decision — a "Standard · …" issue and a rule in `tests/standards/`;
228
- repeats in a PR's files — fixed in it. The full canon and wiring — the `spec`
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 (migrations, columns, orders of
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 — non-overlapping files, no git, a shared file has one owner. After
277
- a fan-out — `git status` and `git diff --stat`. Temp files shared with
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) at `<service>.<N>.<project>.localhost`, not on a host port; others'
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 decision's `<folder>.md`;
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 run in parallel, only my own stale run of a branch is
311
- cancelled; `main`, tags and manual runs never (`queue: max`); deploy — a
312
- queue per environment, apart from CI; others' runs I don't cancel or re-run.
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/form-reset` | 🟦 |
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
- **Decision labels** on GitHub tasks name the decision 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.
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 `<decision>.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)
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 `<decision>.md` next to the check |
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 and puts an answer at a question, 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.
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/parallel-checkouts.md`](docs/parallel-checkouts.md) — parallel worktrees: the full rule, 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.
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/readme/readme.md)).
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)).