@am_shork/attest 0.4.2 → 0.4.3
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/CHANGELOG.md +202 -55
- package/README.md +17 -4
- package/dist/core/parser.js +0 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,172 @@ input, removes/renames a public API or `--json` field, or changes a default
|
|
|
13
13
|
runtime behavior an existing invocation relies on — diagnostic message text is
|
|
14
14
|
not API.
|
|
15
15
|
|
|
16
|
+
## [0.4.3] - 2026-08-01
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **The README's two getting-started samples are now the fixture the packaging
|
|
21
|
+
test installs and runs.** `tests/docs-consistency.spec.ts` opened by naming
|
|
22
|
+
this hole: every `*.md` outside the generated `SPEC.md` is hand-written prose
|
|
23
|
+
that no mechanism binds to the code, "the framework's own blind spot (design
|
|
24
|
+
§11)". Every check in that file until now gated a version string or a name;
|
|
25
|
+
this is the first that gates *meaning* — a registry or a spec on the page that
|
|
26
|
+
the engine would now reject was undetectable, which for a tool whose whole
|
|
27
|
+
claim is reporting drift between intent and what the tests execute is the
|
|
28
|
+
failure that costs the most.
|
|
29
|
+
The mechanism is a naming split rather than new machinery, and most of it was
|
|
30
|
+
already there and unnamed: `fixtures/consumer/` imports by bare specifier from
|
|
31
|
+
`@am_shork/attest` and `@am_shork/attest/define` because it is installed from a
|
|
32
|
+
real tarball outside the repo — which is exactly what a reader has to be able
|
|
33
|
+
to paste. So the two files lost their fixture-specific commentary, gained the
|
|
34
|
+
README's teaching comments, and the README now quotes them; the new assertion
|
|
35
|
+
is byte-equality between the fenced block and the file, newline-normalised so a
|
|
36
|
+
CRLF checkout cannot call a clean tree stale. The fixture is the original and
|
|
37
|
+
the page is the quote: breaking either side fails.
|
|
38
|
+
*What moved in the README:* the spec sample is now `session.spec.ts` beside
|
|
39
|
+
`session.ts` rather than `auth/session.spec.ts`, matching the layout the
|
|
40
|
+
packaging test actually runs, and it shows both of the requirement's scenarios
|
|
41
|
+
because the installed run asserts `scenarioCount: 2`.
|
|
42
|
+
*Limits, both deliberate:* the gate covers 2 of the 23 fenced `ts` blocks in
|
|
43
|
+
the README and `docs/en/**` — the rest are elided, ✓/✗-annotated, or
|
|
44
|
+
deliberately rejected by the engine, and none of those can equal a runnable
|
|
45
|
+
file. And the two claims are joined only on a release: this gate runs in
|
|
46
|
+
`pnpm test` and proves the page and the fixture agree, while the run that
|
|
47
|
+
proves the fixture still *works* is `pnpm test:consumer`, which needs a network
|
|
48
|
+
and a tarball. Both remaining halves are in `Planned`.
|
|
49
|
+
|
|
50
|
+
- **The `docs/en` ↔ `docs/zh` mirror rule is now a gate rather than a paragraph
|
|
51
|
+
in `CLAUDE.md`.** Two assertions in `tests/docs-consistency.spec.ts`, both
|
|
52
|
+
scoped to what is comparable without comparing prose. The file set: a page
|
|
53
|
+
added on one side only is not a stale translation, it is a page that does not
|
|
54
|
+
exist, and nothing in the repository would have noticed. And the structure of
|
|
55
|
+
each pair — the heading tree as levels in document order, and the sequence of
|
|
56
|
+
fence info strings — which catches a section dropped or reordered in
|
|
57
|
+
translation, a code sample present in one language only, and a ```ts that
|
|
58
|
+
became ```bash.
|
|
59
|
+
*The parse is the part with a trap in it.* A `#` at the start of a line inside
|
|
60
|
+
a ```bash sample is a shell comment, and these pages are full of them, so the
|
|
61
|
+
reader tracks fences and skips their contents; a closing fence must repeat the
|
|
62
|
+
character that opened it, or a ``` quoted inside a ~~~ block ends the wrong
|
|
63
|
+
thing. An unterminated fence is reported rather than ignored, because it hides
|
|
64
|
+
every heading after it — two documents can agree by being truncated. All three
|
|
65
|
+
are asserted directly, on the standard this file already applies to its other
|
|
66
|
+
parses: a check whose parser can silently match nothing is not a check.
|
|
67
|
+
*One third of the queued entry turned out to be already covered and was not
|
|
68
|
+
written.* It asked for the `##` issue-code headings of `troubleshooting.md` to
|
|
69
|
+
be held equal between languages; the roster check that predates it already
|
|
70
|
+
asserts each language against `ISSUE_CODES` in both directions, so their
|
|
71
|
+
equality follows and a third assertion would catch nothing they let through.
|
|
72
|
+
*The limit, stated because it is not small:* a page whose structure held while
|
|
73
|
+
its meaning went stale passes everything here. Translation is not a diff. Same
|
|
74
|
+
shape as the limit the History gate carries — it can check that a row cites a
|
|
75
|
+
real version, not that the row is true.
|
|
76
|
+
|
|
77
|
+
### Documentation
|
|
78
|
+
|
|
79
|
+
- **The agent-facing documents gained a diagnosis discipline, a review pass, and
|
|
80
|
+
a design vocabulary**, adapted from an external skill collection rather than
|
|
81
|
+
copied from it — the parts that survived are the ones this repository's own
|
|
82
|
+
defect history had already paid for.
|
|
83
|
+
*New skill, `.claude/skills/diagnose/`.* Its one rule is that no hypothesis may
|
|
84
|
+
be formed before a command exists that has been run at least once and can go
|
|
85
|
+
**red** on the bug in hand. Every defect this repository has recorded failed
|
|
86
|
+
silently rather than loudly, so that bar is higher here than the phrase
|
|
87
|
+
suggests, and the skill's local content is the questions that tell a green run
|
|
88
|
+
apart from one that never happened: read the test count rather than the colour,
|
|
89
|
+
since a `beforeAll` throw reports a file as *skipped* (`[0.4.2]`); confirm the
|
|
90
|
+
file you care about actually ran, since a glob that matched nothing is a clean
|
|
91
|
+
run over code that never loaded; and name which half a command covers before
|
|
92
|
+
trusting it. The ways that goes wrong are the Gotchas in `CLAUDE.md`, pointed
|
|
93
|
+
at rather than restated — a second copy of them would only be a second copy to
|
|
94
|
+
drift. The remainder is the standard loop: minimise until every element is
|
|
95
|
+
load-bearing, three to five falsifiable hypotheses before testing any,
|
|
96
|
+
`[DEBUG-…]`-tagged probes, and "no correct seam is itself the finding".
|
|
97
|
+
*`pre-commit-check` gained §8, a reading pass on two axes*, kept unmerged and
|
|
98
|
+
unranked against each other because a change can pass either and fail the
|
|
99
|
+
other. The Spec axis is mostly machine-checked here and nowhere else — `check`,
|
|
100
|
+
`cover` and `verify` *are* drift detection — so it reads only for what those
|
|
101
|
+
cannot reach: whether an `ATX-n`'s text states the contract, whether `params`
|
|
102
|
+
pin the values the scenario should be pinned to, and whether a green scenario
|
|
103
|
+
attests its requirement or merely passes. The Standards axis carries a twelve-
|
|
104
|
+
smell baseline (Fowler, *Refactoring* ch.3) as labelled heuristics that any
|
|
105
|
+
documented rule here overrides, with two weighted for this repository:
|
|
106
|
+
duplicated path-shaped code means a second copy that did not go through
|
|
107
|
+
`src/core/paths.ts`, and speculative generality is the other face of §1's rule
|
|
108
|
+
that every requirement costs a scenario forever.
|
|
109
|
+
*`CLAUDE.md` gained a design vocabulary* — module, interface, depth, seam,
|
|
110
|
+
adapter, and the three tests that settle most arguments — which exists because
|
|
111
|
+
the two structures it names were already these things and had no word for it.
|
|
112
|
+
`src/core/paths.ts` is the repository's deepest module, and the deletion test
|
|
113
|
+
is not hypothetical for it: every silent path defect in the Gotchas is what
|
|
114
|
+
this codebase looked like when that knowledge lived at the call sites. The two
|
|
115
|
+
registry readers are two adapters at one real seam, which is what makes the
|
|
116
|
+
differential suite possible — and the entry marks where the analogy stops,
|
|
117
|
+
since which adapter runs is fixed by the command and never a fallback, that
|
|
118
|
+
being a security property rather than a design preference.
|
|
119
|
+
*And a section on where a decision goes*, which is this file. It is stated in
|
|
120
|
+
`CLAUDE.md` rather than only in `pre-commit-check` because the moment to write
|
|
121
|
+
one is when the decision happens; a design evaluated and dropped mid-session is
|
|
122
|
+
what is most likely to be lost, and by commit time the reasoning that killed it
|
|
123
|
+
is already gone.
|
|
124
|
+
*The candidate that did not survive is the reason the section exists.* The
|
|
125
|
+
proposal was a `docs/adr/` directory holding three decisions lifted out of the
|
|
126
|
+
Gotchas — why `fixtures/nested-spec/` may not be flattened, why `render
|
|
127
|
+
--check` compares the document rather than the bytes, why one `spawn` helper.
|
|
128
|
+
All three were already recorded here, in more detail than the proposal would
|
|
129
|
+
have carried, so the directory's whole effect would have been to split a store
|
|
130
|
+
this file keeps deliberately single. Recorded rather than filed as work, since
|
|
131
|
+
no such directory exists: had it been put under `docs/en/`, the mirror gate
|
|
132
|
+
added above would not have seen it at all — `pages()` is a non-recursive
|
|
133
|
+
`readdir` filtered to `*.md`, so a subdirectory is invisible to the file-set
|
|
134
|
+
check, the structure check, and the version-bearing sample scan alike. A
|
|
135
|
+
documentation subdirectory is therefore an unmirrored one, silently, which is
|
|
136
|
+
this repository's signature failure shape wearing documentation's clothes.
|
|
137
|
+
|
|
138
|
+
### Fixed
|
|
139
|
+
|
|
140
|
+
- **The packaging test installed into a directory Windows spells two ways, and
|
|
141
|
+
read the result as the package being broken.** `tests/consumer.spec.ts` staged
|
|
142
|
+
its throwaway project under `os.tmpdir()`, which on Windows reads `TEMP` — set
|
|
143
|
+
by the OS to the 8.3 short form, `C:\Users\JOHN_C~1\...`. Vite resolves the
|
|
144
|
+
peer `vitest` from the *long* form of the same directory, so the consumer's
|
|
145
|
+
spec file and the installed `@am_shork/attest` imported two module instances of
|
|
146
|
+
it; `describe` ran against an uninitialised runner and the spec died at import
|
|
147
|
+
with `Cannot read properties of undefined (reading 'config')`, before a
|
|
148
|
+
scenario executed. What that surfaced as is the part worth recording: `attest
|
|
149
|
+
verify` reported `tests-red` plus `declared-not-run` for every scenario —
|
|
150
|
+
a report indistinguishable from a genuinely broken package, produced by a
|
|
151
|
+
package that is fine. Staging through `realpath` fixes it.
|
|
152
|
+
This is `CLAUDE.md`'s first failure family one form further out — not a
|
|
153
|
+
separator inside a path, but two spellings of one directory — and it was
|
|
154
|
+
unreachable until `[0.4.2]` made this suite run on Windows at all. CI is Linux,
|
|
155
|
+
where `os.tmpdir()` has one spelling, so the pipeline was never going to say
|
|
156
|
+
otherwise.
|
|
157
|
+
|
|
158
|
+
- **The AST parser's own source file was binary, so every change to it since the
|
|
159
|
+
initial commit was unreviewable.** `src/core/parser.ts` separated the two
|
|
160
|
+
halves of its param-ref dedupe key with a **raw NUL byte** rather than the
|
|
161
|
+
`\x00` escape. One character no editor renders reclassified the whole file:
|
|
162
|
+
`git diff` answered `Binary files a/… and b/… differ` for every change to it,
|
|
163
|
+
`git grep` and `grep` skipped it in silence, and `.gitattributes`' `eol=lf`
|
|
164
|
+
never applied, because `text=auto` tests for binary first.
|
|
165
|
+
*What it cost is the reviewing, not the running.* A string escape is resolved
|
|
166
|
+
by the compiler, so the key is the same three code units on every platform —
|
|
167
|
+
no behaviour changes, and no requirement, scenario or schema moves. What was
|
|
168
|
+
lost is that every text sweep over this repository silently excluded the one
|
|
169
|
+
module that turns spec source into the plan every command reads. A pre-commit
|
|
170
|
+
pass grepping `src/` for a rule — a `path.relative` outside `paths.ts`, a
|
|
171
|
+
stray `spawn` — was answering about 27 of 28 files and reporting no gap, which
|
|
172
|
+
is worse than not having run it.
|
|
173
|
+
*And it shipped.* `files` carries `dist`, and `tsc` copies a template
|
|
174
|
+
literal's contents verbatim, so `dist/core/parser.js` put the byte into every
|
|
175
|
+
installed copy.
|
|
176
|
+
Recorded at this length because the defect is not the byte but its invisibility:
|
|
177
|
+
this is `CLAUDE.md`'s silent-failure shape with no platform in it at all, and
|
|
178
|
+
the tools anyone would bring to look for it are the ones it disables. The last
|
|
179
|
+
thing it costs is its own diff — the old side of this commit is still the
|
|
180
|
+
binary blob, so this entry is the only readable account of the change.
|
|
181
|
+
|
|
16
182
|
## [0.4.2] - 2026-07-30
|
|
17
183
|
|
|
18
184
|
### Added
|
|
@@ -1880,26 +2046,10 @@ feedback reports are triaged into issues or `Unreleased` and deliberately not
|
|
|
1880
2046
|
kept in this tree; that rule stands, and holds for anything with a reporter, a
|
|
1881
2047
|
repro, or a state to track. What is kept here is the narrower thing this file
|
|
1882
2048
|
already keeps one section of: a **decision with an argument attached**, where
|
|
1883
|
-
losing the argument would cost more than losing the task. The
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
run files the defect.
|
|
1888
|
-
|
|
1889
|
-
- **A structural parity gate over `docs/en/**` and `docs/zh/**`.** The mirror
|
|
1890
|
-
rule — one Chinese page per English page, matching down to the comments inside
|
|
1891
|
-
the code samples, both carried by the same commit — is enforced today by a
|
|
1892
|
-
paragraph in `CLAUDE.md` and by whoever remembers to read it. The home is
|
|
1893
|
-
`tests/docs-consistency.spec.ts`, which already enumerates both directories
|
|
1894
|
-
and already says in its own header why it checks only the two couplings tight
|
|
1895
|
-
enough to assert cheaply; this is a third. What is comparable without
|
|
1896
|
-
comparing prose: the heading tree (level, order, count), the count of fenced
|
|
1897
|
-
code blocks, and — for `troubleshooting.md` — the set of `##` issue-code
|
|
1898
|
-
headings, which is machine-derived on both sides and so must be equal
|
|
1899
|
-
exactly. Translation is not a diff and nothing here should pretend otherwise,
|
|
1900
|
-
which is also the limit: a page whose structure held while its meaning went
|
|
1901
|
-
stale passes. Same shape as the limit the History gate already carries — it
|
|
1902
|
-
can check that a row cites a real version, not that the row is true.
|
|
2049
|
+
losing the argument would cost more than losing the task. The last below came out
|
|
2050
|
+
of reading another repository against this one — `msw`. The first came out of
|
|
2051
|
+
building something queued here, which is the other way an entry arrives: a gate
|
|
2052
|
+
that finds a defect on its first run files the defect.
|
|
1903
2053
|
|
|
1904
2054
|
- **A diagnosable refusal when the resolved compiler has no AST API.**
|
|
1905
2055
|
Discovered by building the `typescript` matrix (see `[0.4.2]`), which is
|
|
@@ -1928,40 +2078,36 @@ run files the defect.
|
|
|
1928
2078
|
it yet — a separate decision, with the honest answer for now being that the
|
|
1929
2079
|
supported range has a ceiling and says so.
|
|
1930
2080
|
|
|
1931
|
-
- **
|
|
1932
|
-
`
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
byte-equal to a living fixture while the paragraph above it describes the old
|
|
1962
|
-
behaviour still passes — the same limit already written down for the History
|
|
1963
|
-
rows, and the same answer: this binds what is machine-derivable and leaves the
|
|
1964
|
-
rest to a reader.
|
|
2081
|
+
- **Region-marked samples, so a partial quote can be bound too.** The whole-file
|
|
2082
|
+
half of this shipped in `[0.4.3]`: the README's two getting-started blocks
|
|
2083
|
+
are now byte-equal to `fixtures/consumer/`. That buys 2 of the 23 fenced `ts`
|
|
2084
|
+
blocks in the README and `docs/en/**`, and the ceiling is structural rather
|
|
2085
|
+
than lazy — the other 21 are elided (`{ /* … */ }`), annotated (`// ✓ the single
|
|
2086
|
+
source`, `// ✗ a literal that can drift`), or deliberately rejected by the engine
|
|
2087
|
+
(`params: { maxMb: MAX_MB }`), and none of those can equal a runnable file.
|
|
2088
|
+
**What raises it is marking spans, not a second mechanism.** A
|
|
2089
|
+
`// #region readme:registry` and its close inside the fixture, with the
|
|
2090
|
+
assertion running block against dedented span, lets one file feed several
|
|
2091
|
+
passages and makes a partial quote bindable by construction. Where that
|
|
2092
|
+
actually reaches, counted rather than hoped: `troubleshooting.md`'s
|
|
2093
|
+
single-source pair is two verbatim lines of `fixtures/consumer/session.spec.ts`
|
|
2094
|
+
and its `AUTH-3` block is the registry entry at a different indent — so a
|
|
2095
|
+
handful more, not the remainder. Anything carrying a `✓`/`✗` is authored
|
|
2096
|
+
commentary and stays a fragment; that is what it is for.
|
|
2097
|
+
*The ✗ blocks are a separate question, and worth not conflating.* A sample
|
|
2098
|
+
showing input the engine must reject could be bound to a fixture whose
|
|
2099
|
+
*rejection* is asserted — which is what `fixtures/eval-invalid-registry`
|
|
2100
|
+
already is. Binding those is the more valuable half, because a counterexample
|
|
2101
|
+
that stopped being a counterexample is the one kind of stale sample that
|
|
2102
|
+
actively teaches the wrong thing. It is also the one that needs a fixture per
|
|
2103
|
+
diagnostic, so it is not free the way the regions are.
|
|
2104
|
+
*The mirror rule still caps what can be claimed on the Chinese side.*
|
|
2105
|
+
`docs/zh/**` matches `docs/en/**` down to the comments inside its code samples,
|
|
2106
|
+
so byte-equality holds on one side only and the zh blocks stay translated prose
|
|
2107
|
+
around the same code — the parity gate in `[0.4.3]` is what covers them now,
|
|
2108
|
+
and it covers structure, not meaning. Unchanged from the whole-file half: the residual
|
|
2109
|
+
risk recorded with it in `[0.4.3]` — that the gate and the run proving the
|
|
2110
|
+
fixture still works live in different suites — applies here identically.
|
|
1965
2111
|
|
|
1966
2112
|
## Under consideration
|
|
1967
2113
|
|
|
@@ -2271,7 +2417,8 @@ requirement, and human review at propose is still the whole answer. What is
|
|
|
2271
2417
|
rejected is grouping as a way to assist it, and this reopens only on a
|
|
2272
2418
|
contradiction that grouping would have caught.
|
|
2273
2419
|
|
|
2274
|
-
[Unreleased]: https://gitlab.com/Pseudorca/attest/-/compare/v0.4.
|
|
2420
|
+
[Unreleased]: https://gitlab.com/Pseudorca/attest/-/compare/v0.4.3...main
|
|
2421
|
+
[0.4.3]: https://gitlab.com/Pseudorca/attest/-/tags/v0.4.3
|
|
2275
2422
|
[0.4.2]: https://gitlab.com/Pseudorca/attest/-/tags/v0.4.2
|
|
2276
2423
|
[0.4.1]: https://gitlab.com/Pseudorca/attest/-/tags/v0.4.1
|
|
2277
2424
|
[0.4.0]: https://gitlab.com/Pseudorca/attest/-/tags/v0.4.0
|
package/README.md
CHANGED
|
@@ -67,13 +67,13 @@ export default defineRequirements({
|
|
|
67
67
|
});
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
**2. Attest it with
|
|
70
|
+
**2. Attest it with scenarios** (`session.spec.ts`):
|
|
71
71
|
|
|
72
72
|
```ts
|
|
73
73
|
import { expect } from 'vitest';
|
|
74
74
|
import { requirement, scenario } from '@am_shork/attest';
|
|
75
|
-
import reqs from '
|
|
76
|
-
import { createSession, advance, isValid } from './session.js';
|
|
75
|
+
import reqs from './requirements/auth.reqs.js';
|
|
76
|
+
import { createSession, advance, touch, isValid } from './session.js';
|
|
77
77
|
|
|
78
78
|
requirement('AUTH-3', () => {
|
|
79
79
|
scenario('idle timeout invalidates the session', () => {
|
|
@@ -82,9 +82,22 @@ requirement('AUTH-3', () => {
|
|
|
82
82
|
advance(s, t + 1, 'minutes');
|
|
83
83
|
expect(isValid(s, t)).toBe(false);
|
|
84
84
|
});
|
|
85
|
+
|
|
86
|
+
scenario('activity resets the idle timer', () => {
|
|
87
|
+
const t = reqs['AUTH-3'].params.idleTimeoutMin;
|
|
88
|
+
const s = createSession();
|
|
89
|
+
advance(s, t - 1, 'minutes');
|
|
90
|
+
touch(s);
|
|
91
|
+
advance(s, t - 1, 'minutes');
|
|
92
|
+
expect(isValid(s, t)).toBe(true);
|
|
93
|
+
});
|
|
85
94
|
});
|
|
86
95
|
```
|
|
87
96
|
|
|
97
|
+
Both files above are quoted from `fixtures/consumer/`, which the packaging test
|
|
98
|
+
installs from a real tarball and runs — a test asserts the quotes are byte-equal
|
|
99
|
+
to the files, so a sample the engine would now reject cannot survive here.
|
|
100
|
+
|
|
88
101
|
**3. Run the engine:**
|
|
89
102
|
|
|
90
103
|
```bash
|
|
@@ -136,7 +149,7 @@ Every diagnostic carries a `code`, and every code has a section in
|
|
|
136
149
|
```
|
|
137
150
|
ERROR registry-not-static (requirements/upload.reqs.ts:5)
|
|
138
151
|
Value is not a literal.
|
|
139
|
-
→ https://gitlab.com/Pseudorca/attest/-/blob/v0.4.
|
|
152
|
+
→ https://gitlab.com/Pseudorca/attest/-/blob/v0.4.3/docs/en/troubleshooting.md#registry-not-static
|
|
140
153
|
```
|
|
141
154
|
|
|
142
155
|
The anchor **is** the code, so the link cannot point somewhere the section
|
package/dist/core/parser.js
CHANGED
|
Binary file
|
package/package.json
CHANGED