From cc86a3277c23d7f60cbd32efc40982bd0831bef0 Mon Sep 17 00:00:00 2001 From: Antony Rizzitelli Date: Thu, 5 Nov 2026 21:52:10 +0000 Subject: [PATCH] chore(verify): remove seeded runs from the verification skill --- .agents/skills/technical-writing/SKILL.md | 108 ++++++++++++++++++ .agents/skills/unslop/SKILL.md | 62 ++++++++++ .agents/skills/verify-interface/SKILL.md | 32 ++---- .../verify-interface/features/README.md | 2 +- .../verify-interface/features/streams.md | 2 +- .../seeded-breaks/atomic-execution.patch | 13 --- .../seeded-breaks/atomic-requests.patch | 16 --- .../seeded-breaks/datums.patch | 13 --- .../seeded-breaks/duplicate-key.patch | 13 --- .../seeded-breaks/entry-points.patch | 15 --- .../seeded-breaks/execution.patch | 12 -- .../expressions-arithmetic.patch | 13 --- .../seeded-breaks/expressions-arrays.patch | 13 --- .../seeded-breaks/expressions-dates.patch | 13 --- .../seeded-breaks/expressions-logic.patch | 13 --- .../seeded-breaks/expressions-text.patch | 13 --- .../seeded-breaks/joins.patch | 13 --- .../seeded-breaks/kinds.patch | 11 -- .../seeded-breaks/schemas.patch | 12 -- .../seeded-breaks/smoke.patch | 22 ---- .../seeded-breaks/staged-query.patch | 9 -- .../seeded-breaks/streams.patch | 13 --- .../seeded-breaks/tables-read.patch | 13 --- .../seeded-breaks/tables-write.patch | 13 --- .../seeded-breaks/types.patch | 13 --- .../seeded-doors/contract.patch | 15 --- .../seeded-doors/format.patch | 26 ----- .../seeded-doors/publish.patch | 13 --- .agents/skills/verify-interface/tsconfig.json | 3 + .../skills/verify-interface/verify.config.ts | 3 - .claude/skills/technical-writing | 1 + .claude/skills/unslop | 1 + .github/workflows/ci.yml | 1 + AGENTS.md | 13 ++- guardrails.suppressions.json | 3 +- package.json | 2 +- pnpm-lock.yaml | 22 +++- 37 files changed, 219 insertions(+), 356 deletions(-) create mode 100644 .agents/skills/technical-writing/SKILL.md create mode 100644 .agents/skills/unslop/SKILL.md delete mode 100644 .agents/skills/verify-interface/seeded-breaks/atomic-execution.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/atomic-requests.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/datums.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/duplicate-key.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/entry-points.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/execution.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/expressions-arithmetic.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/expressions-arrays.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/expressions-dates.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/expressions-logic.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/expressions-text.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/joins.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/kinds.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/schemas.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/smoke.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/staged-query.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/streams.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/tables-read.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/tables-write.patch delete mode 100644 .agents/skills/verify-interface/seeded-breaks/types.patch delete mode 100644 .agents/skills/verify-interface/seeded-doors/contract.patch delete mode 100644 .agents/skills/verify-interface/seeded-doors/format.patch delete mode 100644 .agents/skills/verify-interface/seeded-doors/publish.patch create mode 120000 .claude/skills/technical-writing create mode 120000 .claude/skills/unslop diff --git a/.agents/skills/technical-writing/SKILL.md b/.agents/skills/technical-writing/SKILL.md new file mode 100644 index 0000000..9bc5642 --- /dev/null +++ b/.agents/skills/technical-writing/SKILL.md @@ -0,0 +1,108 @@ +--- +name: technical-writing +description: "Layered technical-writing standard: Diátaxis structure, Google developer style sentences, STE instruction rules, Global English syntax. Use for /technical-writing or when writing or reviewing docs, RFCs, readmes, PR descriptions, or commit messages." +disable-model-invocation: true +--- + +# Technical writing + +In Claude Code, read [the Claude Code adapter](../../docs/harness/claude-code.md) once per session before you follow this skill. It maps the Cursor names in pstack skills to Claude Code. + +Write so a tired engineer understands on the first read. Apply all four layers. + +Three rules sit above the layers: + +- **Cut every word that does no work.** If the sentence survives without a word, the word goes. "In order to" is "to". "It is important to note that" is nothing. +- **Use the short, everyday word.** "Use", not "utilize". "Help", not "facilitate". "Do", not "perform". A long word has to buy its length with precision. +- **When a rule makes a sentence worse, fix the sentence another way or leave it alone.** A sentence that follows every rule and sounds like a machine wrote it has failed. + +The codebase is the word list. Write the real symbol, file, flag, or command name, not a synonym or a description of it. + +Don't invent jargon. Use the words a developer would say out loud: "move", "delete", "a budget that only decreases", not "evacuate", "ratchet", or "endgame". A named pattern is fine when the doc says what it means the first time. Propose a new offender and its replacement as an addition to `unslop`'s abstract-metaphor rule in your reply, with the diff. Don't edit that skill. + +## Vary the rhythm + +A doc can obey every layer and still read machine-written: every sentence clipped short, no view anywhere, nothing specific. + +- Mix sentence lengths on purpose. Short sentences land a point. Longer ones carry a fact with its condition or consequence. +- One thought per sentence does not mean one length per sentence. Split the sentence that carries two thoughts. Keep the long sentence that carries one. +- Have a view where the mode allows it. Explanation weighs trade-offs, so say what you make of them instead of listing pros and cons. Reference stays dry. +- Be specific over sterile. Not "schema changes can cause issues" but "a column rename fails the build". + +## Pick the mode first (Diátaxis) + +One document, one mode. Two questions pick it: does the content inform action (doing) or understanding (thinking), and does it serve learning or work? + +- Action + learning: **tutorial**. +- Action + work: **how-to**. +- Understanding + work: **reference**. +- Understanding + learning: **explanation**. + +Use the compass on a whole document or on one sentence. + +**Tutorial: learning by doing.** The learner's success is your job, not theirs. Open by saying what the learner will build, not what they will "learn". Every step produces a visible result, early and often. Tell them what they should see: the expected output, the prompt change, the log line. Cut explanation to one clause and a link. Stay concrete. Write as "we", in commands: "First, do x. Now, do y." + +**How-to: steps to a goal.** Solve a problem a person has, not an operation the machine can perform. Assume competence and skip teaching. Action only: no digressions, no background, no completeness for its own sake. Link those instead. Allow forks and judgment: "If you want x, do y." Name the guide by the task: "How to calibrate the radar array", not "Radar array calibration". + +**Reference: facts for lookup.** Only describe. No instruction, no persuasion, no opinion. Be dry, complete, and sure. State facts, options, limits, and errors with no hedging. Mirror the structure of the thing described, so code and docs can be navigated together. Put material where readers expect it. Generate from code where possible. + +**Explanation: understanding and why.** One bounded topic, readable away from the product. Each title should tolerate an implicit "About..." in front. Anchor on a real why question. Give context: design decisions, history, constraints, alternatives. Opinion is allowed here and nowhere else. + +Don't mix modes: no reference tables inside a tutorial, no tutorial hand-holding inside reference, no arguing inside a how-to. Split and link instead. + +## Write sentences to the reader (Google developer style) + +- Talk to the reader as "you", in the present tense. "Will" only for things that genuinely happen later. +- Say who does what: "the compiler checks", not "is checked". Passive is fine only when the actor is unknown or beside the point. +- Write instructions as commands: "Click Submit." State facts plainly. Never "should be done". +- Put the condition before the instruction: "To delete the document, click Delete." +- Put the common case first. Exceptions after. +- Sound like a knowledgeable friend. No buzzwords, no figurative language, no "please" in instructions, and never "simply", "easy", or "quickly" in a procedure. If it were simple, the reader would not be here. +- Don't pre-announce ("we will soon support...") and don't start consecutive sentences with the same phrase. +- Link with words that say where the link goes: the page title or a short description. Never "click here". Prefer a sentence of context on the page over a link off it. +- Headings carry the point, not just the topic ("Pick the mode first", not "Modes"). Sentence case. A task heading is a bare verb phrase ("Create an instance"). A concept heading is a noun phrase. One h1 per page, no skipped levels. +- Numbered lists for sequences, bullets for everything else. Introduce a list with a complete sentence. Keep items parallel. +- Code goes in code font. UI elements go in bold. Use serial commas. Drop "etc." and say up front that a list is partial. + +## Make statements load one at a time (STE rules) + +- One instruction per sentence. One thought per sentence everywhere else. +- Split instructions longer than about 20 words and other sentences longer than about 25. +- Put the warning or condition before the step it guards: "If hot oil touches your skin, injuries can occur." +- Keep "the" and "a": "Remove backup file" reads two ways. "Remove the backup file" reads one. +- Give each word one meaning and one job, then keep it. If "check" means inspect, don't also use it for restrain. +- Pick one word per action and stick to it: "start", not "start" here and "initiate" there. +- Write procedures as direct commands, never as narration and never in the passive: "Install the component", not "the component must be installed". +- Avoid "-ing" words where you can. + +## Leave no sentence open to two readings (Global English) + +- Keep words like "only" and "not" next to the word they change: "only fails on growth" and "fails only on growth" say different things. +- Break up long noun strings: "the proto import budget check script" becomes "the script that checks the proto-import budget". +- Make every "it", "they", and "this" point at one obvious thing. Repeat the noun when in doubt. Never use "this" or "which" to point at a whole clause. +- Don't drop verbs: "Phase 1 moves the converters and Phase 2 the runtime" leaves Phase 2 without one. Give it one. +- Keep the small words that show structure, such as "that" in "Ensure that the switch is off". Never trade clarity for word count. +- Repeat the article in a series when it prevents a misread: "the client and the host", not "the client and host", when they are two things. +- Say which parts "and" or "or" joins when a sentence can group two ways. "Both...and", "either...or", and "if...then" are free disambiguators. +- Use periods, not semicolons. Replace an em dash with a new sentence. +- Make text in parentheses a full grammatical unit or its own sentence. Never form plurals with "(s)". +- No slashes: write "a, b, or both" instead of "a/b" or "and/or". +- Call each thing by one name, everywhere. A doc that says "the gate", "the ratchet", and "the budget check" for one thing teaches three things. Don't churn what didn't change. +- Skip idioms, colloquialisms, Latin abbreviations, and metaphors. + +## Voice and repo specifics + +- Apply the **unslop** skill to every doc this skill touches. That skill owns the slop-pattern catalog: AI vocabulary, filler, hedging, formatting tells. +- PR descriptions and commit messages are writing too. Every layer except Diátaxis applies to them. A PR body is a briefing that a reviewer can read in under a minute. Do not paste swarm logs, SHA lists, or metric tables. Link them. +- Product UI strings are not documentation. Use your product's copy guidelines for those. +- Write real paths. Make every count or tree claim true at the commit that lands it, and include the command that regenerates it. + +## Worked example + +Before: + +> Configuration of the proto import ratchet budget script parameters is performed via budget.json. Note that it's important to remember that running with --write, which updates the committed budget to reflect the current count, should only be done when lowering it. If exceeded, CI fails. + +After: + +> `budget.mjs` reads the committed budget from `budget.json` and counts the files that import protos. If the count exceeds the budget, CI fails. Run `budget.mjs --write` only to lower the budget. diff --git a/.agents/skills/unslop/SKILL.md b/.agents/skills/unslop/SKILL.md new file mode 100644 index 0000000..97f5b64 --- /dev/null +++ b/.agents/skills/unslop/SKILL.md @@ -0,0 +1,62 @@ +--- +name: unslop +description: Cut AI tells from any writing. +disable-model-invocation: true +--- + +# Unslop + +Edit text to remove AI patterns. Scan for the patterns below, then rewrite. Preserve meaning and match the intended tone. + +## Patterns to detect and fix + +Rule numbers are stable ids that other skills cite. A removed rule leaves a gap. + +### Content + +3. **Superficial -ing phrases.** "highlighting...", "ensuring...", "reflecting...", "showcasing...", "fostering...". Delete or expand with real sources. +5. **Vague attributions.** "Experts believe", "Industry reports suggest", "Some critics argue". Name the source or delete. + +### Language + +7. **AI vocabulary.** Additionally, crucial, delve, enduring, enhance, fostering, garner, interplay, intricate, landscape (abstract), pivotal, showcase, tapestry (abstract), testament, underscore, vibrant. Replace with plain words. +8. **Fancy ways to say "is".** "serves as", "stands as", "boasts", "features". Say "is" or "has". +9. **"Not just X, but Y."** State the point directly instead. +10. **Rule of three.** Don't force ideas into groups of three. Use the natural number. +11. **Synonym cycling.** Protagonist, main character, central figure, hero all in one paragraph. Pick one, repeat it. +12. **False ranges.** "from X to Y" where X and Y aren't on a meaningful scale. List topics directly. + +### Style + +13. **Em dash overuse.** Avoid em dashes entirely. Use periods or commas only (no parentheses, no en dashes, no hyphen-as-dash substitutes). If a thought needs separation, end the sentence or use a comma. +14. **Colon overuse.** Colons are fine before a list or example, not as mid-sentence connectors. "If you're coming from traditional automation: instead of registering event handlers, you describe conditions" adds nothing with the colon. Rewrite to let the point stand on its own without comparison framing. "Describing when the scheduler should fire works best as plain English." Same meaning, no crutch punctuation. +15. **Boldface overuse.** Don't bold every proper noun or acronym. +16. **Inline-header lists.** The tell is a bold label and colon that restates the line: "**Performance:** Performance improved...". Convert those to prose. A bold lead-in that ends in a period, names the item, and is followed by genuinely new detail ("**Schema in TypeScript.** Tables live in one file.") is fine, not a tell. +17. **Title case headings.** Use sentence case. +18. **Decorative emojis.** Remove from headings and bullets. +19. **Curly quotes.** Use straight quotes. + +### Communication artifacts + +20. **Chatbot phrases.** "I hope this helps!", "Let me know if...", "Of course!", "Certainly!", "Found the smoking gun!" Remove. +22. **Sycophantic tone.** "Great question! You're absolutely right!" Respond directly. + +### Filler + +23. **Filler phrases.** "In order to" becomes "To". "Due to the fact that" becomes "Because". Delete "It is important to note that". +24. **Excessive hedging.** "could potentially possibly be argued that it might" becomes "may". +25. **Generic conclusions.** "The future looks bright." State specific plans or facts. + +### Jargon + +26. **Abstract metaphor nouns.** Substrate (base), wedge, wedge in (add), vector (way, method), locus, vantage, nexus, primitive as a noun, harness as a metaphor, surface as in "API surface", bedrock, scaffolding as a metaphor, modality, paradigm, gold-plating (more than the job needs), ratchet as a metaphor (the mechanism's real name, or a limit that only tightens), evacuate for moving code (move out), endgame (the last phase), north star, flywheel. These read as technical but usually have a plainer concrete word. Pick the concrete word. + +### Plain speech + +27. **Say what it does, not how it feels.** "the database stays close at hand", "SQL you can read", "types that follow your schema" name a feeling. The fix names the mechanism or a number: "`.toSQL()` returns the exact string sent to the database", "a column rename fails the build". Ask what the sentence tells the reader to do or know, then write that. If you can't restate it as a concrete instruction, fact, or number, cut it. One more check: if the sentence could appear unchanged in another project's docs, it says nothing about this one. Cut it. +28. **Shorten or split dense sentences.** If the reader has to backtrack to parse a sentence, break it in two or drop clauses. One idea per sentence. +29. **Active voice.** Prefer it. Catch "is/are/was/were + past participle" and name the actor: "queries are validated" becomes "the compiler validates queries", "the file is parsed by the loader" becomes "the loader parses the file". Passive is fine only when the actor is unknown or genuinely doesn't matter. +30. **Cut adverbs, or use a stronger verb.** "runs quickly" becomes "is fast" or the number, and "significantly improves" becomes the measured delta. An adverb propping up a weak verb means the verb is wrong. +31. **Prefer the plain word.** "utilize" and "leverage" become "use", "facilitate" becomes "help", "numerous" becomes "many", "in the event that" becomes "if". The fancier synonym is rarely clearer. +32. **Mannered prose.** Metaphor or flourish where a literal phrase exists: aphorisms ("wire it or delete it"), rhetorical fragments for effect, personified code ("the plan holds it"), figurative verbs ("rides along", "stands on"), stock framing phrases. "A dial worth turning" becomes "a parameter worth varying". Say what you mean. Rule 26 covers the metaphor nouns. +33. **Over-compression.** Dropped articles, verbless fragments, symbol-speak, and abbreviations that make the reader decode instead of read. "Parser rejects bad date → exit 2, no write" becomes "The parser rejects a bad date, exits with code 2, and writes nothing." Write whole sentences with their articles and verbs, and spell out arrows and abbreviations. diff --git a/.agents/skills/verify-interface/SKILL.md b/.agents/skills/verify-interface/SKILL.md index 70db01a..bfefac4 100644 --- a/.agents/skills/verify-interface/SKILL.md +++ b/.agents/skills/verify-interface/SKILL.md @@ -1,6 +1,6 @@ --- name: verify-interface -description: "Drive this AntelopeJS interface the way a module author does: pack it, load it in `ajs project dev` next to a stub provider wired through ImplementInterface and a consumer, call every declared function through core, and check the minor-bump rule against the published version. Use to prove a change to the interface works before merge, to verify a PR, or to check that a seeded break is caught." +description: "Drive this AntelopeJS interface the way a module author does: pack it, load it in `ajs project dev` next to a stub provider wired through ImplementInterface and a consumer, call every declared function through core, and check the minor-bump rule against the published version. Use to prove a change to the interface works before merge, or to verify a PR." --- # Verify interface @@ -57,28 +57,27 @@ pnpm exec antelopejs-verify clean ## 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, containments, why }`. `containments` names the `feature/journey` journeys that contain a Data or Format door. Each must fail with that kind's seeded door applied and pass without it: write the journey so one of its checks reads the state the door corrupts. - - `journeys`: the repository's journeys by id, each `{ title, entryPoint, run(context) }`, using the check helpers the same import exports. + - `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`). - `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. - `interface.providerSide[":"]` names a function only providers call, with the reason. +- `features/README.md`: the index of the features; `map` does not read it as a feature. - `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`. -- `seeded-breaks/.patch`: one minimal product bug per feature that its journeys must catch. -- `seeded-doors/.patch`: one change per door kind that applies. A Data or Format door's patch must break what its containment journeys check. ```bash pnpm exec antelopejs-verify map ``` -`map` fails on a user-facing file no feature covers, a feature without journeys, docs or seeded break, an unknown journey, an applicable door kind without its seeded door, and a Data or Format door whose `containments` name no journey a feature registers. `containments` (below) then runs those journeys against the seeded doors. +`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`. -2. **An unmapped surface fails the verdict.** A changed user-facing file no feature covers needs a feature entry, its journeys and its seeded break 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 is NOT VERIFIED, and so is the verdict, whatever else passed: never PASS and never PASS+NOTES. +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: - 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; @@ -86,7 +85,7 @@ pnpm exec antelopejs-verify map - 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. 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. A Data or Format row names a `containments` journey that passed in this run, and that journey is proven by `pnpm exec antelopejs-verify containments`: it fails with the kind's seeded door applied. A containment that passes with its door contains nothing, so a Data or Format door without one that fails with its door is not contained. +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. 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`: @@ -105,7 +104,7 @@ Write your judgement to `/evidence/judgement.json`: } ``` -A door row is `{ "kind", "location", "irreversible", "containment", "containmentJourney" }`. Then render and publish: +A door row is `{ "kind", "location", "irreversible", "containment" }`, where `containment` says what limits the damage. Then render and publish: ```bash pnpm exec antelopejs-verify verdict --pr @@ -116,22 +115,11 @@ pnpm exec antelopejs-verify publish --pr ## Trust -The skill is not Trusted yet. It becomes Trusted when it catches every seeded break, flags every seeded door, fails every Data and Format containment with its door, passes a shadow run of 10 consecutive agent PRs, and its last maintain pass is newer than the last change to the repository or its `@antelopejs` dependencies and at most 30 days old. - -## Seeded breaks and doors - -```bash -pnpm exec antelopejs-verify seeded -pnpm exec antelopejs-verify seeded-doors -pnpm exec antelopejs-verify containments -``` - -`seeded` runs each feature's journeys in a scratch worktree of HEAD without the patch, as the control, and with `seeded-breaks/.patch` applied. It prints `CAUGHT` only when a journey that passes in the control fails one of its checks with the patch. Journeys failing in the control and failures without a failing check do not count. `seeded-doors` prints `FLAGGED` when the path rules flag each patch with its own kind. `containments` runs every Data and Format containment journey in a scratch worktree of HEAD without the door, as the control, and with `seeded-doors/.patch` applied. It prints `CONTAINED` only when the journey passes in the control and fails one of its checks with the door. `MISSED` means it passes with its door, `UNPROVEN` that it fails in the control or ends without a failing check, and `NO-DOOR` that the kind has no seeded door; the command exits 1 on anything but `CONTAINED`. +The skill is not Trusted yet. It becomes Trusted when it passes a shadow run of 10 consecutive agent PRs and its last maintain pass is newer than the last change to the repository or its `@antelopejs` dependencies and at most 30 days old. ## Gotchas - A function the consumer awaits forever, such as a getter no provider resolved, fails after 15 seconds with a `Timeout` value. - `minor-bump` needs the npm registry. Without it the journey is not verified, never passed. -- A containment journey of a Data or Format door must check what that door's seeded patch changes, for example the arguments the stub provider logged. `containments` reports one that still passes with its door as `MISSED`. - Runs live under the system temp folder (`TMPDIR`), and the shared caches under `~/.cache/antelopejs-verify`. - A repository that outgrows this skill runs `antelopejs-sync --verify custom` and keeps its own `.agents/skills/verify-/`. diff --git a/.agents/skills/verify-interface/features/README.md b/.agents/skills/verify-interface/features/README.md index f560c4d..ad8f807 100644 --- a/.agents/skills/verify-interface/features/README.md +++ b/.agents/skills/verify-interface/features/README.md @@ -1,6 +1,6 @@ # Feature map -One file per feature. `covers:` lists the source globs it owns, `docs:` the pages a verdict judges, `journeys:` the journeys it runs, and `seeded-breaks/.patch` the break its journeys must catch. `pnpm exec antelopejs-verify map` checks that every user-facing file is covered. +One file per feature. `covers:` lists the source globs it owns, `docs:` the pages a verdict judges, and `journeys:` the journeys it runs. `pnpm exec antelopejs-verify map` checks that every user-facing file is covered. | Feature | What the user does | Journeys | |---|---|---| diff --git a/.agents/skills/verify-interface/features/streams.md b/.agents/skills/verify-interface/features/streams.md index af85710..9c0cfbf 100644 --- a/.agents/skills/verify-interface/features/streams.md +++ b/.agents/skills/verify-interface/features/streams.md @@ -13,7 +13,7 @@ journeys: # Streams -A stream is mapped, filtered, plucked, ordered, sliced and aggregated. This feature holds the Format door of the stage list: `stream-shape` is its containment journey. +A stream is mapped, filtered, plucked, ordered, sliced and aggregated. This feature holds the Format door of the stage list. ## Sub-features diff --git a/.agents/skills/verify-interface/seeded-breaks/atomic-execution.patch b/.agents/skills/verify-interface/seeded-breaks/atomic-execution.patch deleted file mode 100644 index f7fa6f1..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/atomic-execution.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/atomic.ts b/src/atomic.ts -index 7f918d3..7c17805 100644 ---- a/src/atomic.ts -+++ b/src/atomic.ts -@@ -7,7 +7,7 @@ import { - } from "./staged-query/atomic"; - - function isOutcome(value: unknown): value is AtomicMutationOutcome { -- return value === "applied" || value === "not-applied" || value === "unknown"; -+ return value === "applied" || value === "not-applied" || value === "unknown" || typeof value === "number"; - } - - /** Signals an adapter that does not implement the atomic mutation contract. */ diff --git a/.agents/skills/verify-interface/seeded-breaks/atomic-requests.patch b/.agents/skills/verify-interface/seeded-breaks/atomic-requests.patch deleted file mode 100644 index 9c17493..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/atomic-requests.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/src/staged-query/atomic.ts b/src/staged-query/atomic.ts -index 76ad227..39bb134 100644 ---- a/src/staged-query/atomic.ts -+++ b/src/staged-query/atomic.ts -@@ -155,10 +155,7 @@ function validateEquality( - } - - function validateUpdate(request: AtomicUpdate, forbidden: string[]) { -- if ( -- !isToken(request.nextRevision) || -- request.nextRevision === request.expectedRevision -- ) { -+ if (!isToken(request.nextRevision)) { - throw new TypeError("Atomic mutation requires a changed revision token"); - } - if (!isRecord(request.patch)) { diff --git a/.agents/skills/verify-interface/seeded-breaks/datums.patch b/.agents/skills/verify-interface/seeded-breaks/datums.patch deleted file mode 100644 index e882e50..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/datums.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/datum.ts b/src/staged-query/datum.ts -index e97f890..8101f54 100644 ---- a/src/staged-query/datum.ts -+++ b/src/staged-query/datum.ts -@@ -46,7 +46,7 @@ export class Datum extends Query { - ? NonNullable[K] - : NonNullable[K]> | U - >, -- QueryStage("key", undefined, key, def), -+ QueryStage("key", undefined, key), - ); - } - diff --git a/.agents/skills/verify-interface/seeded-breaks/duplicate-key.patch b/.agents/skills/verify-interface/seeded-breaks/duplicate-key.patch deleted file mode 100644 index 2c609df..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/duplicate-key.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/errors.ts b/src/errors.ts -index 6c1e35c..d316b59 100644 ---- a/src/errors.ts -+++ b/src/errors.ts -@@ -21,7 +21,7 @@ export class DuplicateKeyError extends Error { - public readonly index: string, - public readonly fields: string[], - ) { -- super(`Duplicate key on index "${index}" (${fields.join(", ")})`); -+ super(`Duplicate key on index "${index}" (${fields.join("/")})`); - this.name = "DuplicateKeyError"; - } - } diff --git a/.agents/skills/verify-interface/seeded-breaks/entry-points.patch b/.agents/skills/verify-interface/seeded-breaks/entry-points.patch deleted file mode 100644 index 7f7cce9..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/entry-points.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/package.json b/package.json -index 982e937..8123842 100644 ---- a/package.json -+++ b/package.json -@@ -85,10 +85,6 @@ - "types": "./dist/selection.d.ts", - "default": "./dist/selection.js" - }, -- "./stream": { -- "types": "./dist/stream.d.ts", -- "default": "./dist/stream.js" -- }, - "./staged-query": { - "types": "./dist/staged-query/index.d.ts", - "default": "./dist/staged-query/index.js" diff --git a/.agents/skills/verify-interface/seeded-breaks/execution.patch b/.agents/skills/verify-interface/seeded-breaks/execution.patch deleted file mode 100644 index 082305e..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/execution.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/src/query.ts b/src/query.ts -index 1ec74d0..527a2c0 100644 ---- a/src/query.ts -+++ b/src/query.ts -@@ -44,7 +44,6 @@ class IterableCursor implements AsyncGenerator { - this.resolve = undefined; - this.reject = undefined; - } -- await CloseCursor(this.reqId); - return res; - } - diff --git a/.agents/skills/verify-interface/seeded-breaks/expressions-arithmetic.patch b/.agents/skills/verify-interface/seeded-breaks/expressions-arithmetic.patch deleted file mode 100644 index c6e11f0..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/expressions-arithmetic.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/valueproxy.ts b/src/staged-query/valueproxy.ts -index cc8f071..7453861 100644 ---- a/src/staged-query/valueproxy.ts -+++ b/src/staged-query/valueproxy.ts -@@ -361,7 +361,7 @@ export class ValueProxy extends StagedObject { - * @returns A % B - */ - public mod(this: Is, other: ValueProxyOrValue) { -- return this.stage(ValueProxy, QueryStage("mod", undefined, other)); -+ return this.stage(ValueProxy, QueryStage("div", undefined, other)); - } - - /** diff --git a/.agents/skills/verify-interface/seeded-breaks/expressions-arrays.patch b/.agents/skills/verify-interface/seeded-breaks/expressions-arrays.patch deleted file mode 100644 index 54cb72e..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/expressions-arrays.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/valueproxy.ts b/src/staged-query/valueproxy.ts -index cc8f071..c225935 100644 ---- a/src/staged-query/valueproxy.ts -+++ b/src/staged-query/valueproxy.ts -@@ -724,7 +724,7 @@ export class ValueProxy extends StagedObject { - * @returns True if the array is empty - */ - public isempty(this: IsArray) { -- return this.stage(ValueProxy, QueryStage("arr_empty", undefined)); -+ return this.stage(ValueProxy, QueryStage("arr_count", undefined)); - } - - /** diff --git a/.agents/skills/verify-interface/seeded-breaks/expressions-dates.patch b/.agents/skills/verify-interface/seeded-breaks/expressions-dates.patch deleted file mode 100644 index 5b12896..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/expressions-dates.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/valueproxy.ts b/src/staged-query/valueproxy.ts -index cc8f071..403df98 100644 ---- a/src/staged-query/valueproxy.ts -+++ b/src/staged-query/valueproxy.ts -@@ -264,7 +264,7 @@ export class ValueProxy extends StagedObject { - ) { - return this.withTimezone(timezone).stage( - ValueProxy, -- QueryStage("date_dow", undefined), -+ QueryStage("date_doy", undefined), - ); - } - diff --git a/.agents/skills/verify-interface/seeded-breaks/expressions-logic.patch b/.agents/skills/verify-interface/seeded-breaks/expressions-logic.patch deleted file mode 100644 index 2699ed1..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/expressions-logic.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/valueproxy.ts b/src/staged-query/valueproxy.ts -index cc8f071..ef4fb2c 100644 ---- a/src/staged-query/valueproxy.ts -+++ b/src/staged-query/valueproxy.ts -@@ -511,7 +511,7 @@ export class ValueProxy extends StagedObject { - return ValueProxy.#stageOf( - this, - ValueProxy, -- QueryStage("cmp_ge", undefined, other), -+ QueryStage("cmp_gt", undefined, other), - ); - } - diff --git a/.agents/skills/verify-interface/seeded-breaks/expressions-text.patch b/.agents/skills/verify-interface/seeded-breaks/expressions-text.patch deleted file mode 100644 index 141930e..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/expressions-text.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/valueproxy.ts b/src/staged-query/valueproxy.ts -index cc8f071..310219d 100644 ---- a/src/staged-query/valueproxy.ts -+++ b/src/staged-query/valueproxy.ts -@@ -589,7 +589,7 @@ export class ValueProxy extends StagedObject { - * @returns New string - */ - public upcase(this: Is) { -- return this.stage(ValueProxy, QueryStage("str_upcase", undefined)); -+ return this.stage(ValueProxy, QueryStage("str_downcase", undefined)); - } - - /** diff --git a/.agents/skills/verify-interface/seeded-breaks/joins.patch b/.agents/skills/verify-interface/seeded-breaks/joins.patch deleted file mode 100644 index 4a86028..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/joins.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/stream.ts b/src/staged-query/stream.ts -index 94fc57e..27042f0 100644 ---- a/src/staged-query/stream.ts -+++ b/src/staged-query/stream.ts -@@ -171,7 +171,7 @@ export class Stream extends Query { - Stream>, - QueryStage( - "join", -- { innerOnly: true }, -+ { innerOnly: false }, - right, - this.callfunc(predicate, ValueProxy, ValueProxy), - this.callfunc(mapper, ValueProxy, ValueProxy), diff --git a/.agents/skills/verify-interface/seeded-breaks/kinds.patch b/.agents/skills/verify-interface/seeded-breaks/kinds.patch deleted file mode 100644 index 33cb102..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/kinds.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff --git a/src/staged-query/query.ts b/src/staged-query/query.ts -index 8ce897b..f726f26 100644 ---- a/src/staged-query/query.ts -+++ b/src/staged-query/query.ts -@@ -18,5 +18,5 @@ MarkKind(Query.prototype, QUERY_KIND); - * @returns True if the value is a query - */ - export function IsQuery(value: unknown): value is Query { -- return isKindHolder(value) && value[QUERY_KIND] === true; -+ return value instanceof Query; - } diff --git a/.agents/skills/verify-interface/seeded-breaks/schemas.patch b/.agents/skills/verify-interface/seeded-breaks/schemas.patch deleted file mode 100644 index f3da07f..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/schemas.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/src/schema.ts b/src/schema.ts -index ff34b36..c3f7abd 100644 ---- a/src/schema.ts -+++ b/src/schema.ts -@@ -26,7 +26,6 @@ export class Schema extends StagedSchema { - public constructor(id: string, definition: SchemaDefinition) { - super(id, definition); - Schema.defined.set(id, this); -- Schemas.register(id, definition); - } - } - diff --git a/.agents/skills/verify-interface/seeded-breaks/smoke.patch b/.agents/skills/verify-interface/seeded-breaks/smoke.patch deleted file mode 100644 index 2f50daf..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/smoke.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/src/query.ts b/src/query.ts -index 1ec74d0..dab1eb9 100644 ---- a/src/query.ts -+++ b/src/query.ts -@@ -4,7 +4,7 @@ import { StagedObject } from "./staged-query/common"; - import { Query as StagedQuery } from "./staged-query/query"; - - /** @internal */ --export const RunQuery = -+export const RunQueries = - InterfaceFunction<(query: StagedObject["stages"]) => any>(); - /** @internal */ - export const ReadCursor = -@@ -78,7 +78,7 @@ declare module "./staged-query/query" { - } - - function run(this: StagedQuery): Promise { -- return RunQuery(this.build()); -+ return RunQueries(this.build()); - } - - function then( diff --git a/.agents/skills/verify-interface/seeded-breaks/staged-query.patch b/.agents/skills/verify-interface/seeded-breaks/staged-query.patch deleted file mode 100644 index 34d1d6d..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/staged-query.patch +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/src/staged-query/index.ts b/src/staged-query/index.ts -index bdbc4fa..f785ef6 100644 ---- a/src/staged-query/index.ts -+++ b/src/staged-query/index.ts -@@ -1,3 +1,4 @@ -+import "@antelopejs/interface-core"; - export * from "./common"; - export * from "./atomic"; - export { Datum, IsDatum } from "./datum"; diff --git a/.agents/skills/verify-interface/seeded-breaks/streams.patch b/.agents/skills/verify-interface/seeded-breaks/streams.patch deleted file mode 100644 index a55a984..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/streams.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/stream.ts b/src/staged-query/stream.ts -index 94fc57e..c4a318c 100644 ---- a/src/staged-query/stream.ts -+++ b/src/staged-query/stream.ts -@@ -236,7 +236,7 @@ export class Stream extends Query { - * @returns New (sorted) stream - */ - public orderBy(index: string, direction?: "asc" | "desc") { -- return this.stage(undefined, QueryStage("orderBy", { index, direction })); -+ return this.stage(undefined, QueryStage("orderBy", { index, direction: direction ?? "desc" })); - } - - /** diff --git a/.agents/skills/verify-interface/seeded-breaks/tables-read.patch b/.agents/skills/verify-interface/seeded-breaks/tables-read.patch deleted file mode 100644 index e5f7daf..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/tables-read.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/selection.ts b/src/staged-query/selection.ts -index 12887b7..73a898f 100644 ---- a/src/staged-query/selection.ts -+++ b/src/staged-query/selection.ts -@@ -200,7 +200,7 @@ export class Table extends Selection { - ) { - return this.stage( - Selection, -- QueryStage("between", { index }, low, high), -+ QueryStage("between", { index }, high, low), - ); - } - } diff --git a/.agents/skills/verify-interface/seeded-breaks/tables-write.patch b/.agents/skills/verify-interface/seeded-breaks/tables-write.patch deleted file mode 100644 index d25f01d..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/tables-write.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/selection.ts b/src/staged-query/selection.ts -index 12887b7..614c643 100644 ---- a/src/staged-query/selection.ts -+++ b/src/staged-query/selection.ts -@@ -161,7 +161,7 @@ export class Table extends Selection { - obj: (DeepPartial & object) | (DeepPartial & object)[], - options?: InsertOptions, - ) { -- return this.stage(Query, QueryStage("insert", options, obj)); -+ return this.stage(Query, QueryStage("insert", undefined, obj)); - } - - /** diff --git a/.agents/skills/verify-interface/seeded-breaks/types.patch b/.agents/skills/verify-interface/seeded-breaks/types.patch deleted file mode 100644 index 7c4100f..0000000 --- a/.agents/skills/verify-interface/seeded-breaks/types.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/staged-query/common.ts b/src/staged-query/common.ts -index cffd95a..a9b8548 100644 ---- a/src/staged-query/common.ts -+++ b/src/staged-query/common.ts -@@ -71,7 +71,7 @@ export interface Changes { - } - - export interface InsertOptions { -- conflict?: "update" | "replace"; -+ conflict?: "update" | "replace" | "ignore"; - } - - function isStagedConstructor( diff --git a/.agents/skills/verify-interface/seeded-doors/contract.patch b/.agents/skills/verify-interface/seeded-doors/contract.patch deleted file mode 100644 index 6f205a3..0000000 --- a/.agents/skills/verify-interface/seeded-doors/contract.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/package.json b/package.json -index 982e937..8d4aeab 100644 ---- a/package.json -+++ b/package.json -@@ -61,10 +61,6 @@ - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, -- "./atomic": { -- "types": "./dist/atomic.d.ts", -- "default": "./dist/atomic.js" -- }, - "./common": { - "types": "./dist/common.d.ts", - "default": "./dist/common.js" diff --git a/.agents/skills/verify-interface/seeded-doors/format.patch b/.agents/skills/verify-interface/seeded-doors/format.patch deleted file mode 100644 index 819a662..0000000 --- a/.agents/skills/verify-interface/seeded-doors/format.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/src/staged-query/stages.ts b/src/staged-query/stages.ts -index fb8beb1..38d795d 100644 ---- a/src/staged-query/stages.ts -+++ b/src/staged-query/stages.ts -@@ -70,7 +70,7 @@ export interface QueryStageMap { - >; - default: Recorded]>; - map: Recorded; -- filter: Recorded; -+ where: Recorded; - pluck: Recorded; - without: Recorded; - union: Recorded]>; -diff --git a/src/staged-query/stream.ts b/src/staged-query/stream.ts -index 94fc57e..8a5ac62 100644 ---- a/src/staged-query/stream.ts -+++ b/src/staged-query/stream.ts -@@ -83,7 +83,7 @@ export class Stream extends Query { - public filter(predicate: (val: ValueProxy) => ValueProxyOrValue) { - return this.stage( - undefined, -- QueryStage("filter", undefined, this.callfunc(predicate, ValueProxy)), -+ QueryStage("where", undefined, this.callfunc(predicate, ValueProxy)), - ); - } - diff --git a/.agents/skills/verify-interface/seeded-doors/publish.patch b/.agents/skills/verify-interface/seeded-doors/publish.patch deleted file mode 100644 index 8b4ca16..0000000 --- a/.agents/skills/verify-interface/seeded-doors/publish.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/package.json b/package.json -index 982e937..0bafc89 100644 ---- a/package.json -+++ b/package.json -@@ -100,7 +100,7 @@ - "./package.json": "./package.json" - }, - "publishConfig": { -- "access": "public", -+ "access": "restricted", - "provenance": true - }, - "scripts": { diff --git a/.agents/skills/verify-interface/tsconfig.json b/.agents/skills/verify-interface/tsconfig.json index 8d4c934..e0afb34 100644 --- a/.agents/skills/verify-interface/tsconfig.json +++ b/.agents/skills/verify-interface/tsconfig.json @@ -2,6 +2,9 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "rootDir": "../../..", + "types": [ + "node" + ], "module": "nodenext", "moduleResolution": "nodenext", "allowImportingTsExtensions": true, diff --git a/.agents/skills/verify-interface/verify.config.ts b/.agents/skills/verify-interface/verify.config.ts index 506d0ff..84b9146 100644 --- a/.agents/skills/verify-interface/verify.config.ts +++ b/.agents/skills/verify-interface/verify.config.ts @@ -54,13 +54,11 @@ const config: VerifyConfig = { format: { globs: ["src/staged-query/**", "src/errors.ts"], funnels: [], - containments: ["streams/stream-shape"], why: "the stage names, options and arguments the builders record are what every database module decodes, and the kind markers recognise queries and errors across copies of the interface", }, contract: { globs: ["src/index.ts", "src/staged-query/index.ts", "package.json"], funnels: [], - containments: [], why: "the export map and the root entry are what every database module and every query-writing module imports", }, outbound: null, @@ -68,7 +66,6 @@ const config: VerifyConfig = { publish: { globs: ["package.json", ".release-it.json", "changelogen.config.json", ".github/workflows/release.yml"], funnels: [], - containments: [], why: "the package name and release workflow claim @antelopejs/interface-database on npm", }, }, diff --git a/.claude/skills/technical-writing b/.claude/skills/technical-writing new file mode 120000 index 0000000..bedd96f --- /dev/null +++ b/.claude/skills/technical-writing @@ -0,0 +1 @@ +../../.agents/skills/technical-writing \ No newline at end of file diff --git a/.claude/skills/unslop b/.claude/skills/unslop new file mode 120000 index 0000000..158c16b --- /dev/null +++ b/.claude/skills/unslop @@ -0,0 +1 @@ +../../.agents/skills/unslop \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ee1268..c611561 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ jobs: docs docs-typescript-project: docs/tsconfig.json renovate-sync: false + test-job: true permissions: contents: read diff --git a/AGENTS.md b/AGENTS.md index c0a162f..048a674 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,6 +27,14 @@ AQL, the AntelopeJS database query builder, published as `@antelopejs/interface- Finish every change with `pnpm run checks`. It runs typecheck, lint, format, knip, tests, size, suppressions, and sync, and prints only what failed. The change is done when it prints no failure. +### While you work + +`pnpm run checks` runs every step, so run it once, at the end. While you iterate, run only the check that covers what you changed: + +- `pnpm run typecheck` after you change types or signatures. +- `pnpm exec oxlint ` and `pnpm exec oxfmt ` on the files you changed. +- Run one test file instead of the whole suite. In a module that tests with `ajs module test`, run `pnpm run build`, then `pnpm exec ajs module test . -f dist/.test.js`. Repeat `-f` for more files. + Prove a change works with the verification skill in [`.agents/skills/verify-interface/SKILL.md`](.agents/skills/verify-interface/SKILL.md) before you call it done. The tools enforce most rules, listed in the table at the end. The sections below hold the rules no tool checks. @@ -63,7 +71,7 @@ Verify a boundary against the imports a developer or agent would plausibly write ### Suppression budget -`guardrails.suppressions.json` caps the suppressions in authored code: lint directives per rule they name (`*` for none), `@ts-expect-error`, `@ts-ignore` and `SAFETY:` comments, and under `config` each rule a lint config (`oxlint.config.*`, `.oxlintrc.json`, `eslint.config.*`) switches off or to a warning, keyed by rule and `files` glob anchored at the config's directory, so widening or moving a scope is a new ceiling of 0, while narrowing one takes over the room it gives up. Switching a rule off in a config is a suppression too: fix the code its rule reports instead. A suppression over its ceiling fails: fix the code, or remove another suppression of the same kind. A rule that keeps needing suppressions should be changed in the preset. Never raise or remove a number in that file: the check fails a ceiling higher than on the pull request base, and only the operator approves a raise. A directive disabling `anti-slop/require-safety-comment-for-type-assertion` is burn-down debt: remove the assertion, or, where it holds, replace the directive with `SAFETY: `, which takes over its room without a raise. After you remove suppressions, run `pnpm run suppressions:update` and commit the lowered file. +`guardrails.suppressions.json` caps the suppressions in authored code, the repository's own files under `.agents/`, `.claude/` and the other agent directories included (`agentFiles`; only what `antelopejs-sync` writes there is skipped): lint directives per rule they name (`*` for none), `@ts-expect-error`, `@ts-ignore` and `SAFETY:` comments, and under `config` each rule a lint config (`oxlint.config.*`, `.oxlintrc.json`, `eslint.config.*`) switches off or to a warning, keyed by rule and `files` glob anchored at the config's directory, so widening or moving a scope is a new ceiling of 0, while narrowing one takes over the room it gives up. Switching a rule off in a config is a suppression too: fix the code its rule reports instead. A suppression over its ceiling fails: fix the code, or remove another suppression of the same kind. A rule that keeps needing suppressions should be changed in the preset. Never raise or remove a number in that file: the check fails a ceiling higher than on the pull request base, and only the operator approves a raise. A directive disabling `anti-slop/require-safety-comment-for-type-assertion` is burn-down debt: remove the assertion, or, where it holds, replace the directive with `SAFETY: `, which takes over its room without a raise. After you remove suppressions, run `pnpm run suppressions:update` and commit the lowered file. ### What enforces what @@ -80,6 +88,7 @@ Verify a boundary against the imports a developer or agent would plausibly write | String-keyed object tables are read through own keys only | `antelopejs/no-inherited-key-lookup` | | A type guard checks each member it narrows to; keys with `Object.hasOwn` | `antelopejs/no-presence-only-type-guard` | | A type guard checks the nested elements and values it claims | review; no tool | +| Every file type-aware lint reads is listed by a tsconfig | `antelopejs/require-tsconfig-include` | | Params ≤ 5, function ≤ 120 lines, file ≤ 500 lines, depth ≤ 4, complexity ≤ 20 | `eslint/max-*`, `eslint/complexity` | | Cleanup preserves declarations, runtime exports and DMS auto-imports | `antelopejs-api-diff ` | | Comment-only changes preserve JavaScript emit | `antelopejs-comment-proof ` | @@ -92,7 +101,7 @@ Verify a boundary against the imports a developer or agent would plausibly write | No `@ts-ignore` | `typescript/ban-ts-comment` | | No once-used type parameter standing in for `unknown` | `typescript/no-unnecessary-type-parameters` | | No `any` passed, assigned or returned where a type is declared | `typescript/no-unsafe-{argument,assignment,return}` | -| Files ≤ 20,000 bytes, tests included | `antelopejs-check-size` (pre-commit) | +| Files ≤ 20,000 bytes, tests and agent files included | `antelopejs-check-size` (pre-commit) | | Suppressions stay within `guardrails.suppressions.json` | `antelopejs-suppressions` (pre-commit, checks) | | No lint config switches off a rule to stay under the budget | `antelopejs-suppressions` (pre-commit, checks) | | No added directive disables the `SAFETY:`, comment or test rules | `antelopejs-suppressions` (pre-commit, checks) | diff --git a/guardrails.suppressions.json b/guardrails.suppressions.json index a68d5ff..0819544 100644 --- a/guardrails.suppressions.json +++ b/guardrails.suppressions.json @@ -9,5 +9,6 @@ "tsExpectError": 2, "tsIgnore": 0, "safety": 5, - "config": {} + "config": {}, + "agentFiles": true } diff --git a/package.json b/package.json index 7141c5b..7aec840 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "@antelopejs/core": "^1.16.0", "@antelopejs/interface-core": ">=0.2.4 <1.0.0", "@antelopejs/mongodb": "1.6.0", - "@antelopejs/tooling-configs": "^0.2.17", + "@antelopejs/tooling-configs": "^0.3.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 6be0494..9b7615a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ importers: specifier: 1.6.0 version: 1.6.0(socks@2.8.7) '@antelopejs/tooling-configs': - specifier: ^0.2.17 - version: 0.2.17(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.3.0 + version: 0.3.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 @@ -99,16 +99,20 @@ packages: '@antelopejs/mongodb@1.6.0': resolution: {integrity: sha512-HKPw1FC+NDqHe0B7ceEUGpbH8oOg9AcdEhcBjSnBwrmf0Z0hWwn2T95p0hEcW3TunaFRoueAJsQjSdUqdckSFA==} - '@antelopejs/tooling-configs@0.2.17': - resolution: {integrity: sha512-8fwh7xQPzazP/l/ivEwRKpfN8qShV9+PRizpI9cBewe2TwoRPu4n95w6sYSlygiRmm0HxLJYZFhmSPRgtiNo1Q==} + '@antelopejs/tooling-configs@0.3.0': + resolution: {integrity: sha512-ZiYDycndvrYdySvUGdpBEZMbpau6QBMN0LYS9mAO2sLvVagwULl7Sel55vKMn9giHhh3YsHsAWWqB/+4mlNagw==} engines: {node: '>=22.18.0'} hasBin: true peerDependencies: '@nuxt/eslint-config': '>=1.2.0' eslint-plugin-perfectionist: '>=5.0.0' knip: '>=6.0.0' + mongodb-memory-server: '>=10.0.0' oxfmt: ^0.66.0 oxlint: 1.81.0 + pixelmatch: '>=7.0.0' + playwright-core: '>=1.56.0' + sharp: '>=0.34.0' peerDependenciesMeta: '@nuxt/eslint-config': optional: true @@ -116,10 +120,18 @@ packages: optional: true knip: optional: true + mongodb-memory-server: + optional: true oxfmt: optional: true oxlint: optional: true + pixelmatch: + optional: true + playwright-core: + optional: true + sharp: + optional: true '@cacheable/memory@2.2.0': resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} @@ -2854,7 +2866,7 @@ snapshots: - snappy - socks - '@antelopejs/tooling-configs@0.2.17(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.3.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