@miroshnik/elves 0.0.0 → 2026.1018.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +310 -0
- package/README.md +344 -0
- package/bin/ai-dev.ts +7 -0
- package/bin/elves.ts +2847 -0
- package/bin/shims/timeout +113 -0
- package/claude/CLAUDE.md +71 -0
- package/dist/github/scripts/github.js +3428 -0
- package/dist/spec/scripts/architecture.js +628 -0
- package/dist/spec/scripts/eslint-config.js +54 -0
- package/dist/spec/scripts/harness-qzb932c8.js +959 -0
- package/dist/spec/scripts/harness-v9s1v9w3.js +16045 -0
- package/dist/spec/scripts/harness.js +53 -0
- package/dist/spec/scripts/knip-hints.cjs +34 -0
- package/docs/ci-concurrency.md +82 -0
- package/docs/cloud-sessions.md +203 -0
- package/docs/judgment.md +81 -0
- package/docs/parallel-checkouts.md +220 -0
- package/docs/pr-checks.md +117 -0
- package/docs/testing.md +304 -0
- package/package.json +70 -1
- package/skills/ci-wait/SKILL.md +180 -0
- package/skills/ci-wait/scripts/wait-ci.sh +377 -0
- package/skills/dashboard/SKILL.md +212 -0
- package/skills/dashboard/scripts/dashboard.ts +912 -0
- package/skills/dashboard/scripts/sessions.ts +772 -0
- package/skills/est/SKILL.md +221 -0
- package/skills/est/reference.md +381 -0
- package/skills/est/scripts/est.ts +4854 -0
- package/skills/github/SKILL.md +206 -0
- package/skills/github/locales/en.json +449 -0
- package/skills/github/locales/ru.json +449 -0
- package/skills/github/reference.md +1073 -0
- package/skills/github/scripts/github.ts +4390 -0
- package/skills/github/scripts/locale.ts +495 -0
- package/skills/github/scripts/package.json +3 -0
- package/skills/slot/SKILL.md +145 -0
- package/skills/slot/scripts/package.json +3 -0
- package/skills/slot/scripts/slot.ts +279 -0
- package/skills/spec/SKILL.md +247 -0
- package/skills/spec/canon.md +219 -0
- package/skills/spec/checks.md +709 -0
- package/skills/spec/ci.md +378 -0
- package/skills/spec/migration.md +125 -0
- package/skills/spec/reference.md +889 -0
- package/skills/spec/scripts/architecture.ts +839 -0
- package/skills/spec/scripts/c4.ts +184 -0
- package/skills/spec/scripts/eslint-config.ts +82 -0
- package/skills/spec/scripts/harness.ts +1483 -0
- package/skills/spec/scripts/knip-hints.cjs +34 -0
- package/skills/spec/scripts/knip-hints.d.cts +17 -0
- package/skills/spec/scripts/package-run.ts +22 -0
- package/skills/spec/scripts/package.json +3 -0
- package/skills/spec/scripts/spec-break.ts +232 -0
- package/skills/spec/scripts/spec-claims.ts +198 -0
- package/skills/spec/scripts/spec-diff.ts +1111 -0
- package/skills/spec/scripts/spec-doc.ts +744 -0
- package/skills/spec/scripts/spec-exceptions.ts +233 -0
- package/skills/spec/scripts/spec-publish.ts +214 -0
- package/skills/spec/scripts/spec-run.ts +235 -0
- package/skills/spec/scripts/spec-shard.ts +267 -0
- package/skills/spec/scripts/speclib.ts +826 -0
- package/skills/spec/scripts/vendor/babel-parser.LICENSE +19 -0
- package/skills/spec/scripts/vendor/babel-parser.cjs +14615 -0
- package/skills/spec/scripts/vendor/babel-parser.d.cts +3 -0
- package/skills/task-env/SKILL.md +231 -0
- package/skills/task-env/proxy/compose.yaml +51 -0
- package/skills/task-env/reference.md +254 -0
- package/skills/task-env/scripts/task-env.ts +1017 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Specification — decisions
|
|
2
|
+
|
|
3
|
+
The spec canon, moved out of the `AGENTS.md` core: it applies as a rule to any
|
|
4
|
+
project; the core keeps a paragraph of it and a link here. Read it when you
|
|
5
|
+
write or migrate a test, add a check, an exception or a standard; how to wire
|
|
6
|
+
each mechanism — `checks.md` next to this file, the tools — `SKILL.md`.
|
|
7
|
+
|
|
8
|
+
A project's spec is **decisions with a mechanical check**, functional and
|
|
9
|
+
non-functional: what the system does, what it's made of, which rules the code
|
|
10
|
+
obeys and which qualities it holds. A check is how the system should work,
|
|
11
|
+
code is how it does work: whatever the code is, that's how the system works,
|
|
12
|
+
so code can't be the source of what should be. The source of what should be is
|
|
13
|
+
a decision and its trace (an issue, a commit with a reason); code only shows
|
|
14
|
+
which decision was made, and a test "from code" is written only when the trace
|
|
15
|
+
exists. There are no separate specifications: a decision exists only while a
|
|
16
|
+
mechanism checks it — a test, a lint rule, a check over the architecture
|
|
17
|
+
model. **No check — no decision; no decision — no code:** behavior without a
|
|
18
|
+
check may be changed without agreement, but removing what people use — only
|
|
19
|
+
by a decision in an issue; a standard without a check doesn't apply; code that
|
|
20
|
+
no decision claims is dead and is deleted.
|
|
21
|
+
|
|
22
|
+
- **A decision is the unit of the spec, a folder is its home.** One anatomy
|
|
23
|
+
for all kinds:
|
|
24
|
+
- `<folder>.md` — why and for whom, the reason and rejected alternatives;
|
|
25
|
+
the first paragraph is the description in the table of contents; a
|
|
26
|
+
`<!-- spec: … -->` marker puts a generated diagram (C4, sequence) into the
|
|
27
|
+
story. Test files have no JSDoc headers;
|
|
28
|
+
- the main file `<folder>.test.ts` — the statements: the page's sections
|
|
29
|
+
follow the order of its `describe`s, the main one first; `<folder>.e2e.ts`
|
|
30
|
+
next to it comes right after it (the units open the page), then the other
|
|
31
|
+
`*.test.ts`, `*.e2e.ts`;
|
|
32
|
+
- the mechanism — in the same folder: an `eslint.ts` fragment, an ast-grep
|
|
33
|
+
`rule.yml`. The project's tool configs (`eslint.config.*`, `sgconfig.yml`)
|
|
34
|
+
only collect them from the tree (the harness's `collectEslint`) — a rule
|
|
35
|
+
has one home;
|
|
36
|
+
- `exceptions/` — exceptions for elements without a file of their own, one
|
|
37
|
+
file per element (below).
|
|
38
|
+
- **Monorepo — a package subfolder inside the decision folder:**
|
|
39
|
+
`tests/<kind>/<name>/<package>/`. A package has its own runner (aliases,
|
|
40
|
+
environment, plugins) — it takes `tests/*/*/<package>/`; a test right in the
|
|
41
|
+
decision folder, without a subfolder, is repository-wide (the root runner).
|
|
42
|
+
One decision — one page: the main file is `<folder>.test.ts` at the folder
|
|
43
|
+
root or in the package subfolder (`<package>/<folder>.test.ts`); the story
|
|
44
|
+
starts with the root's main file, then the packages' main files by package
|
|
45
|
+
name, then the other files. `<folder>.md` and `exceptions/` — one per
|
|
46
|
+
folder, at its root. Runners are launched from the repository root (Vitest
|
|
47
|
+
or Jest `projects`, `-c` with the package's config): the harness counts
|
|
48
|
+
element paths from cwd; it reads `exceptions/` relative to the test file
|
|
49
|
+
and `fileOf` from the project root, and the exception path in the hint is
|
|
50
|
+
from the repository root, even with the runner in the package directory.
|
|
51
|
+
- **Three roots of `tests/` — by the reader's question;** the tool kind isn't
|
|
52
|
+
encoded in the structure:
|
|
53
|
+
```
|
|
54
|
+
tests/
|
|
55
|
+
capabilities/<name>/ what the system does: behavior — unit, integration, e2e
|
|
56
|
+
architecture/ what it's made of: model.ts — C1 external systems (adapters,
|
|
57
|
+
hosts, environment keys, headers), C2 containers (links,
|
|
58
|
+
deploy configs), C3 modules and libraries (directory,
|
|
59
|
+
purpose, API, what it depends on, packages); <name>/ —
|
|
60
|
+
checks over the model
|
|
61
|
+
standards/<name>/ which rules all code obeys: form conventions,
|
|
62
|
+
cross-cutting invariants and qualities
|
|
63
|
+
lib/ factories, database, helpers — not the spec, not in the docs
|
|
64
|
+
```
|
|
65
|
+
Flat, no subsystems; the folder name is the decision's identifier in
|
|
66
|
+
English. **One fact — one home:** a general rule ("every mutation writes an
|
|
67
|
+
audit record") goes in `standards/`, the behavior of the audit log itself —
|
|
68
|
+
in its own capability; the other capabilities say nothing about it.
|
|
69
|
+
- **Decisions are functional and non-functional — both have a check.** A
|
|
70
|
+
functional one (what the system does) is a capability. A non-functional one
|
|
71
|
+
is architectural (the `architecture` model: what the system is made of) or a
|
|
72
|
+
standard (`standards/`: which rules the code obeys). A quality —
|
|
73
|
+
performance, security, accessibility, reliability — is the same kind of
|
|
74
|
+
decision with a check, not a wish in prose: a cross-cutting one ("every
|
|
75
|
+
mutation writes an audit record", "every route checks permissions", "every
|
|
76
|
+
page passes axe") is a standard, "registry + invariant"; the budget of a
|
|
77
|
+
single capability ("search responds in under 300 ms") — in its capability,
|
|
78
|
+
next to the behavior, checked by a load test that asserts the budget. A
|
|
79
|
+
quality without a check isn't a decision.
|
|
80
|
+
- **A check is the decision's acceptance test and its regression test — that
|
|
81
|
+
is TDD, for every decision.** Written before the code, it is red: the
|
|
82
|
+
decision isn't implemented yet; the code turns it green; from then on the
|
|
83
|
+
same check holds the decision against every later change. Not only
|
|
84
|
+
behavior: "fast" is held by a load test, "data in Postgres" — by the model,
|
|
85
|
+
"every form has a reset button" — by an invariant over the forms. **A
|
|
86
|
+
decision changes through its check:** the check changes first and goes red
|
|
87
|
+
on the current code, then the code follows it to green. Code changed the
|
|
88
|
+
other way round, against a check, turns it red — that's the check doing its
|
|
89
|
+
job, not an obstacle to remove.
|
|
90
|
+
- **The mechanism — by what we pin down** (how to wire it — `checks.md` next
|
|
91
|
+
to this file); each is wrapped in a runner test, so `spec-doc` and
|
|
92
|
+
`spec-diff` see everything the same way:
|
|
93
|
+
- behavior — a test: an algorithm — a unit test, a subsystem — an
|
|
94
|
+
integration test, interaction of subsystems — e2e; the level isn't encoded
|
|
95
|
+
in the name, the `.e2e.ts` suffix is only for the runner;
|
|
96
|
+
- a budget (latency, throughput, size) — a test that measures and asserts
|
|
97
|
+
it: "fast" is a load test against the budget, in the capability's folder;
|
|
98
|
+
- a form convention ("no `console` in the domain") — a lint rule with
|
|
99
|
+
examples (the harness's `examples`): "not allowed" gives a rule error,
|
|
100
|
+
"allowed" and a file outside scope — don't;
|
|
101
|
+
- a behavioral convention — "registry + invariant" (`invariant`): a registry
|
|
102
|
+
from the code (router, schema, files), a test per element, a "registry
|
|
103
|
+
isn't empty" test and a violator on which the check fails;
|
|
104
|
+
- **a registry that parallel PRs edit element by element — one file per
|
|
105
|
+
element** (exceptions — `exceptions/`): deleting and adding your own file
|
|
106
|
+
overlaps with nothing, while in a shared array neighboring lines conflict,
|
|
107
|
+
and every next PR goes for a CI rerun; sorting doesn't save it, and
|
|
108
|
+
`merge=union` brings both back when neighboring lines are deleted. **A
|
|
109
|
+
record about an element with its own file — a mark in that file** (outside
|
|
110
|
+
scope and exception): the element is deleted — the record goes too,
|
|
111
|
+
whereas a record in a test or in `exceptions/` outlived the element, and a
|
|
112
|
+
parallel PR added a record about what no longer existed;
|
|
113
|
+
- boundaries, dependencies and the stack (a database, a framework) — the
|
|
114
|
+
architecture model: "data in Postgres" is a storage container with its
|
|
115
|
+
`pg` client, a framework is a package allowed for its modules;
|
|
116
|
+
`boundariesConfig` (ESLint, the error shows in the editor) and
|
|
117
|
+
`architecture` (a directory is
|
|
118
|
+
in a module, a package is allowed for a module and used, an external
|
|
119
|
+
system only through an adapter, a module in one container, a library in
|
|
120
|
+
the containers of the modules that depend on it); the whole graph (cycles,
|
|
121
|
+
orphans) — dependency-cruiser, only when it's needed;
|
|
122
|
+
- the order of interaction, when it is itself a decision ("reserve first,
|
|
123
|
+
then charge"), — `sequence` with `order` over a `trace` at the model's
|
|
124
|
+
boundaries.
|
|
125
|
+
- **A check can fail and doesn't flake** (the "Tests and code" section in
|
|
126
|
+
`AGENTS.md`): a test is written before the code and is red first, a lint
|
|
127
|
+
rule proves "not allowed" with an example, an invariant — with a violator,
|
|
128
|
+
an exception — with a ratchet. `.only`, `skip` without a reason and an issue
|
|
129
|
+
number, retries, mocks of your own modules (only external edges may be
|
|
130
|
+
substituted) — a lint error.
|
|
131
|
+
- **Claimedness — code holds on to decisions at attachment points,** not line
|
|
132
|
+
by line:
|
|
133
|
+
- every entry point (route, page, job, command) is called by at least one
|
|
134
|
+
capability test — by the call log (`journal` and `spec-claims`), not by
|
|
135
|
+
coverage: coverage says "executed", not "claimed";
|
|
136
|
+
- every code directory is in a model module, every package is allowed for a
|
|
137
|
+
module and imported;
|
|
138
|
+
- every file and export is reachable from an entry point (`deadCode`, knip);
|
|
139
|
+
- every environment variable is declared and read (`envVars`).
|
|
140
|
+
|
|
141
|
+
Existing unclaimed code — exceptions with an issue, not deletion.
|
|
142
|
+
- **A test is for a decision, not for every line,** inside an entry point too.
|
|
143
|
+
A test is needed when all three hold: it's a decision (it could have been
|
|
144
|
+
otherwise), a breakage would go unnoticed (types don't catch it, eyes don't
|
|
145
|
+
see it), a mistake is costly (shared code, data, money, external systems). I
|
|
146
|
+
don't test compiler and library guarantees, styles and layout, label texts —
|
|
147
|
+
except a regression after a real bug. A thin spec page is no reason to test
|
|
148
|
+
trifles: a test of a trifle fails on any edit, it gets fixed mechanically,
|
|
149
|
+
and it stops being spec.
|
|
150
|
+
- **Exceptions — with a ratchet:** for an element with its own file — a mark
|
|
151
|
+
in it (`// spec-exception(<decision>) #N: reason`, outside scope —
|
|
152
|
+
`spec-outside`), otherwise in the decision folder's `exceptions/` directory,
|
|
153
|
+
one file per exception (`<element>.json`: the element, `#issue` and the
|
|
154
|
+
reason); an exception that no longer violates fails the check (`remove the
|
|
155
|
+
mark in <file>`, `remove the exception: delete <file>`). The old `exceptions.ts` is migrated
|
|
156
|
+
by `spec-exceptions`; the harness fails on it with a hint. Disabling lint in
|
|
157
|
+
code — only with a rule and `-- #N reason` (`lintExceptions`); a blanket
|
|
158
|
+
`eslint-disable` is forbidden, an unnecessary disable is a `lint` error.
|
|
159
|
+
Exceptions are visible on the decision's page — it's debt, not a secret. A
|
|
160
|
+
task to sort out a violator is a Task, not a Bug: a bug is wrong behavior
|
|
161
|
+
for the user, while a violation of a standard in force is debt. A new check
|
|
162
|
+
on existing code enters green right away: current violations — as
|
|
163
|
+
exceptions with a task to sort them out, not a disabled check or a postponed
|
|
164
|
+
rollout.
|
|
165
|
+
- **What can't be checked isn't called spec.** Where the look or UX is a
|
|
166
|
+
decision, the check is targeted: a screenshot test, a job test;
|
|
167
|
+
accessibility and budgets — like any quality (above); the rest (look, texts,
|
|
168
|
+
impression) — a product document or acceptance in the issue.
|
|
169
|
+
- **Names are statements in the project language** (`language` in
|
|
170
|
+
`.agents/elves.json`). `describe` — a capability in the user's terms ("only
|
|
171
|
+
someone with access to the entity itself sees its history"), `it` — a
|
|
172
|
+
concrete example; not a function, component or file name (`spec-doc` warns
|
|
173
|
+
about such ones and `spec-diff` counts the PR's new ones in its body: in
|
|
174
|
+
`ru` a statement has Russian words, in another language it is a phrase of
|
|
175
|
+
words, not an identifier). A rule without an exit code and without
|
|
176
|
+
exceptions: failing `--strict` on names was paperwork, not findings (#447).
|
|
177
|
+
Tests of one
|
|
178
|
+
capability from units and e2e —
|
|
179
|
+
under the same `describe`. JSDoc before a `describe` or `it` — what the name
|
|
180
|
+
doesn't show, one to three sentences for the spec reader; file names, issue
|
|
181
|
+
numbers, how the test is set up — in `//`, that's for the code reader.
|
|
182
|
+
Rewrite an unclear name rather than explain it.
|
|
183
|
+
- **Intent comes in through an issue:** we discuss the implementation and make
|
|
184
|
+
decisions in the task (the questions section, comments). A code task gets at
|
|
185
|
+
creation its decision labels (the "Task tracking" section in `AGENTS.md`)
|
|
186
|
+
and the scenarios section: future `it` names (or "describe › it") as a list:
|
|
187
|
+
from them the user sees the requirements before the code and can stop it.
|
|
188
|
+
For a "registry + invariant" standard the harness generates the tests per
|
|
189
|
+
element — the scenario is the name of the `describe` in which `invariant`
|
|
190
|
+
(or `examples`) is called. The agent first writes the checks and runs them
|
|
191
|
+
locally — the new ones are red (the check can fail, the behavior doesn't
|
|
192
|
+
exist yet); then the code — green. Push and PR — only when green: a red CI
|
|
193
|
+
run proves nothing, it's noise. The PR's spec section — `spec-diff
|
|
194
|
+
--scenarios`, matching scenarios against tests.
|
|
195
|
+
- **The diff of checks in a PR is the diff of the spec,** the diff of code is
|
|
196
|
+
the implementation. The PR body is `spec-diff` output: added, changed and
|
|
197
|
+
removed test names, changes to the architecture model, exceptions and
|
|
198
|
+
registries. A removed test is a withdrawn requirement; that must be visible.
|
|
199
|
+
- **Documentation is generated,** not written by hand: `spec-doc` builds a
|
|
200
|
+
page per decision (why — `<folder>.md`, what holds — `describe`, what checks
|
|
201
|
+
it — the tests, collapsed under the section; for a standard — the examples'
|
|
202
|
+
code and the exceptions), the architecture page from the model (C4 diagrams
|
|
203
|
+
and the module table) and sequence diagrams from scenario traces.
|
|
204
|
+
`docs/spec` isn't in `main`: after a merge CI publishes it to the `spec`
|
|
205
|
+
branch (`spec-publish`); review goes by `spec-diff`. Product documents
|
|
206
|
+
(vision, business decisions, research) aren't spec and live separately.
|
|
207
|
+
- **A recurring decision is a standard:** on task close (check 5 of the "Epic
|
|
208
|
+
refresh on task close" section in `AGENTS.md`) a repetition found becomes a
|
|
209
|
+
"Standard · …" issue; the result is a rule in `tests/standards/` and a
|
|
210
|
+
refactoring.
|
|
211
|
+
- **Migrating an old spec** (OpenSpec, a technical specification) into
|
|
212
|
+
decisions — the `migration.md` reference next to this file: an outcome for
|
|
213
|
+
each requirement, check by breaking, removal categories, "the spec fell
|
|
214
|
+
behind the code".
|
|
215
|
+
- **What's not here:** state markers and todos as a plan (the plan is in the
|
|
216
|
+
issue), ADRs (the reason is in the decision's `<folder>.md`), capability
|
|
217
|
+
registries, handwritten documentation of behavior and hand-drawn diagrams,
|
|
218
|
+
Gherkin, mutation testing (noise from equivalent mutants, pressure towards
|
|
219
|
+
tests of trifles), approval points other than the issue.
|