aval-adr 1.5.0 → 1.6.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.
Files changed (2) hide show
  1. package/README.md +80 -2
  2. 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
- Five read-only tools: `aval_resolve`, `aval_keys`, `aval_heads`, `aval_show`,
124
- `aval_history`. They resolve through the same graph the CLI does and return the
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,6 +256,7 @@ 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 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aval-adr",
3
- "version": "1.5.0",
3
+ "version": "1.6.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.5.0",
36
- "@aval-adr/darwin-x64": "1.5.0",
37
- "@aval-adr/linux-arm64-gnu": "1.5.0",
38
- "@aval-adr/linux-x64-gnu": "1.5.0",
39
- "@aval-adr/linux-x64-musl": "1.5.0",
40
- "@aval-adr/win32-x64": "1.5.0"
35
+ "@aval-adr/darwin-arm64": "1.6.0",
36
+ "@aval-adr/darwin-x64": "1.6.0",
37
+ "@aval-adr/linux-arm64-gnu": "1.6.0",
38
+ "@aval-adr/linux-x64-gnu": "1.6.0",
39
+ "@aval-adr/linux-x64-musl": "1.6.0",
40
+ "@aval-adr/win32-x64": "1.6.0"
41
41
  }
42
42
  }