@assuredloop/cli 0.2.4 → 2.0.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 +274 -21
- package/bin/al.js +8 -2
- package/package.json +1 -1
- package/skills/assuredloop/SKILL.md +40 -17
- package/src/v4/blocks.js +333 -0
- package/src/v4/check.js +37 -16
- package/src/v4/checks.js +71 -20
- package/src/v4/commands.js +3 -1
- package/src/v4/conclude.js +8 -4
- package/src/v4/indexer.js +121 -15
- package/src/v4/issues.js +297 -0
- package/src/v4/markers.js +63 -0
- package/src/v4/phrases.js +78 -0
- package/src/v4/scope.js +13 -1
- package/src/v4/state.js +36 -9
package/README.md
CHANGED
|
@@ -10,8 +10,10 @@ AssuredLoop keeps an AI focused on one change. It holds together:
|
|
|
10
10
|
- the code, the tests and the documents that came with the change.
|
|
11
11
|
|
|
12
12
|
Its command, `al`, keeps these records, checks them, and says what to do
|
|
13
|
-
next. It refuses only in its own `al conclude`.
|
|
14
|
-
|
|
13
|
+
next. It refuses only in its own `al conclude`. `al check` gives the AI
|
|
14
|
+
review hints, and the review decides. Only a few exact rules are `not ok`
|
|
15
|
+
(see "Hints, and the rules that block"). A project that wants them to block
|
|
16
|
+
turns on `al check --strict` in its CI.
|
|
15
17
|
|
|
16
18
|
AssuredLoop is not a lifecycle tool. It has no phases, no roles, no task
|
|
17
19
|
states and no approvals beyond the owner's sign-off. How and when you design,
|
|
@@ -95,7 +97,15 @@ Search at level 2 needs the library `@huggingface/transformers` 4.3.1 (see
|
|
|
95
97
|
- **Bindings.** When a link is first indexed, `al index` records the hash of
|
|
96
98
|
both ends. Ordinary indexing never moves a binding. When the target
|
|
97
99
|
changes, the source gets a hint until someone checks it and runs
|
|
98
|
-
`al index --align <ID>`.
|
|
100
|
+
`al index --align <ID>`. When the source paragraph's own text changes,
|
|
101
|
+
it gets the hint `holder-changed` until it is aligned in the same way.
|
|
102
|
+
This hint covers the links that a paragraph of a spec doc, an ADR or an
|
|
103
|
+
open change spec still declares, to a target that still resolves, in the
|
|
104
|
+
records of the open requests. A requirement is out, because a changed
|
|
105
|
+
requirement needs a new sign-off. An output file is out. When no current
|
|
106
|
+
marker declares a link any more, `al index` removes its binding, in the
|
|
107
|
+
open and the archived records, and names it in its output. Git history
|
|
108
|
+
keeps it.
|
|
99
109
|
- **Consolidation is by hand.** A PR copies a change paragraph into `specs/`
|
|
100
110
|
word for word, and records its disposition: `incorporated`, `removed`,
|
|
101
111
|
`superseded` or `abandoned`. No command writes the spec.
|
|
@@ -109,16 +119,16 @@ The user picks the path. The skill has the steps of each path.
|
|
|
109
119
|
| 0. Fix | no promise changes: it restores what the spec says, or claims a typo in any paragraph | none | no | the PR merges |
|
|
110
120
|
| 1. Small promise amend | one or a few promise paragraphs change | a short request; the PR edits `specs/` directly | yes | the PR merges |
|
|
111
121
|
| 1d. Small design correction | names or wording of design paragraphs only | a short request with a one-line source note; the PR edits `specs/` directly | no | the PR merges |
|
|
112
|
-
| 2. Big change | a real design, one or a few PRs | a request and a change spec | if a promise kind changes | the close rule holds |
|
|
122
|
+
| 2. Big change | a real design, one or a few PRs | a request and a change spec | if a promise kind changes, or the request has an R<n> | the close rule holds |
|
|
113
123
|
| 3. Epic | big, in parts over time | as 2, with optional task references | as 2 | the close rule holds |
|
|
114
124
|
| S. Spike | not yet clear what to build | a request with an organized question; the findings open with the answer | yes, on the question | the answer names the question version |
|
|
115
125
|
|
|
116
126
|
Every PR states its path and its claim in a commit message, for example
|
|
117
127
|
`Tier: 0 — typo in INV-3` or `Tier: 2 — invoice-payments, part 1`. `al check`
|
|
118
128
|
reads the newest `Tier:` line in the branch's commits, and checks the claim
|
|
119
|
-
against the kinds and the changed paragraphs
|
|
120
|
-
|
|
121
|
-
meaning.
|
|
129
|
+
against the kinds and the changed paragraphs. Where they disagree, it gives
|
|
130
|
+
a hint, for example "path 0, but a rule paragraph is changed with no typo
|
|
131
|
+
claim". It checks the labels, not their meaning.
|
|
122
132
|
|
|
123
133
|
**The close rule.** `al conclude <name>` refuses while a promise change has
|
|
124
134
|
no sign-off, a spike's current question is not signed, or a change paragraph
|
|
@@ -137,10 +147,14 @@ with a baseline effect has no valid disposition for its current version.
|
|
|
137
147
|
- `al spec`: every doc's IDs, display numbers and kinds.
|
|
138
148
|
`al spec --add-ids <file> [--prefix <P>]`: give each unmarked paragraph the
|
|
139
149
|
next free ID.
|
|
140
|
-
- `al index`: write the derived facts and the new bindings,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
150
|
+
- `al index`: write the derived facts and the new bindings, remove the
|
|
151
|
+
bindings of links that no marker declares, and list the AI hints to
|
|
152
|
+
refresh. `al index --align <ID>` moves the bindings of one paragraph to its
|
|
153
|
+
targets' current text and to its own, on purpose.
|
|
154
|
+
- `al check [--strict] [--issues <folder>]`: the checks of this branch
|
|
155
|
+
against its base. With `--issues <folder>`, it also checks the work items
|
|
156
|
+
that the caller saved in the folder (see "Hints from the linked work
|
|
157
|
+
items").
|
|
144
158
|
- `al context [<name> | <ID>]`: where the project, a request or a paragraph
|
|
145
159
|
stands. `--diff <range> --for review` is the review view. `--audit` gives a
|
|
146
160
|
request's whole trace; `--at <commit>` any v4 state.
|
|
@@ -173,18 +187,208 @@ results: .assuredloop/results # where result files are
|
|
|
173
187
|
outputs: # in the central repo: its output repos
|
|
174
188
|
- {name: invoicer-web, path: ../invoicer-web, commit: <sha>}
|
|
175
189
|
central: {path: ../invoicer} # in an output repo: the central repo
|
|
190
|
+
phrases: # text for the review to look at; each match is a hint
|
|
191
|
+
- the requester # a phrase: ignoring case, across a line break too
|
|
192
|
+
- {pattern: "Avery (asked|wants)"} # a JavaScript regular expression
|
|
176
193
|
```
|
|
177
194
|
|
|
195
|
+
`al check` gives the hint `phrase` for each match of `phrases:` in the spec
|
|
196
|
+
docs, the ADRs and the Markdown files of the open requests, but their
|
|
197
|
+
`origin/` folders; with `--issues <folder>`, in the work items too. The list is empty by
|
|
198
|
+
default. A bad entry or an invalid pattern stops `al check` with exit 2.
|
|
199
|
+
|
|
178
200
|
`.assuredloop/schema.yaml` holds the kinds and the link words, with a version.
|
|
179
201
|
`al spec --add-ids` writes both files when they are absent.
|
|
180
202
|
|
|
181
203
|
ADRs are `specs/adr/NNNN-<slug>.md`, with `Status: proposed`, `accepted` or
|
|
182
|
-
`superseded
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
204
|
+
`superseded by ADR-<n>` on the first line. A status line may name several
|
|
205
|
+
ADRs that supersede it: `superseded by ADR-11, ADR-12` or
|
|
206
|
+
`ADR-11 and ADR-12`. The heading's marker holds `ADR-<n>` and its
|
|
207
|
+
`decides:`, `source:` and `supersedes:` links.
|
|
208
|
+
|
|
209
|
+
- Any text may be rewritten in other words when its meaning stays the same,
|
|
210
|
+
an accepted or superseded ADR's text too. A new ADR that supersedes it is
|
|
211
|
+
needed only when the decision changes, in whole or in part.
|
|
212
|
+
- The review judges whether a rewrite keeps the decision; `al` does not. So
|
|
213
|
+
a changed ADR text, a changed or removed link in its marker, or any other
|
|
214
|
+
change of its marker line, gives a hint: check that its decision is
|
|
215
|
+
unchanged. Links added to the marker of an accepted ADR, with nothing else
|
|
216
|
+
changed, give no finding.
|
|
217
|
+
- `al check` gives a hint for a marker line of an ADR that holds anything
|
|
218
|
+
other than its ID, its kind and links: an unknown token, a second `<!--`
|
|
219
|
+
or `-->`, or an empty item in a list of links. A line that starts like a
|
|
220
|
+
marker, but that `al` does not read as one, gets the same hint.
|
|
221
|
+
- `al check` gives a hint for an ADR whose first line is not
|
|
222
|
+
`Status: proposed`, `Status: accepted` or `Status: superseded by ADR-<n>`,
|
|
223
|
+
with one ADR or a list of them.
|
|
224
|
+
- After a rewrite, `al index --align <ID>` moves the bindings of paragraph
|
|
225
|
+
`<ID>` to the ADR's new text, for an accepted or a superseded ADR.
|
|
226
|
+
- `al check` gives a hint for a proposed or accepted ADR with no `source:`
|
|
227
|
+
or no `decides:`.
|
|
228
|
+
- A current place that names a superseded ADR also names the ADR that
|
|
229
|
+
supersedes it, in the same marker or the same paragraph. Current places
|
|
230
|
+
are the spec docs, the open requests and the proposed and accepted ADRs.
|
|
231
|
+
The chain itself and dated records are exempt: an ADR that supersedes it,
|
|
232
|
+
a superseded ADR, an ADR's `## History`, a request's `## Decisions`, the
|
|
233
|
+
`origin/` files and the archived requests. Else `al check` gives
|
|
234
|
+
`not ok superseded-cited`.
|
|
235
|
+
- A superseded ADR's status line names the ADR that supersedes it, and that
|
|
236
|
+
ADR's marker has `supersedes:` for it. Else `al check` gives
|
|
237
|
+
`not ok superseded-status`.
|
|
238
|
+
|
|
239
|
+
## Hints from the linked work items
|
|
240
|
+
|
|
241
|
+
A project may keep its work items in any tracker, or in none. `al` never
|
|
242
|
+
contacts a tracker. The caller saves the items in a folder, and
|
|
243
|
+
`al check --issues <folder>` checks the items that the `tasks:` of the open
|
|
244
|
+
requests name. Without `--issues`, `al check` reads no item. A task's `ref`
|
|
245
|
+
names an item as `#n`, `owner/repo#n`, `<name>#n` for an output repo that
|
|
246
|
+
config names, an issue URL on any host, or the item's own `ref`, for
|
|
247
|
+
example `PROJ-12`. Each finding is a hint:
|
|
248
|
+
|
|
249
|
+
- `issue-missing`: a task names an item that is not in the folder;
|
|
250
|
+
- `issue-superseded`: the item's text names a superseded ADR without the ADR
|
|
251
|
+
that supersedes it, by the rule of `superseded-cited`;
|
|
252
|
+
- `issue-bare-ref`: a bare `#n` in an item of another repository, which
|
|
253
|
+
GitHub reads as that repository's issue. The request's own repository
|
|
254
|
+
comes from `git remote get-url origin`;
|
|
255
|
+
- `issue-not-sub`: the item is not a child of the request's own item, the
|
|
256
|
+
item that `assuredloop-requests.json` in the folder names (below);
|
|
257
|
+
- `phrase`: a phrase of `phrases:` matches;
|
|
258
|
+
- `issue-unread`: a file in the folder is not valid JSON, or holds an entry
|
|
259
|
+
that is not an item.
|
|
260
|
+
|
|
261
|
+
The folder holds `*.json` files. Each file holds one item, or a list of
|
|
262
|
+
items. An item is the JSON that the GitHub CLI prints with
|
|
263
|
+
`--json number,url,body,comments,parent`, so GitHub issues need no
|
|
264
|
+
conversion:
|
|
265
|
+
|
|
266
|
+
| Field | What it is | Needed |
|
|
267
|
+
|---|---|---|
|
|
268
|
+
| `body` | the item's text | yes |
|
|
269
|
+
| `url` | its address, `https://<host>/owner/repo/issues/n` for GitHub | one of `url`, `number` with `repository`, or `ref` |
|
|
270
|
+
| `number` | its number in its repository | |
|
|
271
|
+
| `comments` | a list, each with `body`, and `isMinimized` true for a hidden comment | no |
|
|
272
|
+
| `parent` | the parent item, with `url` and `number`, or `ref`; `null` for none | no: with no `parent`, the child check is not made |
|
|
273
|
+
| `ref` | the reference that a task names, for another tracker, for example `PROJ-12` | for another tracker |
|
|
274
|
+
| `repository` | `owner/repo`, or a project key | no |
|
|
275
|
+
|
|
276
|
+
To save GitHub issues, GitHub Enterprise included, save each issue with
|
|
277
|
+
`gh issue view`. `gh issue list` gives at most 100 comments of an issue.
|
|
278
|
+
|
|
279
|
+
```sh
|
|
280
|
+
mkdir -p /tmp/items
|
|
281
|
+
gh issue view 31 --repo acme/invoicer \
|
|
282
|
+
--json number,url,body,comments,parent > /tmp/items/invoicer-31.json
|
|
283
|
+
GH_HOST=github.example.com gh issue view 5 --repo acme/web \
|
|
284
|
+
--json number,url,body,comments,parent > /tmp/items/web-5.json
|
|
285
|
+
al check --issues /tmp/items
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
An item of another tracker:
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
{"ref": "PROJ-12", "repository": "PROJ", "body": "Build the export (ADR-3).",
|
|
292
|
+
"comments": [{"body": "Done.", "isMinimized": false}], "parent": {"ref": "PROJ-1"}}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The folder also says which item is each request's own item, in the file
|
|
296
|
+
`assuredloop-requests.json`. `al` reads the own item from this file only,
|
|
297
|
+
never from an origin source. Without the file, or for a request that it
|
|
298
|
+
does not list, `issue-not-sub` is not checked, and the `Not known` line
|
|
299
|
+
says so once for each request. The file is not read as an item. A file that
|
|
300
|
+
is not valid gets `issue-unread` and is ignored; `al check` goes on.
|
|
301
|
+
|
|
302
|
+
- `format`: the exact text `assuredloop-items/1`;
|
|
303
|
+
- `requests`: each key is a request's name, and its value is that request's
|
|
304
|
+
own item, in a form that a task's `ref` uses: a URL, `owner/repo#n`, or
|
|
305
|
+
another tracker's key, for example `PROJ-1`.
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"format": "assuredloop-items/1",
|
|
310
|
+
"requests": {
|
|
311
|
+
"invoice-exports": "https://github.com/acme/invoicer/issues/30",
|
|
312
|
+
"pay-run": "PROJ-1"
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
To write it for a GitHub issue:
|
|
318
|
+
|
|
319
|
+
```sh
|
|
320
|
+
printf '{"format": "assuredloop-items/1", "requests": {"invoice-exports": "%s"}}\n' \
|
|
321
|
+
"$(gh issue view 30 --repo acme/invoicer --json url --jq .url)" \
|
|
322
|
+
> /tmp/items/assuredloop-requests.json
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
The JSON Schema (draft 2020-12) of each file, for a person or an AI that
|
|
326
|
+
writes them. An item file:
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
{
|
|
330
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
331
|
+
"title": "an item file: one item, or a list of items",
|
|
332
|
+
"oneOf": [
|
|
333
|
+
{"$ref": "#/$defs/item"},
|
|
334
|
+
{"type": "array", "items": {"$ref": "#/$defs/item"}}
|
|
335
|
+
],
|
|
336
|
+
"$defs": {
|
|
337
|
+
"item": {
|
|
338
|
+
"type": "object",
|
|
339
|
+
"required": ["body"],
|
|
340
|
+
"anyOf": [
|
|
341
|
+
{"required": ["url"], "properties": {"url": {"type": "string", "pattern": "^https?://[^/\\s]+/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+/issues/[0-9]+([^A-Za-z0-9_][\\s\\S]*)?$"}}},
|
|
342
|
+
{"required": ["number", "repository"]},
|
|
343
|
+
{"required": ["ref"]}
|
|
344
|
+
],
|
|
345
|
+
"properties": {
|
|
346
|
+
"body": {"type": "string"},
|
|
347
|
+
"url": {"type": "string"},
|
|
348
|
+
"number": {"type": "integer"},
|
|
349
|
+
"comments": {
|
|
350
|
+
"type": "array",
|
|
351
|
+
"items": {
|
|
352
|
+
"type": "object",
|
|
353
|
+
"required": ["body"],
|
|
354
|
+
"properties": {"body": {"type": "string"}, "isMinimized": {"type": "boolean"}}
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
"parent": {
|
|
358
|
+
"oneOf": [
|
|
359
|
+
{"type": "null"},
|
|
360
|
+
{
|
|
361
|
+
"type": "object",
|
|
362
|
+
"properties": {"url": {"type": "string"}, "number": {"type": "integer"}, "ref": {"type": "string", "minLength": 1}}
|
|
363
|
+
}
|
|
364
|
+
]
|
|
365
|
+
},
|
|
366
|
+
"ref": {"type": "string", "minLength": 1},
|
|
367
|
+
"repository": {"type": "string", "minLength": 1}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
`assuredloop-requests.json`:
|
|
375
|
+
|
|
376
|
+
```json
|
|
377
|
+
{
|
|
378
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
379
|
+
"title": "assuredloop-requests.json",
|
|
380
|
+
"type": "object",
|
|
381
|
+
"required": ["format", "requests"],
|
|
382
|
+
"additionalProperties": false,
|
|
383
|
+
"properties": {
|
|
384
|
+
"format": {"const": "assuredloop-items/1"},
|
|
385
|
+
"requests": {
|
|
386
|
+
"type": "object",
|
|
387
|
+
"additionalProperties": {"type": "string", "minLength": 1}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
```
|
|
188
392
|
|
|
189
393
|
## Search
|
|
190
394
|
|
|
@@ -266,21 +470,70 @@ does not change; `al` hashes each paragraph without its marker.
|
|
|
266
470
|
|
|
267
471
|
**From 0.1.0.** v4 (0.2.0) does not read 0.1.0 records. Keep 0.1.0 pinned
|
|
268
472
|
until you move. Older records stay readable as files; run the pinned 0.1.0 at
|
|
269
|
-
those commits.
|
|
473
|
+
those commits.
|
|
474
|
+
|
|
475
|
+
In a project where `.assuredloop` is a file, as 0.1.0 writes it, each `al`
|
|
476
|
+
command refuses with exit 2 and one line: `al: .assuredloop is a file, so the
|
|
477
|
+
project has 0.1.0 records, which v4 does not read; keep 0.1.0 pinned, or move
|
|
478
|
+
as "From 0.1.0" in the README says`. It reads and writes nothing else, so
|
|
479
|
+
`al check` never passes there. `al --version` and the usage message still
|
|
480
|
+
work. To move:
|
|
270
481
|
|
|
271
482
|
1. Finish each open 0.1.0 request on its pinned copy, or start it again
|
|
272
483
|
under v4.
|
|
273
484
|
2. Install 0.2.0 (see "Install").
|
|
274
|
-
3.
|
|
485
|
+
3. Remove the 0.1.0 file: `git rm .assuredloop`. v4 keeps its settings in
|
|
486
|
+
the folder `.assuredloop/`. If the file had a `root:` line, write the
|
|
487
|
+
same line in `.assuredloop/config.yaml` (see "Settings").
|
|
488
|
+
4. Run `al spec --add-ids`, as in steps 1-5 above. It marks every paragraph
|
|
275
489
|
and writes the adoption record.
|
|
276
490
|
|
|
491
|
+
## Hints, and the rules that block
|
|
492
|
+
|
|
493
|
+
`al check` gives the AI review hints, and the review decides. A finding is
|
|
494
|
+
`not ok` only for a rule that meets two tests: breaking it lets an
|
|
495
|
+
unapproved or wrong decision be built, and an exact check of the records,
|
|
496
|
+
hashes and links decides it, with no judgement. These rules are:
|
|
497
|
+
|
|
498
|
+
- **An unsigned requirement** (`not ok unsigned-requirement`). In each
|
|
499
|
+
request whose record has `requirements:`, open or archived, the latest
|
|
500
|
+
version of each requirement has a valid sign-off. A sign-off is valid only
|
|
501
|
+
when a `covers` entry matches the requirement's id, version and `sha256`
|
|
502
|
+
exactly; its file is in the request's `origin/` folder, and the hash of
|
|
503
|
+
its signed text equals the sign-off's `sha256`; and it records when it was
|
|
504
|
+
signed and the signer's words. A requirement in `request.md` that is not
|
|
505
|
+
its latest recorded version is not signed. An entry that `al check` cannot
|
|
506
|
+
read is `not ok` too. A promise paragraph that changes with no signed
|
|
507
|
+
requirement that covers it stays `not ok signoff-coverage`.
|
|
508
|
+
- **Changed record evidence** (`not ok record-evidence`). Each origin file
|
|
509
|
+
under `sources:`, and each sign-off file, still has the hash that the
|
|
510
|
+
record lists. `al check` hashes the snapshot's text after its `---` line,
|
|
511
|
+
as `al record` did.
|
|
512
|
+
- **A citation of a superseded ADR** (`not ok superseded-cited` and
|
|
513
|
+
`superseded-status`; see "Settings").
|
|
514
|
+
|
|
515
|
+
A request record that cannot be read (`record-unreadable`) and a spec root
|
|
516
|
+
that cannot be read (`spec-root`) are `not ok` too. Every other finding is a
|
|
517
|
+
hint in every mode, `--strict` included: the marker and ID lints, the path
|
|
518
|
+
claim, a link that does not resolve, an ADR's changed text or missing links,
|
|
519
|
+
an invalid disposition, and the rest. `--strict` changes only the exit code:
|
|
520
|
+
it is 1 when a `not ok` prints.
|
|
521
|
+
|
|
522
|
+
`al check` gives the hint "promise kind?" (`promise-kind`) for a paragraph
|
|
523
|
+
that is not a promise kind and holds a requirement word: MUST, SHALL, SHOULD,
|
|
524
|
+
MAY, never or always. When the paragraph is a promise, change its kind. When
|
|
525
|
+
it applies a promise, for example a flow step that keeps a rule, give it
|
|
526
|
+
`builds-on:` the promise paragraph in its marker. The hint then goes. A
|
|
527
|
+
`builds-on:` to another kind, or to an ID that does not exist, keeps it.
|
|
528
|
+
|
|
277
529
|
## What is enforced, and by whom
|
|
278
530
|
|
|
279
531
|
| The tool checks | The skill asks | The owner decides |
|
|
280
532
|
|---|---|---|
|
|
281
533
|
| `al conclude` refuses on its three conditions (see "The close rule") | write the owner's words and the organized requirement, and ask for the sign-off | what is wanted, by signing the organized requirement |
|
|
282
|
-
|
|
|
283
|
-
|
|
|
534
|
+
| `not ok`: sign-off coverage of promise changes, unsigned requirement versions, changed record evidence, citations of superseded ADRs and their status lines, and records or a spec root that cannot be read | choose the path, and state it in a commit message and the PR | whether a changed requirement is signed again |
|
|
535
|
+
| hints for the review: IDs missing, used twice, lost or used again; kinds and links; links that do not resolve; path and typo claims; dispositions and exact equality; stale bindings; overlapping changes; ADR text changes; a proposed or accepted ADR with no `source:` or no `decides:`; result applicability; a design paragraph that links to none of its requirement's promise paragraphs in its change spec; a `for:` that names no task; matches of the configured phrases; with `--issues <folder>`, the hints from the linked work items | record each later decision, with its source | decisions whose source is the owner, such as dropping or keeping work |
|
|
536
|
+
| `check --strict` exits 1 on a `not ok`, when a project opts in | | |
|
|
284
537
|
|
|
285
538
|
`al` checks that declarations are well formed and that references resolve.
|
|
286
539
|
It does not check that a kind is right, that a requirement is fully
|
package/bin/al.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// al, the v4 command (T17 made it the one in package.json's bin).
|
|
3
3
|
import { parseArgs } from 'node:util';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
|
-
import { readFileSync, realpathSync } from 'node:fs';
|
|
5
|
+
import { readFileSync, realpathSync, statSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
|
7
8
|
import { check } from '../src/v4/check.js';
|
|
8
9
|
import { Fail, spec } from '../src/v4/spec.js';
|
|
@@ -28,7 +29,7 @@ const COMMANDS = {
|
|
|
28
29
|
},
|
|
29
30
|
index: { run: index, positionals: 0, options: { align: { type: 'string' } } },
|
|
30
31
|
spec: { run: spec, positionals: (o) => (o['add-ids'] === undefined ? Infinity : 0), options: { 'add-ids': { type: 'string' }, prefix: { type: 'string' }, yes: { type: 'boolean' } } },
|
|
31
|
-
check: { run: check, positionals: 0, options: { strict: { type: 'boolean' } } },
|
|
32
|
+
check: { run: check, positionals: 0, options: { strict: { type: 'boolean' }, issues: { type: 'string' } } },
|
|
32
33
|
export: { run: exportCommand, positionals: 0, options: { at: { type: 'string' }, out: { type: 'string' }, yes: { type: 'boolean' } } },
|
|
33
34
|
search: {
|
|
34
35
|
run: search,
|
|
@@ -102,6 +103,11 @@ async function main(argv) {
|
|
|
102
103
|
throw new Fail(`extra argument ${JSON.stringify(extra)}: ${used} takes ${n === 0 ? 'no' : n} positional argument${n === 1 ? '' : 's'}`,
|
|
103
104
|
'quote an option value that holds spaces, and leave out the extra argument');
|
|
104
105
|
}
|
|
106
|
+
// 0.1.0 kept its settings in a file .assuredloop; v4 keeps a folder (#241).
|
|
107
|
+
if (statSync(join(top, '.assuredloop'), { throwIfNoEntry: false })?.isFile()) {
|
|
108
|
+
throw new Fail('.assuredloop is a file, so the project has 0.1.0 records, which v4 does not read; keep 0.1.0 pinned, or move as "From 0.1.0" in the README says',
|
|
109
|
+
'npm install --global @assuredloop/cli@0.1.0, or move as "From 0.1.0" in the README says');
|
|
110
|
+
}
|
|
105
111
|
let out;
|
|
106
112
|
try {
|
|
107
113
|
out = await command.run({ top, cwd, args: parsed.positionals, opts: parsed.values });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assuredloop/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "AssuredLoop keeps an AI focused on one change: the al command gathers and checks a change's requirement, spec, decisions, code and tests.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -10,8 +10,9 @@ description: >-
|
|
|
10
10
|
# AssuredLoop v4
|
|
11
11
|
|
|
12
12
|
`al` keeps the records of what was asked, decided, changed and delivered,
|
|
13
|
-
and gives hints. It refuses only in
|
|
14
|
-
test and review is your project's
|
|
13
|
+
and gives the review hints. The review decides. It refuses only in
|
|
14
|
+
`al conclude`. How and when you design, test and review is your project's
|
|
15
|
+
way of working; this skill does not set it.
|
|
15
16
|
|
|
16
17
|
## Start here, on every path
|
|
17
18
|
|
|
@@ -31,16 +32,19 @@ test and review is your project's way of working; this skill does not set it.
|
|
|
31
32
|
| 0 | No promise changes: a fix that restores what the spec says, or a typo in any paragraph. | none | no |
|
|
32
33
|
| 1 | One or a few promise paragraphs change. | a short request; no change spec | yes |
|
|
33
34
|
| 1d | Only names or wording of design paragraphs change. | a short request; no change spec | no |
|
|
34
|
-
| 2 | A real design, in one or a few PRs. | a request and a change spec | if a promise changes |
|
|
35
|
+
| 2 | A real design, in one or a few PRs. | a request and a change spec | if a promise changes, or the request has an R<n> |
|
|
35
36
|
| 3 | A big change, delivered in parts over time. | as path 2, part by part | as path 2 |
|
|
36
37
|
| S | It is not clear yet what to build. | a request with a question | yes, on the question |
|
|
37
38
|
|
|
38
39
|
The promise kinds are purpose, scope, rule, limit and definition. Work that
|
|
39
40
|
grows changes its path: a small change that needs a design becomes path 2.
|
|
40
41
|
|
|
41
|
-
4. Before you ask for review, run `al index`, then `al check`.
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
4. Before you ask for review, run `al index`, then `al check`. A `not ok` is
|
|
43
|
+
one of the few exact rules: a promise change or a requirement version
|
|
44
|
+
with no valid sign-off, changed record evidence, a citation of a
|
|
45
|
+
superseded ADR, or a record that cannot be read. Fix each one. A hint is
|
|
46
|
+
for the review, not a verdict: deal with it, or say in the PR why it
|
|
47
|
+
stays. A finding has the same severity with or without `--strict`.
|
|
44
48
|
5. Put the claim in a commit message and in the PR, for example
|
|
45
49
|
`Tier: 1 — amends INV-10`. `al check` reads the newest `Tier:` line of the
|
|
46
50
|
branch.
|
|
@@ -63,7 +67,7 @@ The export link MUST expire 30 minutes after the email is sent.
|
|
|
63
67
|
marker. Its ID is retired and never used again. `al check` gives a hint
|
|
64
68
|
that names the deletion; when it is on purpose, nothing more is needed. In
|
|
65
69
|
`specs/`, a removal needs `removes:<ID>` in a change spec: a lost ID there
|
|
66
|
-
|
|
70
|
+
gets a hint that asks for it.
|
|
67
71
|
- For a new paragraph, write the text with no marker. Then run
|
|
68
72
|
`al spec --add-ids <file> --yes`: it gives each unmarked paragraph the next
|
|
69
73
|
free ID. Then write the kind and the links into each new marker.
|
|
@@ -78,10 +82,21 @@ The export link MUST expire 30 minutes after the email is sent.
|
|
|
78
82
|
needs at least one link. In `specs/`, the ID of a change spec paragraph
|
|
79
83
|
names its request: `invoice-exports/SP-12`.
|
|
80
84
|
- An ADR in `specs/adr/` has one marker on its heading: `ADR-<n>`, its kind,
|
|
81
|
-
and its `decides:`, `source:` and `supersedes:` links.
|
|
82
|
-
|
|
83
|
-
`decides:`.
|
|
84
|
-
|
|
85
|
+
and its `decides:`, `source:` and `supersedes:` links. `al check` gives a
|
|
86
|
+
hint for each proposed or accepted ADR with no `source:` or no
|
|
87
|
+
`decides:`.
|
|
88
|
+
- Any text may be rewritten in other words when its meaning stays the same,
|
|
89
|
+
an accepted or superseded ADR's text too. Write a new ADR that supersedes
|
|
90
|
+
it only when the decision changes, in whole or in part. `al check` gives a
|
|
91
|
+
hint on a changed ADR: check that its decision is unchanged. After the
|
|
92
|
+
rewrite, run `al index --align <ID>` for each paragraph that links to it.
|
|
93
|
+
- When an ADR is superseded, set its first line to
|
|
94
|
+
`Status: superseded by ADR-<m>`, and put `supersedes:ADR-<n>` in the marker
|
|
95
|
+
of ADR-m. When several ADRs supersede it, name each one:
|
|
96
|
+
`Status: superseded by ADR-11, ADR-12`. Where a spec paragraph, an open
|
|
97
|
+
request or a proposed or accepted ADR names ADR-n, name an ADR that
|
|
98
|
+
supersedes it beside it: in the same marker for a link, in the same
|
|
99
|
+
paragraph for a word. Else `al check` gives `not ok`.
|
|
85
100
|
- `al` writes every hash and every derived field. Do not write them by hand.
|
|
86
101
|
|
|
87
102
|
## Path 0: a fix
|
|
@@ -127,9 +142,9 @@ The export link MUST expire 30 minutes after the email is sent.
|
|
|
127
142
|
1. `al new <name> --tier 1d --from -`, with a one-line note of where the
|
|
128
143
|
correction comes from.
|
|
129
144
|
2. Change only names or wording of design paragraphs in `specs/`. Add or
|
|
130
|
-
remove no paragraph.
|
|
131
|
-
supersedes it.
|
|
132
|
-
3. Claim `Tier: 1d — <what changes>`. `al check` gives
|
|
145
|
+
remove no paragraph. You may reword an ADR when its decision stays; a
|
|
146
|
+
changed decision is a new ADR that supersedes it.
|
|
147
|
+
3. Claim `Tier: 1d — <what changes>`. `al check` gives a hint when the PR
|
|
133
148
|
touches a promise paragraph, or adds or removes a design paragraph. No
|
|
134
149
|
sign-off is needed.
|
|
135
150
|
4. Run `al conclude <name> --yes` with the PR.
|
|
@@ -138,8 +153,11 @@ The export link MUST expire 30 minutes after the email is sent.
|
|
|
138
153
|
|
|
139
154
|
1. `al new <name> --tier 2 --from -`. Write the organized requirement as in
|
|
140
155
|
path 1, with `Out:` and `Assumed:` lines where they help. Draft freely;
|
|
141
|
-
deliver with the owner's sign-off or after it.
|
|
142
|
-
promise kind
|
|
156
|
+
deliver with the owner's sign-off or after it. Each R<n> needs the
|
|
157
|
+
owner's sign-off, also when the change touches no promise kind: until it
|
|
158
|
+
is signed, `al check` gives `not ok unsigned-requirement`. For a
|
|
159
|
+
design-only change, which needs no sign-off, write the organized section
|
|
160
|
+
with no `### R<n>`: a short note of what the design changes and why.
|
|
143
161
|
2. Write the design in `requests/<name>/spec.md`, the change spec: the
|
|
144
162
|
promises this change adds or changes, its design, its plan. The order
|
|
145
163
|
of the paragraphs is free. The recommended order is one section per
|
|
@@ -198,7 +216,12 @@ project uses them, list them in the record as
|
|
|
198
216
|
`tasks: [{id: T2, ref: "issue #34", delivers: [R3]}]`, and name the task in
|
|
199
217
|
the commit messages of its PRs. Plan, step and migration paragraphs may live
|
|
200
218
|
in the tracker issue that the task's `ref` names, instead of in the change
|
|
201
|
-
spec.
|
|
219
|
+
spec. `al` never contacts a tracker: save the items in a folder (the README
|
|
220
|
+
has the format and the `gh` commands), and `al check --issues <folder>`
|
|
221
|
+
gives hints on them: an item missing from the folder, a superseded ADR
|
|
222
|
+
named without its successor, a bare `#n` in another repository's item, an
|
|
223
|
+
item that is not a child of the request's own item, and the phrases of
|
|
224
|
+
`phrases:` in `.assuredloop/config.yaml`. Run `al conclude <name> --yes` with the last part.
|
|
202
225
|
|
|
203
226
|
## Path S: a spike
|
|
204
227
|
|