aval-adr 1.5.0 → 1.7.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/README.md +115 -3
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -59,6 +59,77 @@ that is where an agent left to its own judgment does damage.
|
|
|
59
59
|
| 6 | `retired` | an ADR deliberately retired this key |
|
|
60
60
|
| 7 | `unknown` | no such key or scope, or a key not decided on that axis |
|
|
61
61
|
|
|
62
|
+
## Which decisions bear on this change
|
|
63
|
+
|
|
64
|
+
`resolve` needs a key, which is a chicken-and-egg problem at the start of a
|
|
65
|
+
task: the way to learn that a decision governs the file you are about to edit
|
|
66
|
+
is to already know its name. The alternative is `HEADS.md`, which is every
|
|
67
|
+
decision the repository has ever made.
|
|
68
|
+
|
|
69
|
+
`aval relevant` ranks the vocabulary against what you are about to touch:
|
|
70
|
+
|
|
71
|
+
```console
|
|
72
|
+
$ aval relevant --path crates/aval/src/mcp.rs --text "release a new version by pushing a tag"
|
|
73
|
+
advisory A ranking is a suggestion: it resolves nothing. Only `aval resolve <key>` answers.
|
|
74
|
+
|
|
75
|
+
14.2104 release.trigger active ADR-0008 A pushed version tag, never a merge
|
|
76
|
+
11.5524 release.version-scheme active ADR-0012 Semantic Versioning 2.0.0
|
|
77
|
+
4.9182 ci.test-gate-stage active ADR-0009 pre-commit
|
|
78
|
+
4.7105 forge.primary undecided no accepted decision for forge.primary
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**This is retrieval, not resolution**, and the output says so on every run. The
|
|
82
|
+
order is a guess about attention. What is not a guess is the verdict on each
|
|
83
|
+
row: it comes from the same `resolve` the rest of the tool runs, which is what
|
|
84
|
+
makes the `undecided` row the interesting one — nobody decided it, and an agent
|
|
85
|
+
that quietly fills the gap is doing the thing the corpus exists to prevent.
|
|
86
|
+
|
|
87
|
+
### The signals
|
|
88
|
+
|
|
89
|
+
| Signal | Weight | What it reads |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| text | 1.0 | BM25 over `--text`, against one document per key: the key's name and description, then the title, choice, reason and body of whatever decides it, and superseded titles at a third of the weight |
|
|
92
|
+
| path | 0.6 | the same BM25 over the words in each `--path` — directories and file stems, extensions dropped |
|
|
93
|
+
| mention | 2.0 per path, three at most | a record's body names that path itself: backticked, as a markdown link, or as a glob matched by its literal directory |
|
|
94
|
+
| co-change | 0.5 per path, three at most | `git log` says the commits that wrote the record also touched that path |
|
|
95
|
+
|
|
96
|
+
A mention outranks any amount of word overlap, because it is the one signal an
|
|
97
|
+
author put there on purpose. Co-change is weakest and capped hardest: a record
|
|
98
|
+
and a config file in one commit may share nothing but a Tuesday. The tokenizer
|
|
99
|
+
is frozen — lowercase, ASCII-fold, split on every non-alphanumeric, drop stop
|
|
100
|
+
words and one-character tokens, light suffix stemming — and the conformance
|
|
101
|
+
battery compares the resulting order byte for byte, so changing it is a change
|
|
102
|
+
somebody reviews rather than a ranking that quietly moved.
|
|
103
|
+
|
|
104
|
+
`--changed` adds what git reports modified, staged and untracked, which is the
|
|
105
|
+
whole query for "what does this branch touch". `--scope S` ranks only the keys
|
|
106
|
+
answerable at S and resolves each one there. `--top N` defaults to 5, and a row
|
|
107
|
+
must also score a fifth of the top row to be printed, so a query with one good
|
|
108
|
+
answer reports one. Rules (below) that match the same words are listed after
|
|
109
|
+
the keys, clearly apart.
|
|
110
|
+
|
|
111
|
+
Exit is **always 0**, usage errors aside. A ranking has no verdict to report,
|
|
112
|
+
and "I ranked and found little" must not share a code with "I could not look".
|
|
113
|
+
|
|
114
|
+
### For a router
|
|
115
|
+
|
|
116
|
+
`--json` carries a `dependencies` array — the same keys, compacted:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{"dependencies":[
|
|
120
|
+
{"key":"release.trigger","state":"active","exit":0,"adr":"ADR-0008","unresolved":false},
|
|
121
|
+
{"key":"forge.primary","state":"undecided","exit":4,"unresolved":true}]}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
One row per ranked key, in ranked order, and `unresolved` is true for exactly
|
|
125
|
+
`undecided` and `contradiction`. That is what a dispatcher reads —
|
|
126
|
+
[relais](https://github.com/fredericrous/relais) routes on unresolved decision
|
|
127
|
+
dependencies — so a change whose decisions are not settled goes to a person
|
|
128
|
+
instead of to a worker. The full verdict, with the choice and the reason, is in
|
|
129
|
+
`keys`; `why` on each row says which signal earned it its place, and
|
|
130
|
+
`decided_elsewhere` names the scopes that do decide a key the asked scope does
|
|
131
|
+
not.
|
|
132
|
+
|
|
62
133
|
## In front of an agent
|
|
63
134
|
|
|
64
135
|
A corpus that resolves is half the point. The other half is that whatever is
|
|
@@ -120,10 +191,16 @@ a session:
|
|
|
120
191
|
$ claude mcp add aval -- aval mcp
|
|
121
192
|
```
|
|
122
193
|
|
|
123
|
-
|
|
124
|
-
`
|
|
194
|
+
Nine read-only tools: `aval_resolve`, `aval_relevant`, `aval_keys`,
|
|
195
|
+
`aval_heads`, `aval_show`, `aval_history`, `aval_rules`, `aval_rule` and
|
|
196
|
+
`aval_repos`. They resolve through the same graph the CLI does and return the
|
|
125
197
|
same bytes `--json` would, so the two surfaces cannot drift apart.
|
|
126
198
|
|
|
199
|
+
`aval_relevant` is the one whose result is **not** an answer, and its
|
|
200
|
+
description says so where a model will read it: the ranking is advisory, what
|
|
201
|
+
is authoritative is the verdict beside each key, and an `undecided` row is the
|
|
202
|
+
reason to have called it.
|
|
203
|
+
|
|
127
204
|
The distinction that matters: **a verdict is not an error**. `undecided`,
|
|
128
205
|
`retired`, `unknown` and `contradiction` all come back as ordinary results with
|
|
129
206
|
`isError: false`, because each is an answer. Reporting `contradiction` as a tool
|
|
@@ -179,11 +256,13 @@ different edges:
|
|
|
179
256
|
| | |
|
|
180
257
|
|---|---|
|
|
181
258
|
| `aval resolve <key> [--scope S]` | the authoritative lookup |
|
|
259
|
+
| `aval relevant [--path P]… [--text W] [--changed] [--top N]` | which keys bear on what you are about to touch, ranked, each with its verdict. Advisory: it resolves nothing |
|
|
182
260
|
| `aval check` | every invariant; what the git hook and CI run |
|
|
183
261
|
| `aval heads [--write\|--check]` | the projection |
|
|
184
262
|
| `aval show ADR-0015` | derived status, including partial supersession |
|
|
185
263
|
| `aval history <key>` | the chain, labelled as history |
|
|
186
|
-
| `aval rules [--level L] [--all]` | the rules the decisions have adopted, one line each |
|
|
264
|
+
| `aval rules [--level L] [--all] [--all-traits]` | the rules the decisions have adopted, one line each |
|
|
265
|
+
| `aval traits [--summary\|--detect\|--check]` | what this repository says it is, and what its tracked files suggest |
|
|
187
266
|
| `aval rule <id>` | one rule, with its translation and why |
|
|
188
267
|
| `aval hook install [--check]` | put the heads in front of an agent at session start |
|
|
189
268
|
| `aval pack [--write\|--check]` | publish this corpus's declarations for others to read |
|
|
@@ -230,6 +309,39 @@ prints: the decision at the scope asked, then the default-scope decision, then
|
|
|
230
309
|
these rules, then the book a rule cites — as explanation only. Remembered
|
|
231
310
|
advice from that book does not outrank a rule here.
|
|
232
311
|
|
|
312
|
+
### Traits: rules that only apply where the thing exists
|
|
313
|
+
|
|
314
|
+
Rules about command-line programs mean nothing in a repository that ships none.
|
|
315
|
+
A rule file says what it is about, the fleet corpus declares the vocabulary,
|
|
316
|
+
and each repository declares which of its parts have which traits:
|
|
317
|
+
|
|
318
|
+
```yaml
|
|
319
|
+
# a rule file's frontmatter
|
|
320
|
+
applies: [cli]
|
|
321
|
+
|
|
322
|
+
# the producer's .adr.yaml — travels in its pack
|
|
323
|
+
traits: [cli, ui]
|
|
324
|
+
|
|
325
|
+
# a consumer's .adr.yaml — its own, never in a pack
|
|
326
|
+
areas:
|
|
327
|
+
"web/**": [ui]
|
|
328
|
+
"cmd/**": [cli]
|
|
329
|
+
disclaims:
|
|
330
|
+
"tools/**": [cli]
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
With `areas` declared, `aval rules` and the hook list only rules that apply to
|
|
334
|
+
the traits declared, and **say what they hid** — on stderr, in `omitted` on
|
|
335
|
+
every `--json` and tool payload, and in one hook line printed even when nothing
|
|
336
|
+
is hidden. A hidden rule is still active: `aval rule <id>` explains it and
|
|
337
|
+
`--all-traits` lists it. Without `areas`, nothing changes.
|
|
338
|
+
|
|
339
|
+
`aval traits --detect` proposes areas from the files git tracks, and `aval
|
|
340
|
+
traits --check` reports what the declaration misses, locally per package, plus
|
|
341
|
+
any glob that matches nothing. Detection is advisory and leans toward
|
|
342
|
+
reporting: a false positive costs one `disclaims` line, while a miss would hide
|
|
343
|
+
a constraint. It never filters anything on its own.
|
|
344
|
+
|
|
233
345
|
## Sharing one decision across repositories
|
|
234
346
|
|
|
235
347
|
A decision made once should be readable everywhere it applies. `aval pack`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aval-adr",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Ask what the current architecture decision is, and get a typed answer",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adr",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"node": ">=18"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
|
-
"@aval-adr/darwin-arm64": "1.
|
|
36
|
-
"@aval-adr/darwin-x64": "1.
|
|
37
|
-
"@aval-adr/linux-arm64-gnu": "1.
|
|
38
|
-
"@aval-adr/linux-x64-gnu": "1.
|
|
39
|
-
"@aval-adr/linux-x64-musl": "1.
|
|
40
|
-
"@aval-adr/win32-x64": "1.
|
|
35
|
+
"@aval-adr/darwin-arm64": "1.7.0",
|
|
36
|
+
"@aval-adr/darwin-x64": "1.7.0",
|
|
37
|
+
"@aval-adr/linux-arm64-gnu": "1.7.0",
|
|
38
|
+
"@aval-adr/linux-x64-gnu": "1.7.0",
|
|
39
|
+
"@aval-adr/linux-x64-musl": "1.7.0",
|
|
40
|
+
"@aval-adr/win32-x64": "1.7.0"
|
|
41
41
|
}
|
|
42
42
|
}
|