From 21a9984814f8f41988b85587fd52165732ae5d2d Mon Sep 17 00:00:00 2001 From: Antony Rizzitelli Date: Sun, 8 Nov 2026 20:45:51 +0100 Subject: [PATCH] build(deps): bump @antelopejs/tooling-configs to 0.4.0 --- .agents/skills/verify-interface/SKILL.md | 54 +++++++++++-------- .agents/skills/verify-interface/calls.ts | 54 +++++++++++++++++++ .../skills/verify-interface/verify.config.ts | 45 ++-------------- .github/workflows/ci.yml | 9 ---- AGENTS.md | 3 ++ package.json | 4 +- pnpm-lock.yaml | 13 ++--- 7 files changed, 102 insertions(+), 80 deletions(-) create mode 100644 .agents/skills/verify-interface/calls.ts diff --git a/.agents/skills/verify-interface/SKILL.md b/.agents/skills/verify-interface/SKILL.md index c1990f9..2185ad8 100644 --- a/.agents/skills/verify-interface/SKILL.md +++ b/.agents/skills/verify-interface/SKILL.md @@ -12,7 +12,7 @@ The user of an interface is a module author: one module implements it, others im ## Launch ```bash -pnpm exec antelopejs-verify new --base origin/main --title "" +node_modules/.bin/antelopejs-verify new --base origin/main --title "" ``` `new` prints the run folder; pass it as `` below. `new` runs the setup (`pnpm run build` by default), then packs the interface with `pnpm pack` and extracts it, so journeys load what npm would publish, `files` and `exports` included. Every `services` entry of `verify.config.ts` then starts in its own Docker container on a free loopback port, as for a module. Every journey gets its own `HOME` and scratch folder, `CI=1`, `AJS_NO_UPDATE_CHECK=1`, `NO_COLOR=1` and a free port from the kernel, so two runs never share an instance. Run it from the repository root after installing dependencies. @@ -20,7 +20,7 @@ pnpm exec antelopejs-verify new --base origin/main --title "" ## Doctor ```bash -pnpm exec antelopejs-verify doctor +node_modules/.bin/antelopejs-verify doctor ``` Doctor checks that HEAD has not moved since `new`, `ajs` comes from the repository's `@antelopejs/core`, the build is newer than `src/`, what `new` prepared is there, and no process the run recorded is still alive. Do not drive a run that fails doctor. @@ -28,11 +28,22 @@ Doctor checks that HEAD has not moved since `new`, `ajs` comes from the reposito ## Drive ```bash -pnpm exec antelopejs-verify plan --base origin/main -pnpm exec antelopejs-verify journey [journey...] +node_modules/.bin/antelopejs-verify plan --base origin/main +node_modules/.bin/antelopejs-verify journey --plan ``` -`plan` maps the diff to features and lists unmapped surfaces. `journey` runs a feature's journeys, or the ones named. The pack ships these journeys for this kind; a feature file names them next to the repository's own: +`plan` maps the diff to features and lists unmapped surfaces. `journey --plan` runs every feature `plan` lists as `run` for the run's base, `smoke` first, in one call: it prints each feature's lines in turn, keeps going after a feature fails, and exits 1 when any journey did not pass. Run it once instead of one call per feature. + +Features run one after another by default. When verify.config.ts declares no `services`, use `--jobs 4` when the machine is otherwise idle (load below the core count); otherwise run sequentially. `--jobs` runs up to that many features at once, each in a process of its own, capped at half the machine's cores; it finishes sooner but costs about twice the CPU time. The output, the evidence and the exit code are those of the sequential call, and a feature that lists a journey an earlier feature lists waits for it, so it still reuses that PASS. A feature whose process dies or leaves an uncaught error is named on stderr, and the others still run. On an interrupt, a feature's process that has not stopped 5 s later, or at a second interrupt, is killed with the apps it started; the call prints every feature that ended and names the rest in `not run: `. `--jobs` above 1 is refused when verify.config.ts declares `services`, because each journey resets them as it starts, which would wipe the data of a journey running beside it. Without `--jobs`, a journey that leaves an uncaught error behind ends the call and the line `not run: ` names what to rerun. + +To rerun after a fix, run one feature, or only the journeys you name, or several features by name, with `--jobs` too: + +```bash +node_modules/.bin/antelopejs-verify journey [journey...] +node_modules/.bin/antelopejs-verify journey --features , [--jobs 4] +``` + +The pack ships these journeys for this kind; a feature file names them next to the repository's own: - `every-function`: a fixture project loads the packed interface with a stub provider and a consumer module. The consumer walks the loaded interface, and every function the `.d.ts` files declare is called through core: - an async proxy answers the stub's echo, and the stub's call log shows the same arguments (the second view); @@ -44,22 +55,23 @@ pnpm exec antelopejs-verify journey [journey...] ## Evidence -Each step writes `/evidence////` with `command.txt`, `stdout.txt`, `stderr.txt`, `exit-code.txt`, `pid.txt` for background processes, `tree.txt`, `http.json` for every request, and `checks.json`. `result.json` holds a journey's result and `feature.json` the merged results of the feature. Read the evidence before you judge a PASS: a check proves only what it asserts. +Each step writes `/evidence////` with `command.txt`, `stdout.txt`, `stderr.txt`, `exit-code.txt`, `pid.txt` for background processes, `tree.txt`, `http.json` for every request, and `checks.json`. `result.json` holds a journey's result and `feature.json` the merged results of the feature. A journey id listed under two features runs once per run: when a feature runs without named ids, from `--plan`, `--features` or `journey `, and another feature of the run already passed that id at the same HEAD, the feature records a result with `reusedFrom` naming that evidence folder and runs nothing. Name the id to run it again. Read the evidence before you judge a PASS: a check proves only what it asserts. ## Cleanup ```bash -pnpm exec antelopejs-verify clean +node_modules/.bin/antelopejs-verify clean ``` -Each journey deletes its scratch folder and `HOME`, its install included, when it ends: passed, failed or interrupted. Only its evidence stays. `clean` stops what the run started, by recorded PID and container name, and removes everything but `evidence/` and `run.json`. Run it after every run, failed ones included. +Each journey deletes its scratch folder and `HOME`, its install included, when it ends: passed, failed or interrupted. Only its evidence stays. `clean` stops what the run started, by recorded PID and container name, and removes everything but `evidence/` and `run.json`, the folders `context.prepared` kept included. Run it after every run, failed ones included. ## The repository's own layer - `verify.config.ts` default-exports a `VerifyConfig` from `@antelopejs/tooling-configs/verify`: - `doors`: one entry per door kind (`data`, `format`, `contract`, `outbound`, `exposure`, `publish`), each `null` when it cannot apply or `{ globs, funnels, why }`. - `journeys`: the repository's journeys by id, each `{ title, entryPoint, run(context) }`, using the check helpers the same import exports. A check states what the product must do; an environment that went away is never a failing check. When a journey fails, the harness reruns each service's readiness command, up to four times over about 3.5 s, and looks at every `ajs project dev` the journey started, its own readiness probe included. The journey ends NOT VERIFIED, not FAIL, when a service still fails every rerun, an app was killed by SIGKILL, SIGTERM, SIGHUP or SIGINT that the harness did not send, or the app the harness started failed to bind the port the harness gave it (`EADDRINUSE` naming that port). An app that exits on its own with an exit code, one that crashes by another signal such as SIGABRT or SIGSEGV, and one that logs `EADDRINUSE` for any other port are the product's failure. A change that takes its own service down still reads as an outage: the service stays down, so the journey is NOT VERIFIED. For anything else the journey depends on, such as a process it started with `context.command` or an outside endpoint, probe it when a check fails and call `context.notVerified(reason)` when it no longer answers. - - `env`, `setup` (commands replacing the build), `userFacing` (globs replacing the kind's default) and `formal` (formal checks: `name`, `covers`, `command`, `violation`, and optionally `behaviour` and `baseCommand`). + - `context.prepared(key, build)` does the preparation many journeys repeat once per run, such as scaffolding and installing a project, and returns the journey's own copy at `/`. The first journey that asks for `key` runs `build(folder)` on the empty folder with its usual steps, after a step `prepared ` that marks them as the build and says whether it was kept. A build is kept until `clean` only when it returns, records at least one check, passes every check, and leaves only files, directories and symlinks in the folder. Every later journey of the run, also in a later `journey` command, gets a fresh copy and one step, `prepared `, that names the journey that built it and holds that build's evidence under `build/`. A build that is not kept is built again by the next journey, and rerunning the journey that built builds again. An absolute symlink into the folder points into each copy; other symlinks keep their target. `build` writes only inside the folder it is given: a write elsewhere, such as a sibling under `` or the repository, stays with the journey that built. Two concurrent `journey` commands that reach the same key at once both build, and the first to finish is kept. The commands `build` runs get a `HOME` of their own that is deleted when it ends: redo after `prepared` anything a later step needs in `HOME`, such as `ajs config set git`. Pass the same `build` for a key in every journey. + - `env`, `setup` (commands replacing the build), `userFacing` and `shared` (globs replacing the kind's default; `shared` lists the files whose change runs every feature), and `formal` (formal checks: `name`, `covers`, `command`, `violation` with an optional `states` group for the counterexample length, and optionally `behaviour` and `baseCommand`). - `services`: image, port, readiness command and a `reset` command run before every journey, as for a module. `new` starts them in Docker on free ports; a journey reads them with `context.service(name)` and `context.native(...)`. - `interface.provide(api, services)` runs in the stub provider after the generic stubs attach, for example to hand a client pointed at `services` to `internal.SetClient`. - `interface.calls[":"]` calls a plain exported function from the consumer and states the value it must return. @@ -68,24 +80,25 @@ Each journey deletes its scratch folder and `HOME`, its install included, when i - `features/.md`: frontmatter lists `covers:` (source globs), `docs:` (pages a verdict judges) and `journeys:`, then the sections Sub-features, How to get to it (user POV), Driving it with antelopejs-verify, and Gotchas. `features/smoke.md` covers nothing and runs on every verdict; here it runs `every-function`. ```bash -pnpm exec antelopejs-verify map +node_modules/.bin/antelopejs-verify map ``` `map` fails on a user-facing file no feature covers, a feature without journeys or docs, an unknown journey, and a `verify.config.ts` journey no feature names. ## Verdict rules -1. **Run every feature whose `covers:` globs the diff touches, plus `smoke`.** `plan` prints that list as `run`. +1. **Run every feature whose `covers:` globs the diff touches, plus `smoke`.** `plan` prints that list as `run`. When the diff changes a shared file (`package.json`, `pnpm-lock.yaml`, `pnpm-workspace.yaml`, `tsconfig.json`, `tsconfig.*.json`, `antelope-lock/**`; `shared` in `verify.config.ts` replaces this list; files under `.agents/`, `.claude/` and `.github/` never count), `run` is every feature, every feature's docs pages are judged, and `full` says why. Before a release, create the run with `new --all` and plan with `plan --all`: every feature runs, whatever the diff. 2. **An unmapped surface fails the verdict.** A changed user-facing file no feature covers needs a feature entry and its journeys in the same PR. 3. **Every registered journey of a feature in `run` must have run and passed.** Run whole features. A journey that could not reach the product, or whose environment went away during it, is NOT VERIFIED, and so is the verdict, whatever else passed: never PASS and never PASS+NOTES. Every journey result in the run's evidence counts, also for a feature outside `run`: a failed one fails the verdict, because it shows a `covers:` glob that misses what the diff broke. A NOT VERIFIED one outside `run` is listed under Not verified and caps the result at PASS+NOTES. A journey outside `run` that never ran is not required. -4. **Docs.** Judge every `docs:` page of a touched feature against what the journeys showed. A stale page fails the verdict. -5. **Formal checks.** `pnpm exec antelopejs-verify formal ` runs every formal check of `verify.config.ts` that covers the diff, or whose own `files` the diff changes, and records it under `evidence/formal//`. For each one it decides, without judgement, whether the diff changed what the covered code does. `emit` (the default) compares the structure of the covered files' JavaScript on the base and on HEAD: node kinds, names and literal values, leaving out types, comments, formatting, parentheses and `export` keywords. Each use of an imported binding stands for its origin (the resolved module and the imported name), so an import retargeted to another module or alias counts as changed; a function declaration refers to the top-level variables and classes of its own file by file and name, so moving it next to a different variable of the same name counts as changed. Exports are keyed by the origin they export, so a re-export or an `export { f as run }` retargeted to another binding counts as changed. Each file must keep its top-level statements and its side-effect imports in order; function declarations, which are hoisted, may move between covered files with their imports. So a type-only edit, a reformat or a moved function declaration counts as unchanged, while reordering statements or moving any other statement counts as changed. `emit` judges behaviour, not the public surface: exports are the api-diff's job. `probe` runs the check's `behaviour.command` with a worktree of each commit and compares what it prints. Then: +4. **Docs.** Judge every page `plan` lists as `docs` against what the journeys showed: the `docs:` pages of the touched features, or of every feature when the full set runs. A stale page fails the verdict. +5. **Formal checks.** `node_modules/.bin/antelopejs-verify formal ` runs every formal check of `verify.config.ts` that covers the diff, or whose own `files` the diff changes, and records it under `evidence/formal//`. For each one it decides, without judgement, whether the diff changed what the covered code does. `emit` (the default) compares the structure of the covered files' JavaScript on the base and on HEAD: node kinds, names and literal values, leaving out types, comments, formatting, parentheses and `export` keywords. Each use of an imported binding stands for its origin (the resolved module and the imported name), so an import retargeted to another module or alias counts as changed; a function declaration refers to the top-level variables and classes of its own file by file and name, so moving it next to a different variable of the same name counts as changed. Exports are keyed by the origin they export, so a re-export or an `export { f as run }` retargeted to another binding counts as changed. Each file must keep its top-level statements and its side-effect imports in order; function declarations, which are hoisted, may move between covered files with their imports. So a type-only edit, a reformat or a moved function declaration counts as unchanged, while reordering statements or moving any other statement counts as changed. `emit` judges behaviour, not the public surface: exports are the api-diff's job. `probe` runs the check's `behaviour.command` with a worktree of each commit and compares what it prints. Then: - a covered check that did not run, or exits non-zero (a tool error, or a check that no longer matches its record), fails the verdict; - - recorded findings fail the verdict only when the covered behaviour changed; when it did not, the verdict reports them as known findings; - - when the covered behaviour changed, or the diff changed the check's own `files`, `formal` also runs the check on the base (`baseCommand` when set). A rule the base broke counts as fixed only when HEAD reports it passing (`passing`); a rule the base broke that HEAD does not report at all fails the verdict. A change that leaves no violated rule passes. + - when the covered behaviour did not change, the verdict reports recorded findings as known findings, except a rule HEAD breaks that the base run did not, which fails the verdict whatever the diff touched, the check's own `files` included; when the base run itself exited non-zero, HEAD's findings cannot be compared and fail the verdict, and so does a result whose check's own `files` changed with no base run recorded. A check the diff adds is the exception: when the base could not run it, the base's `verify.config.ts` never names it, none of its files is a git rename of a base file, and the covered behaviour is unchanged, its recorded findings pass as the record it brings. List those findings in the PR body so a reviewer sees what the record accepts. A new check on a diff that also changes the covered code fails with `new check with a covered-code change: land the check in its own PR first`: land the check alone, then the code change. A moved or renamed check is a changed check; + - when the covered behaviour changed, or the diff changed the check's own `files`, `formal` also runs the check on the base (`baseCommand` when set). A rule the base broke counts as fixed only when HEAD reports it passing (`passing`); a rule the base broke that HEAD does not report at all fails the verdict. A change that leaves no violated rule passes; + - when the covered behaviour changed, recorded findings fail the verdict unless the base run exited 0 and broke each of them with the same counterexample length (the `states` named group of `violation`, after the rule's group, in decimal digits; a `states` group that is the first group or captures anything else gives no length), and HEAD fixed every other rule the base broke; then the verdict reports them as the same known findings as the base, with the fixed ones. A new rule, a changed length, or a length either run did not print fails. - The decision never reads the check's output, so a model whose rule names stay the same still fails on a changed behaviour. It is conservative: a local rename or an extracted helper counts as changed, and code outside `covers` is invisible to it. `--all` also runs the checks that do not cover the diff, without gating. -6. **One-way doors.** The section comes right after the result line and is mandatory. `pnpm exec antelopejs-verify doors` prints the `One-way-door:` line of the diff. Every file the path rules flag needs a door row or a false positive with its reason. + Whether the behaviour changed never reads the check's output, so a model whose rule names stay the same still fails on a changed behaviour unless the rule above keeps its findings known. It is conservative: a local rename or an extracted helper counts as changed, and code outside `covers` is invisible to it. `--all` also runs the checks that do not cover the diff, without gating. +6. **One-way doors.** The section comes right after the result line and is mandatory. `node_modules/.bin/antelopejs-verify doors` prints the `One-way-door:` line of the diff. Every file the path rules flag needs a door row or a false positive with its reason. 7. **Result.** FAIL if any rule fails. NOT VERIFIED when any journey of a required feature is NOT VERIFIED, for example a SaaS module run without its sandbox keys, however many other journeys passed. PASS+NOTES when everything required ran and passed but the judgement lists something no journey reached or something the owner should look at. PASS otherwise. Write your judgement to `/evidence/judgement.json`: @@ -104,14 +117,13 @@ Write your judgement to `/evidence/judgement.json`: } ``` -A door row is `{ "kind", "location", "irreversible", "containment" }`, where `containment` says what limits the damage. Then render and publish: +A door row is `{ "kind", "location", "irreversible", "containment" }`, where `containment` says what limits the damage. Then render the verdict: ```bash -pnpm exec antelopejs-verify verdict --pr -pnpm exec antelopejs-verify publish --pr +node_modules/.bin/antelopejs-verify verdict --pr ``` -`verdict` writes `evidence/verdict.md` and exits 1 unless the result is PASS or PASS+NOTES. `publish` commits the evidence to the orphan branch `verify-evidence` under `pr-//` from a temporary worktree, posts the verdict as a PR comment, sets commit status `verify` on the head SHA (`success` for PASS and PASS+NOTES, `error` for NOT VERIFIED, `failure` for FAIL) and reads it back. It prints the error GitHub answers when the token cannot write statuses. A new push voids the status. Post a verdict only from a thread that did not write the change. +`verdict` writes `evidence/verdict.md` and exits 1 unless the result is PASS or PASS+NOTES. Report the verdict and what you saw in your own reply, from a thread that did not write the change. ## Trust diff --git a/.agents/skills/verify-interface/calls.ts b/.agents/skills/verify-interface/calls.ts new file mode 100644 index 0000000..0ce61d8 --- /dev/null +++ b/.agents/skills/verify-interface/calls.ts @@ -0,0 +1,54 @@ +import { equal } from "@antelopejs/tooling-configs/verify"; +import type { InterfaceHooks } from "@antelopejs/tooling-configs/verify"; + +import type * as AuthInterface from "../../../src/index.ts"; + +type PlainJson = Parameters[2]; + +/** Copies a value of the interface's JSON types into the pack's, dropping the `undefined` members the interface allows. */ +function plain(value: AuthInterface.JsonValue): PlainJson { + if (Array.isArray(value)) return value.map(plain); + if (!(value instanceof Object)) return value; + return Object.fromEntries(Object.entries(value).flatMap(([key, member]) => (member === undefined ? [] : [[key, plain(member)] as const]))); +} + +/** The plain functions the consumer calls through core, each with the value it must resolve to. */ +export const CALLS: NonNullable["calls"]> = { + ".:ValidateRaw": { + call: async (api) => plain(await api.ValidateRaw("tok", { maxAge: "1h" })), + expect: { stub: ".:internal.Verify", args: ["tok", { maxAge: "1h" }] }, + }, + ".:SignRaw": { call: (api) => api.SignRaw({ userId: 1 }, { expiresIn: "1h" }), expect: { stub: ".:internal.Sign", args: [{ userId: 1 }, { expiresIn: "1h" }] } }, + ".:SignServerResponse": { + call: async (api) => { + const headers: Record = {}; + // SAFETY: SignServerResponse only calls setHeader on the response. + const res = { setHeader: (name: string, value: string) => void (headers[name] = value) } as never; + const returned = await api.SignServerResponse(res, "payload", undefined, { httpOnly: true }); + return { headers, same: returned === res }; + }, + expect: { headers: { "Set-Cookie": "ANTELOPEJS_AUTH=[object Object]; httpOnly" }, same: true }, + }, + ".:CreateAuthDecorator": { call: async (api) => api.CreateAuthDecorator({})().length, expect: 3 }, + ".:Authentication": { call: async (api) => api.Authentication().length, expect: 3 }, + ".:internal.defaultSource": { + // SAFETY: defaultSource reads only the headers of the request. + call: async (api) => api.internal.defaultSource({ headers: { cookie: "ANTELOPEJS_AUTH=c" } } as never, {} as never) ?? null, + expect: "c", + }, + ".:internal.defaultAuthenticator": { + call: async (api) => plain(await api.internal.defaultAuthenticator("tok", {})), + expect: { stub: ".:internal.Verify", args: ["tok", {}] }, + }, + ".:internal.CheckAuthentication": { + call: (api) => + api.internal.CheckAuthentication( + // SAFETY: the source and the authenticator below never read the request. + {} as never, + // SAFETY: the source and the authenticator below never read the response. + {} as never, + { source: () => "t", authenticator: (data) => `verified:${data}`, authenticatorOptions: {}, validator: (claims) => `validated:${claims}` }, + ), + expect: "validated:verified:t", + }, +}; diff --git a/.agents/skills/verify-interface/verify.config.ts b/.agents/skills/verify-interface/verify.config.ts index 2b1c5c4..80756cc 100644 --- a/.agents/skills/verify-interface/verify.config.ts +++ b/.agents/skills/verify-interface/verify.config.ts @@ -1,8 +1,10 @@ import { equal } from "@antelopejs/tooling-configs/verify"; -import type { InterfaceHooks, Journey, VerifyConfig } from "@antelopejs/tooling-configs/verify"; +import type { Journey, VerifyConfig } from "@antelopejs/tooling-configs/verify"; import type * as AuthInterface from "../../../src/index.ts"; +import { CALLS } from "./calls.ts"; + /** The setup every scenario shares: the packed interface, a recording stand-in provider behind `internal.Sign` and `internal.Verify`, and small helpers. */ const SCENARIO_PRELUDE = ` const [dist, repo] = process.argv.slice(2); @@ -236,47 +238,6 @@ type _Authentication = Assert, [ type _Decorators = Assert, ReturnType>>; `; -/** The plain functions the consumer calls through core, each with the value it must resolve to. */ -const CALLS: NonNullable["calls"]> = { - ".:ValidateRaw": { - call: (api) => api.ValidateRaw("tok", { maxAge: "1h" }), - expect: { stub: ".:internal.Verify", args: ["tok", { maxAge: "1h" }] }, - }, - ".:SignRaw": { call: (api) => api.SignRaw({ userId: 1 }, { expiresIn: "1h" }), expect: { stub: ".:internal.Sign", args: [{ userId: 1 }, { expiresIn: "1h" }] } }, - ".:SignServerResponse": { - call: async (api) => { - const headers: Record = {}; - // SAFETY: SignServerResponse only calls setHeader on the response. - const res = { setHeader: (name: string, value: string) => void (headers[name] = value) } as never; - const returned = await api.SignServerResponse(res, "payload", undefined, { httpOnly: true }); - return { headers, same: returned === res }; - }, - expect: { headers: { "Set-Cookie": "ANTELOPEJS_AUTH=[object Object]; httpOnly" }, same: true }, - }, - ".:CreateAuthDecorator": { call: (api) => api.CreateAuthDecorator({})().length, expect: 3 }, - ".:Authentication": { call: (api) => api.Authentication().length, expect: 3 }, - ".:internal.defaultSource": { - // SAFETY: defaultSource reads only the headers of the request. - call: (api) => api.internal.defaultSource({ headers: { cookie: "ANTELOPEJS_AUTH=c" } } as never, {} as never) ?? null, - expect: "c", - }, - ".:internal.defaultAuthenticator": { - call: async (api) => api.internal.defaultAuthenticator("tok", {}), - expect: { stub: ".:internal.Verify", args: ["tok", {}] }, - }, - ".:internal.CheckAuthentication": { - call: (api) => - api.internal.CheckAuthentication( - // SAFETY: the source and the authenticator below never read the request. - {} as never, - // SAFETY: the source and the authenticator below never read the response. - {} as never, - { source: () => "t", authenticator: (data) => `verified:${data}`, authenticatorOptions: {}, validator: (claims) => `validated:${claims}` }, - ), - expect: "validated:verified:t", - }, -}; - const config: VerifyConfig = { doors: { data: null, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 01ad123..a3e6b96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,15 +25,6 @@ jobs: test-job: true permissions: contents: read - - checks: - if: always() - needs: [ci] - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') - run: exit 1 renovate-sync: if: >- github.event_name == 'pull_request' && startsWith(github.head_ref, 'renovate/') && github.event.pull_request.head.repo.full_name == github.repository diff --git a/AGENTS.md b/AGENTS.md index 228376b..3222cc0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -111,6 +111,9 @@ Verify a boundary against the imports a developer or agent would plausibly write | A CI gate reads `needs.` only of jobs in its `needs` | `antelopejs-sync --check` | | Entry-only rules exempt no tracked outsider | `antelopejs-sync --check` | | A DMS layer's `build/backend-boundary.ts` is the pack's plugin | `antelopejs-sync --check` | +| No `skipLibCheck` in the root tsconfig or one `typecheck` or `build` uses | Reported by `antelopejs-sync` (note only) | +| `build` skips the lib check only over what `typecheck` checks | Reported by `antelopejs-sync` (note only) | +| `release` runs `typecheck` | Reported by `antelopejs-sync` (note only) | | A test names the wrong implementation it catches | review; no tool | diff --git a/package.json b/package.json index 1a076ff..7083e7a 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "test": "pnpm run build && ajs module test .", "format:check": "oxfmt --check .", "knip": "knip", - "prepare": "if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1; then lefthook install; fi", + "prepare": "if [ -e .git ] && command -v git >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1 && GIT_DIR=.git GIT_WORK_TREE=. git rev-parse --git-dir >/dev/null 2>&1; then GIT_DIR=.git GIT_WORK_TREE=. lefthook install; fi", "checks": "antelopejs-checks", "size": "antelopejs-check-size", "sync": "antelopejs-sync", @@ -61,7 +61,7 @@ "@antelopejs/core": "^1.21.0", "@antelopejs/interface-api": "^0.2.0", "@antelopejs/interface-core": "^0.4.0", - "@antelopejs/tooling-configs": "^0.3.3", + "@antelopejs/tooling-configs": "^0.4.0", "@types/chai": "^4.3.20", "@types/mocha": "^10.0.10", "@types/node": "^22.19.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbbb2cb..c8e4243 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: ^0.4.0 version: 0.4.0 '@antelopejs/tooling-configs': - specifier: ^0.3.3 - version: 0.3.3(eslint-plugin-perfectionist@5.11.0(eslint@10.10.0(jiti@2.7.0))(typescript@5.9.3))(knip@6.34.0)(oxfmt@0.66.0)(oxlint@1.81.0(oxlint-tsgolint@7.0.2001)) + specifier: ^0.4.0 + version: 0.4.0(eslint-plugin-perfectionist@5.11.0(eslint@10.10.0(jiti@2.7.0))(typescript@5.9.3))(knip@6.34.0)(oxfmt@0.66.0)(oxlint@1.81.0(oxlint-tsgolint@7.0.2001)) '@types/chai': specifier: ^4.3.20 version: 4.3.20 @@ -90,8 +90,8 @@ packages: '@antelopejs/interface-core@0.4.0': resolution: {integrity: sha512-IPX6dtt3KHknclN4mLOYeRYL2Odz1Pts+dtaZul62NghsBUX/0OFHD4Qhoaf+Beva4hjInbaF918ajr4Z5HVWQ==} - '@antelopejs/tooling-configs@0.3.3': - resolution: {integrity: sha512-jQ09zavCF5MMQtfyd8YOqpoxV6l6bp3m9tA5iN0z0XhQpVHM5VWr53yEuQ6Mc+1tseBW85N94ruP4TEfiGg8iQ==} + '@antelopejs/tooling-configs@0.4.0': + resolution: {integrity: sha512-qmi9CD5OZkWgIFGYZTQ63eyFBMEqGfY8967smEKyQVM0WW1n7kybqeG0ycB0wP+zgT4/LHehF6h7byLvTMBE1A==} engines: {node: '>=22.18.0'} hasBin: true peerDependencies: @@ -99,7 +99,7 @@ packages: eslint-plugin-perfectionist: '>=5.0.0' knip: '>=6.0.0' mongodb-memory-server: '>=10.0.0' - oxfmt: ^0.66.0 + oxfmt: ^0.66.0 || ^0.77.0 oxlint: 1.81.0 pixelmatch: '>=7.0.0' playwright-core: '>=1.56.0' @@ -2653,10 +2653,11 @@ snapshots: dependencies: reflect-metadata: 0.2.2 - '@antelopejs/tooling-configs@0.3.3(eslint-plugin-perfectionist@5.11.0(eslint@10.10.0(jiti@2.7.0))(typescript@5.9.3))(knip@6.34.0)(oxfmt@0.66.0)(oxlint@1.81.0(oxlint-tsgolint@7.0.2001))': + '@antelopejs/tooling-configs@0.4.0(eslint-plugin-perfectionist@5.11.0(eslint@10.10.0(jiti@2.7.0))(typescript@5.9.3))(knip@6.34.0)(oxfmt@0.66.0)(oxlint@1.81.0(oxlint-tsgolint@7.0.2001))': dependencies: '@oxlint/plugins': 1.81.0 dts-bundle-generator: 9.5.1 + minimatch: 10.2.6 typescript: 5.9.3 yaml: 2.9.1 optionalDependencies: