@am_shork/attest 1.1.0 → 1.1.1
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 +255 -41
- package/README.md +7 -2
- package/dist/core/schema.js +1 -1
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -90,6 +90,149 @@ above and 110 stale lines on this one.
|
|
|
90
90
|
security hole takes a major, on whatever schedule a hole is found. That price is
|
|
91
91
|
now stated rather than unknown, which is what made the line decidable at all.
|
|
92
92
|
|
|
93
|
+
## [1.1.1] - 2026-09-03
|
|
94
|
+
|
|
95
|
+
### Documentation
|
|
96
|
+
|
|
97
|
+
- **`docs/{en,zh}/intent-quality.md` gains a seventh finding: a param no
|
|
98
|
+
statement interpolates changes with nothing outside the line to show it.**
|
|
99
|
+
`render --check` gives a review surface for the 92.1% of this repository's
|
|
100
|
+
params that a statement does interpolate — editing one moves `SPEC.md`. It
|
|
101
|
+
cannot help with the rest: five are structurally uninterpolatable (a
|
|
102
|
+
kind-to-payload table or a list, which `non-scalar-interpolation` refuses in
|
|
103
|
+
a statement) and two are scalars nobody interpolated. For those, every gate
|
|
104
|
+
treats the edit as fine — `check` sees a schema-valid literal,
|
|
105
|
+
`possible-drift` is satisfied by any covering scenario reading the param at
|
|
106
|
+
all, and `verify` stays green wherever the assertion derives its expectation
|
|
107
|
+
from the same value — so a human reading the registry diff is the only thing
|
|
108
|
+
that catches an unintended move. Split out of the *Under consideration* entry
|
|
109
|
+
on changing an existing param's value, as the cheap half it named while the
|
|
110
|
+
WARNING it also considered stays undecided for want of a second corpus.
|
|
111
|
+
|
|
112
|
+
### Added
|
|
113
|
+
|
|
114
|
+
- **`pre-commit-check` gained §9, a hand sweep for a dependency bump.** Closes
|
|
115
|
+
the *Considered and rejected* entry on a standing `no-deprecated` gate: three
|
|
116
|
+
deprecated call sites across a Zod 3→4, Vitest 3→4 and typescript-eslint
|
|
117
|
+
8.2→8.65 walk was too small a denominator to carry a third tsconfig or an
|
|
118
|
+
`allowJs` admission into the wider typed project, so the check stays a manual
|
|
119
|
+
step — run `@typescript-eslint/no-deprecated` against a throwaway project that
|
|
120
|
+
also covers the five files no tsconfig in this tree includes
|
|
121
|
+
(`eslint.config.js`, `bin/attest.js`, the three `vitest*.config.ts`) — rather
|
|
122
|
+
than a permanent gate.
|
|
123
|
+
|
|
124
|
+
- **A gate on which files inside this tree may name the package by the name it
|
|
125
|
+
publishes under.** A built `dist/` makes this package resolve *itself*:
|
|
126
|
+
`package.json` names the subpaths in `exports`, so `@am_shork/attest/define`
|
|
127
|
+
resolves to `dist/core/registry.js` on any machine that has run a build. CI's
|
|
128
|
+
lint, test and typecheck jobs run before one, and there it resolves nowhere.
|
|
129
|
+
Measured once, on an archived `requirements.delta.ts` written with the
|
|
130
|
+
specifier the workflow document hands *adopters*: it passed `eslint`, both
|
|
131
|
+
typechecks, the whole suite, `check self`, `verify self` and `archive --apply`
|
|
132
|
+
locally — **six gates** — then failed **three CI jobs** on that one line. This
|
|
133
|
+
is the sibling of the `bin/attest.js` loads `dist/` gotcha and the more
|
|
134
|
+
dangerous half of it: there a stale build runs old code, here the build
|
|
135
|
+
vouches for an import that has no other resolution.
|
|
136
|
+
|
|
137
|
+
`tests/import-boundary.spec.ts` now reads the whole checked tree through the
|
|
138
|
+
same AST walk the runner-boundary tests use, and asserts the set of published
|
|
139
|
+
specifiers against a one-entry allowlist. The single legitimate namer is
|
|
140
|
+
`examples/judgement-table/judgement.spec.ts` at `@am_shork/attest/inspect`,
|
|
141
|
+
and it is legitimate for exactly the reason the rule exists: `./inspect` is
|
|
142
|
+
the only subpath with somewhere to resolve *to* inside this tree, via
|
|
143
|
+
`resolve.alias` in `vitest.config.ts` and the matching `paths` entry in
|
|
144
|
+
`tsconfig.typecheck.json`.
|
|
145
|
+
|
|
146
|
+
*Two things about the walk are load-bearing and neither is obvious.* It does
|
|
147
|
+
not use `findFiles`, because `SKIP_DIRS` excludes `changes/` and `archive/` —
|
|
148
|
+
correctly, for a normal scan — while `tsc` and `eslint` are handed `self/**`
|
|
149
|
+
and walk straight into both, and the import this exists for was in an
|
|
150
|
+
*archived* delta; the engine's own scanner would have produced a gate that
|
|
151
|
+
passes over the only file it was written about, which is what the first draft
|
|
152
|
+
of it did. And it covers the repo root as well as the four directories
|
|
153
|
+
`tsconfig.typecheck.json` includes, because `vitest.config.ts`,
|
|
154
|
+
`vitest.self.config.ts` and `vitest.consumer.config.ts` are named by no
|
|
155
|
+
`include` in the tree yet Vitest resolves them **first**, before it can
|
|
156
|
+
collect a single test — a published specifier there fails CI earlier and
|
|
157
|
+
harder than one under `self/`, and the directory-shaped version of this gate
|
|
158
|
+
would not have looked. Each half is pinned by an instrument guard, so a walk
|
|
159
|
+
that quietly stops reading `examples/` or the root fails on the guard rather
|
|
160
|
+
than passing empty.
|
|
161
|
+
|
|
162
|
+
- **A product identity, as delivery artifacts.** `assets/brand/` carries the
|
|
163
|
+
mark, four icon sizes and four lockups as SVG, with `assets/brand/README.md`
|
|
164
|
+
as the guide: the geometry grid, the mortise-and-tenon overlap that must not
|
|
165
|
+
become clearance, the scaling rules, the type stack and the palette. The
|
|
166
|
+
README header and `package.json`'s keywords are the rest of it.
|
|
167
|
+
|
|
168
|
+
**Nothing in `src/` reads any of it, and that is the shape of the change
|
|
169
|
+
rather than a stage it has not reached yet.** A CLI half was built and dropped
|
|
170
|
+
in the same session — a palette module plus a `--help` banner — and the
|
|
171
|
+
argument that closed it is filed under *Considered and rejected*.
|
|
172
|
+
|
|
173
|
+
### Changed
|
|
174
|
+
|
|
175
|
+
- **Three deprecated call sites retired, found by turning
|
|
176
|
+
`@typescript-eslint/no-deprecated` on the whole typed tree rather than by
|
|
177
|
+
reading the diff.** Neither was reachable by the repo's standing gates: both
|
|
178
|
+
compiled, linted and ran green, because a deprecation is a JSDoc tag and
|
|
179
|
+
nothing in `eslint.config.js` was asking for it.
|
|
180
|
+
|
|
181
|
+
`src/core/schema.ts` passed `{ message: … }` to the `statement` refinement.
|
|
182
|
+
Zod 4 deprecated that parameter in favour of `error`, and the rest of the file
|
|
183
|
+
had already moved — `paramValue`, `RegistryRecord` and `RegistrySchema` all
|
|
184
|
+
spell it `error` — so this was the one survivor of an incomplete migration,
|
|
185
|
+
not a considered exception. The wording is unchanged, and by the
|
|
186
|
+
`Issue.message` rule it is prose either way, so nothing observable moved:
|
|
187
|
+
parsed against the built schema, the refusal still comes back with the same
|
|
188
|
+
`message` under the same `custom` code. Design §5.1 quotes this schema in both
|
|
189
|
+
languages and carries the same edit.
|
|
190
|
+
|
|
191
|
+
`tests/registry-types.spec-d.ts` used `expectTypeOf(…).toMatchTypeOf<…>()`,
|
|
192
|
+
deprecated in expect-type 1.2.0 and split into two successors. Both sites
|
|
193
|
+
assert *assignability to a supertype* — `Registry` for the whole registry,
|
|
194
|
+
`Requirement` for one entry — which is `toExtend`, the successor that keeps
|
|
195
|
+
the `extends` relation. `toMatchObjectType` is the other one and would have
|
|
196
|
+
been the wrong reading: it checks a strict subset of keys, which is a
|
|
197
|
+
different claim than the one these two tests were making.
|
|
198
|
+
|
|
199
|
+
`eslint.config.js` called `tseslint.config()`, deprecated in
|
|
200
|
+
typescript-eslint 8.65 now that ESLint core provides the same `extends`
|
|
201
|
+
flattening via `defineConfig()`. Swapped for `defineConfig` + `globalIgnores`
|
|
202
|
+
from `eslint/config`. Because both spellings lint clean, *clean was not
|
|
203
|
+
evidence*: equivalence was established by diffing `eslint --print-config` for
|
|
204
|
+
a file in each of the five trees across the swap — byte-identical — plus the
|
|
205
|
+
linted file set and problem count, 166 and 0 on both sides.
|
|
206
|
+
|
|
207
|
+
**The third one is the interesting one, because the first sweep structurally
|
|
208
|
+
could not see it.** `no-deprecated` is type-aware, so it only reaches files
|
|
209
|
+
some project includes — and `tsconfig.typecheck.json` includes `src`, `tests`,
|
|
210
|
+
`self` and `examples`, which leaves `eslint.config.js`, `bin/attest.js` and
|
|
211
|
+
the three `vitest*.config.ts` named by no `include` in the tree. This is the
|
|
212
|
+
same blind spot `tests/import-boundary.spec.ts` was written about from the
|
|
213
|
+
other direction: the root config files are read *first* by the tooling and
|
|
214
|
+
belong to no project last. Re-run against a throwaway project that includes
|
|
215
|
+
them, the remaining four are clean — and that result was itself checked
|
|
216
|
+
against the failure shape this repo keeps meeting, by re-running the scan on
|
|
217
|
+
the pre-fix `eslint.config.js` and confirming it goes red, since a scan
|
|
218
|
+
matching no files is also silent.
|
|
219
|
+
|
|
220
|
+
With that gap closed — and the installed dependency tree carrying no registry
|
|
221
|
+
deprecation, the adopter-facing samples inside `skill.ts` and every code fence
|
|
222
|
+
in `docs/` and the README checked against the deprecated identifiers harvested
|
|
223
|
+
from the dependency typings, `fixtures/**` scanned under a throwaway project,
|
|
224
|
+
and the suite run under `--pending-deprecation` surfacing no runtime `DEP`
|
|
225
|
+
warning — this is the whole inventory as of Zod 4.4, expect-type 1.4,
|
|
226
|
+
typescript-eslint 8.65, Vitest 4.1, TypeScript 5.9 and Node 24.
|
|
227
|
+
|
|
228
|
+
**Two halves of that are unmeasured rather than clean, and are named here
|
|
229
|
+
rather than left to be inferred from the version list.** `package.json`
|
|
230
|
+
declares `typescript` as `^5.5.0 || ^6.0.0` while 5.9.3 is what is installed,
|
|
231
|
+
so anything TypeScript 6 deprecates has not been looked at; and `engines`
|
|
232
|
+
allows Node 20.19 while the sweep ran on 24, so a `DEP` warning that fires
|
|
233
|
+
only on an older runtime would not have appeared. Nothing gates any of it
|
|
234
|
+
going forward — see *Under consideration*.
|
|
235
|
+
|
|
93
236
|
## [1.1.0] - 2026-09-02
|
|
94
237
|
|
|
95
238
|
### Added
|
|
@@ -7098,6 +7241,15 @@ have to gather again.
|
|
|
7098
7241
|
`intent-quality.md`, the four things no gate is looking at already being that
|
|
7099
7242
|
page's subject and this being a fifth of the same kind.
|
|
7100
7243
|
|
|
7244
|
+
**The cheap half is written under `[Unreleased]`, and the decision above is
|
|
7245
|
+
unchanged by it.** `docs/{en,zh}/intent-quality.md` now carries a seventh
|
|
7246
|
+
finding: a param no statement interpolates changes with nothing outside the
|
|
7247
|
+
line to show it, so the discipline that catches an unintended move there is a
|
|
7248
|
+
human reading the registry diff. That costs no engine surface and settles
|
|
7249
|
+
nothing this entry is still waiting on — the WARNING in blocker **(2)** is
|
|
7250
|
+
still a rule with no measured population, and this is the fallback the entry
|
|
7251
|
+
itself named for exactly that state, not a smaller version of the rule.
|
|
7252
|
+
|
|
7101
7253
|
- **The intent layer is separable from the repository it describes, and the
|
|
7102
7254
|
verification layer is not — so a third party can audit intent and never
|
|
7103
7255
|
obtain a verdict.** Filed 2026-08-22 from a design evaluation of using Attest
|
|
@@ -7399,47 +7551,6 @@ have to gather again.
|
|
|
7399
7551
|
cost somebody a round trip. Without the first, this trades a derivable field for
|
|
7400
7552
|
an underivable duplicate.
|
|
7401
7553
|
|
|
7402
|
-
- **A Stop hook that will not let an agent end a turn while `check self` is red.**
|
|
7403
|
-
From the same read: `nostics` ships `hooks/consume-diagnostic.mjs`, a Claude
|
|
7404
|
-
Code Stop hook that reads the log its dev-server collector writes and returns
|
|
7405
|
-
`{"decision": "block"}` with the diagnostic as the reason, so the agent keeps
|
|
7406
|
-
working rather than stopping on a red application. Three details there are the
|
|
7407
|
-
design and not the plumbing, and are the transferable part: it guards on
|
|
7408
|
-
`stop_hook_active` so a blocked stop cannot loop; it feeds back **one**
|
|
7409
|
-
diagnostic per turn rather than the whole log; and when it consumes one it drops
|
|
7410
|
-
every other entry sharing that code and file, so a fault at twenty call sites is
|
|
7411
|
-
one round trip and not twenty.
|
|
7412
|
-
*Why the shape fits this repository specifically.* `CLAUDE.md`'s "Before
|
|
7413
|
-
committing" and the whole of `pre-commit-check` are discipline: nothing runs
|
|
7414
|
-
`check self` unless a person or an agent remembers to, and the repeat-grouping
|
|
7415
|
-
that hook does by hand is something `formatIssues` already computes. `.claude/`
|
|
7416
|
-
here holds five skills, no hooks and no `settings.json`, so this would be the
|
|
7417
|
-
first of its kind rather than an extension of anything.
|
|
7418
|
-
*Measured cost, 2026-09-01.* `node bin/attest.js check self --json` runs in
|
|
7419
|
-
**1.04–1.57 s** over three consecutive runs; `status self --json` in 0.78 s.
|
|
7420
|
-
That is paid at the end of every turn, which is the number to weigh rather than
|
|
7421
|
-
the wall clock of a single invocation.
|
|
7422
|
-
*What blocks it is the stale-`dist/` trap, and a hook is the worst place to meet
|
|
7423
|
-
it.* `bin/attest.js` loads `dist/`, not `src/` — the Gotcha this repository
|
|
7424
|
-
already keeps — so a Stop hook reports on the last build. After a `src/` edit it
|
|
7425
|
-
blocks on a diagnostic the working tree has already fixed, or clears while the
|
|
7426
|
-
tree is broken, and in both directions it is a gate arguing about a state nobody
|
|
7427
|
-
is in. Either the hook builds first, adding seconds to every turn, or it runs the
|
|
7428
|
-
engine from source and stops being the thing an adopter could copy.
|
|
7429
|
-
*And the larger question it sits in front of.* If this is right here it is right
|
|
7430
|
-
for an adopter, which makes it a question about what `attest init` writes rather
|
|
7431
|
-
than about `.claude/` in this repository. A hook that can block an agent's stop
|
|
7432
|
-
is a much larger commitment than a document, and the rejection quoted in full
|
|
7433
|
-
inside the `init --check` entry in this section — the gate on the installed
|
|
7434
|
-
skill's freshness — turned on exactly the distinction it needs:
|
|
7435
|
-
a signal about work the reader actually did, against a chore the tool imposes. A
|
|
7436
|
-
block on `check` is the first kind, which is the argument for it, and that
|
|
7437
|
-
argument should be made explicitly rather than arrived at by shipping.
|
|
7438
|
-
*What would move it.* Running it here, unshipped and repo-local, for a few weeks,
|
|
7439
|
-
and counting how often it blocks on something that would otherwise have been
|
|
7440
|
-
committed. If that count is zero the discipline was already working and the hook
|
|
7441
|
-
is overhead; if it is not, the number is the case for `init` writing one.
|
|
7442
|
-
|
|
7443
7554
|
- **An outward-facing form of the judgement tables, so an adopter can run this
|
|
7444
7555
|
counter-pressure on their own registry.**
|
|
7445
7556
|
`tests/intent-rule-candidates.spec.ts` maintains four hand-judged tables over
|
|
@@ -7675,6 +7786,68 @@ it sat between 0.2.0 and 0.1.7 for two releases, where standing still meant
|
|
|
7675
7786
|
sinking one version deeper each time a release was cut above it, and a rejection
|
|
7676
7787
|
filed under a version reads as belonging to it.
|
|
7677
7788
|
|
|
7789
|
+
### A brand palette and banner in the CLI
|
|
7790
|
+
|
|
7791
|
+
Built, measured green on every gate, and dropped 2026-09-03 in the session that
|
|
7792
|
+
built it. Filed here rather than deleted, because the half that was wrong is not
|
|
7793
|
+
the half that looks wrong, and the next person to propose "let's put the brand
|
|
7794
|
+
colours in the CLI" should reach this before rebuilding it.
|
|
7795
|
+
|
|
7796
|
+
**What it was.** `src/cli/brand.ts`: one module holding the palette behind a
|
|
7797
|
+
semantic interface — `pass`, `drift`, `muted`, `note`, `id`, plus the three
|
|
7798
|
+
marks — with `cli/report.ts` and `cli/index.ts` rewritten onto it so `chalk` was
|
|
7799
|
+
imported in exactly one place. Attest Green for a passing verdict, Drift Orange
|
|
7800
|
+
for a WARNING, Slate for every secondary line, and a four-row ASCII mark above
|
|
7801
|
+
`--help` on a TTY.
|
|
7802
|
+
|
|
7803
|
+
**The deletion test passed, which is why this is worth recording.** Delete the
|
|
7804
|
+
module and the same seven colour decisions reappear across two files and thirty
|
|
7805
|
+
call sites, spelled slightly differently each time — that is not a pass-through,
|
|
7806
|
+
and it is the ordinary reason this repository consolidates something. The gates
|
|
7807
|
+
agreed: lint, both typechecks, the full suite, `check self`, `verify self` and
|
|
7808
|
+
`render --check` were all green, and `attest --help` through a pipe was
|
|
7809
|
+
byte-identical, so `self/cli-surface.spec.ts` saw nothing.
|
|
7810
|
+
|
|
7811
|
+
**What it could not answer is who the interface is for.** A module earns its
|
|
7812
|
+
keep by having callers who vary across it, and this one had two callers that
|
|
7813
|
+
never will: there is no second theme, no `--color` flag, no second renderer, and
|
|
7814
|
+
nothing in the roadmap proposes one. *One adapter is a hypothetical seam.* The
|
|
7815
|
+
identity was consolidated at a place where nothing varies, which buys a
|
|
7816
|
+
re-tunability nobody has asked to use, and every colour in it is invisible to
|
|
7817
|
+
every test in the tree — they strip SGR before asserting, correctly, because
|
|
7818
|
+
`Issue.message` is prose and so is its colour. So the module's whole surface is
|
|
7819
|
+
unassertable: **the interface is the test surface, and this interface had none.**
|
|
7820
|
+
|
|
7821
|
+
**Three findings survive the rejection, and they are the reason it is written
|
|
7822
|
+
out.** They cost a session to learn and none of them is recoverable from the
|
|
7823
|
+
diff, which no longer exists.
|
|
7824
|
+
|
|
7825
|
+
- *Frost (`#E6EAF2`) can never be written by a CLI.* The guide defines it as
|
|
7826
|
+
"text on Void", and a terminal's background belongs to the reader — Frost on a
|
|
7827
|
+
light profile is invisible. The terminal's own foreground already plays that
|
|
7828
|
+
role. The same argument retires Void, Surface and Paper: a CLI that paints its
|
|
7829
|
+
own ground fights the profile the reader picked.
|
|
7830
|
+
- *Drift Orange cannot become the ERROR colour.* The palette has no failure
|
|
7831
|
+
colour, and the guide makes Drift Orange's scarcity part of what the product
|
|
7832
|
+
means — it appears when something is wrong. Spending it on every ERROR spends
|
|
7833
|
+
the meaning. The terminal's red is not a placeholder for a brand colour; it is
|
|
7834
|
+
the right answer.
|
|
7835
|
+
- *A monospace mark must be ASCII, and the reason is width, not encoding.*
|
|
7836
|
+
`█`, `╱`, `╲` and `═` are all East Asian **ambiguous** width, so a CJK
|
|
7837
|
+
terminal — which is where this repository is read — renders them two cells
|
|
7838
|
+
wide, and mixing them with a one-cell `=` or space tears a multi-row figure
|
|
7839
|
+
apart. The CLI's existing `✓`, `✗` and `●` are ambiguous too and survive it
|
|
7840
|
+
only because each is alone on its line with nothing aligned under it. This
|
|
7841
|
+
generalises past branding: it applies to any table, tree or rule the CLI ever
|
|
7842
|
+
draws.
|
|
7843
|
+
|
|
7844
|
+
*One thing found on the way is not a branding decision and does not belong to
|
|
7845
|
+
this rejection*: `chalk.dim` is SGR 2, which some profiles render at 40% opacity
|
|
7846
|
+
and others ignore outright, so the same secondary line is either unreadable or
|
|
7847
|
+
undifferentiated depending on the terminal. That is a legibility defect in
|
|
7848
|
+
output that ships today, and it survives this entry as a candidate on its own
|
|
7849
|
+
terms rather than as a reason to bring the palette back.
|
|
7850
|
+
|
|
7678
7851
|
### A backlink index over `archive/`, recording which change introduced or renamed an id
|
|
7679
7852
|
|
|
7680
7853
|
Proposed from OpenViking's `.relations.json`, restated twice on measurement, and
|
|
@@ -9407,6 +9580,47 @@ declared prefix ownership recorded above in this section — rejected on there
|
|
|
9407
9580
|
being no adoption that needs it, which is the same condition this sentence
|
|
9408
9581
|
supposes — which addresses, rather than a scorer, which ranks.
|
|
9409
9582
|
|
|
9583
|
+
### A standing gate on deprecated API usage
|
|
9584
|
+
|
|
9585
|
+
Priced on 2026-09-02, on the sweep that retired three deprecated call sites
|
|
9586
|
+
under `[Unreleased]` — turning `@typescript-eslint/no-deprecated` on by hand
|
|
9587
|
+
over a dependency walk through Zod 3→4, Vitest 3→4 and typescript-eslint
|
|
9588
|
+
8.2→8.65, none of the three reachable by anything the repo runs on a commit.
|
|
9589
|
+
The obvious move was to promote the rule into `eslint.config.js` beside
|
|
9590
|
+
`no-unnecessary-condition`, itself promoted from `strictTypeChecked` on exactly
|
|
9591
|
+
this argument: it needs the checker, and it found real instances here.
|
|
9592
|
+
|
|
9593
|
+
*What blocked it was not the rule, it was the reach.* `no-deprecated` is
|
|
9594
|
+
type-aware, so it only sees files a project includes, and the one deprecated
|
|
9595
|
+
site with the widest blast radius — `tseslint.config()` in `eslint.config.js`
|
|
9596
|
+
itself — sits in the set no project includes, along with `bin/attest.js` and
|
|
9597
|
+
the three `vitest*.config.ts`. Turning the rule on as-is buys a gate that
|
|
9598
|
+
covers `src`, `tests`, `self` and `examples` and silently *not* the files the
|
|
9599
|
+
tooling reads first, which is the shape this repo has been burned by twice
|
|
9600
|
+
already: `tests/import-boundary.spec.ts` deliberately does not use `findFiles`
|
|
9601
|
+
for the same reason, and its instrument guards exist because a walk that
|
|
9602
|
+
quietly stops reading a tree passes empty. A gate whose coverage is
|
|
9603
|
+
believed-wider-than-it-is is the failure this file keeps recording. Closing the
|
|
9604
|
+
gap meant either growing `tsconfig.typecheck.json` over the root config files —
|
|
9605
|
+
pulling `allowJs` into the wider project for `eslint.config.js` and
|
|
9606
|
+
`bin/attest.js` — or a second small project just for them, a third tsconfig in
|
|
9607
|
+
a repo that deliberately has two and has a comment in each explaining why.
|
|
9608
|
+
|
|
9609
|
+
**Rejected on the denominator.** Three call sites across an entire dependency
|
|
9610
|
+
walk is too small to carry the cost of either tsconfig shape — permanent, on
|
|
9611
|
+
every file the wider project would newly admit — against a defect a hand sweep
|
|
9612
|
+
at upgrade time already catches. `pre-commit-check` §9 records that sweep: run
|
|
9613
|
+
`no-deprecated` against a throwaway project covering what
|
|
9614
|
+
`tsconfig.typecheck.json` does and the five files it does not, and confirm the
|
|
9615
|
+
scan is actually seeing them before trusting a clean result. The check still
|
|
9616
|
+
happens, on the cadence the defect actually arrives on, without a third
|
|
9617
|
+
tsconfig or `allowJs` admitted into a wider project.
|
|
9618
|
+
|
|
9619
|
+
*Worth reopening if the count moves* — a dependency bump that turns up more
|
|
9620
|
+
than a handful of deprecated sites in one sweep is the signal that a hand pass
|
|
9621
|
+
is no longer keeping up, and it is measured the same way this entry was: read
|
|
9622
|
+
the `no-deprecated` output before deciding whether it needs a permanent home.
|
|
9623
|
+
|
|
9410
9624
|
## How an entry is re-measured
|
|
9411
9625
|
|
|
9412
9626
|
What the three sections above have learned about themselves, kept apart from all
|
package/README.md
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://gitlab.com/Pseudorca/attest/-/raw/main/assets/brand/attest-logo-horizontal.svg">
|
|
4
|
+
<img src="https://gitlab.com/Pseudorca/attest/-/raw/main/assets/brand/attest-logo-horizontal-light.svg" alt="Attest" width="260">
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
2
7
|
|
|
3
8
|
A TDD-native spec framework. **Tests are the source of truth for verification;
|
|
4
9
|
ID-bound requirements are the source of truth for intent.** Attest binds the two
|
|
@@ -159,7 +164,7 @@ Every diagnostic carries a `code`, and every code has a section in
|
|
|
159
164
|
```
|
|
160
165
|
ERROR registry-not-static (requirements/upload.reqs.ts:5)
|
|
161
166
|
Value is not a literal.
|
|
162
|
-
→ https://gitlab.com/Pseudorca/attest/-/blob/v1.1.
|
|
167
|
+
→ https://gitlab.com/Pseudorca/attest/-/blob/v1.1.1/docs/en/troubleshooting.md#registry-not-static
|
|
163
168
|
```
|
|
164
169
|
|
|
165
170
|
The anchor **is** the code, so the link cannot point somewhere the section
|
package/dist/core/schema.js
CHANGED
|
@@ -42,7 +42,7 @@ export const RequirementSchema = z.object({
|
|
|
42
42
|
statement: z
|
|
43
43
|
.string()
|
|
44
44
|
.refine((s) => /\b(SHALL|MUST)\b/.test(s), {
|
|
45
|
-
|
|
45
|
+
error: 'statement must contain the RFC-2119 keyword SHALL or MUST',
|
|
46
46
|
}),
|
|
47
47
|
rationale: z.string().min(10, 'rationale must not be empty (the intent layer has to say why)'),
|
|
48
48
|
// A param is any JSON value. Lists (vendor blacklists, id sets) are the most
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@am_shork/attest",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "TDD-native spec framework: tests are the source of truth for verification, ID-bound requirements the source of truth for intent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.28.0",
|
|
@@ -64,9 +64,12 @@
|
|
|
64
64
|
"keywords": [
|
|
65
65
|
"tdd",
|
|
66
66
|
"spec",
|
|
67
|
-
"requirements",
|
|
68
67
|
"drift",
|
|
69
|
-
"
|
|
68
|
+
"requirements",
|
|
69
|
+
"traceability",
|
|
70
|
+
"requirements-traceability",
|
|
71
|
+
"vitest",
|
|
72
|
+
"typescript"
|
|
70
73
|
],
|
|
71
74
|
"license": "MIT",
|
|
72
75
|
"dependencies": {
|