~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

Agents read the web, lexicon and query rules only where they work

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:docs/agents-split
crates/knotless-serve/AGENTS.mdnew+21−0
@@ -0,0 +1,21 @@
# Queries
This is part of the root `AGENTS.md`, loaded when work reaches `crates/knotless-serve/`.
A query of the index (`crates/knotless-serve/src/index.rs`) is planned
as the tests run it, against tables said to be prod's size
(`crates/knotless-serve/src/plans.rs`), and `test` fails on any that
scans a table that grows with the network: `forge`, `check_run`,
`comment` and the rest of `GROWING`, or sorts many of its rows (a range
leading its index, or what a search finds for each row of a scan, as a
repository's pushes for every repository). Tests on empty tables pass a query
that takes seconds on prod, so this is what holds them. Its log names
the query and its plan. Give it an index, or a query that uses the one
there: an expression index needs the expression as written, and a
column compared to it marked `+` (`json_extract(c.value, '$.pull') =
+p.uri`), as the column's affinity otherwise keeps the index unused.
A scan meant to be, of a few rows an operator reads now and then, says
so in its SQL with why, `-- scans: forge, as only the operator asks`,
where review sees it; a sort meant to be, `-- sorts: forge, …`. On prod,
a statement slower than 100 ms logs `sql`, with the request's trace. To see the plans while fixing one, run its tests
with `KNOTLESS_PLANS=<dir>` and `node harness/plans.mjs <dir>`.
crates/knotless-serve/CLAUDE.mdnew+1−0
@@ -0,0 +1 @@
@AGENTS.md
lexicons/AGENTS.mdnew+20−0
@@ -0,0 +1,20 @@
# Lexicons
This is part of the root `AGENTS.md`, loaded when work reaches `lexicons/`.
A new or changed schema under `lexicons/` follows the Lexicon Style Guide
(https://atproto.com/guides/lexinomicon) and passes `just lex`: goat's
linter, less two warnings kept on purpose (`pack`'s inline `bytes` and
`listReports`' `id`), then `harness/lexicons.mjs`. That checks what the
linter does not see: `knownValues` rather than `enum`; objects in new
arrays rather than bare values; open unions; a description on every
`main`; a `maxLength` on strings without a format in what people write
(records and procedure input); accounts by DID, not handle. And that every
schema only grows from main's, as the branch left main: a schema already
published is evolved, not changed, with new fields optional and nothing removed, renamed, retyped,
made required or narrowed. Fix anything either says, or say why not.
Still by hand: no `required` a reader could do without, since it can never
be dropped; a description on any field whose name does not say what it
holds; `maxGraphemes` beside `maxLength` for text people read; reuse of
`dev.knotless.defs` and `com.atproto.repo.strongRef` over new shapes.
lexicons/CLAUDE.mdnew+1−0
@@ -0,0 +1 @@
@AGENTS.md
web/AGENTS.mdnew+55−0
@@ -0,0 +1,55 @@
# Working on the web pages
This is part of the root `AGENTS.md`, loaded when work reaches `web/`.
- The web pages are the SvelteKit app in `web/`; knotless-serve answers
what they show as XRPC queries, each with its lexicon under
`lexicons/` and the views it shares in `dev.knotless.defs`, as
atproto's appviews do. The pages' TypeScript types are made from those
lexicons by @atproto/lex (`npm run lex` in `web/`, which `dev`, `build`
and `check` run first), into `web/src/lib/lexicons`, not kept in git.
`just lex` checks the answers the screenshots recorded against their
lexicons, and the `test` check every answer the serve tests receive.
- Before changing a page, load the `ui` skill (`.claude/skills/ui`):
its house rules (sentence case, empty states, space), and `just
look`, which draws any page in any state from the recording as
pictures to open: empty, signed in, a menu open, a long title. Look
at every state you touch before pushing.
- For the web pages: the pull's `shots` check shows what moved, with
pictures. No screenshot is kept anywhere: the pages are shot as main
draws them and as the change does, in one run. To look before pushing,
`just shots`, which builds `web/` and main's, shoots only the pages
whose HTML differs, drawn from `harness/shots/fixtures.json` with no
PDS, and leaves each that moved under `target/shots/results/` (its
expected, actual and diff). After a change to what a page's answer
holds, `just shots-up` in the background once, then `just
shots-record`. Each checkout's shots have three ports of their own,
from its path; `SHOTS_PORT` sets another base for all of these.
- `shots-live` shoots this checkout's pages from the recording and again
through knotless-serve over a world: one that moved means the
recording is out of date. Keep `just shots-up` running in the
background while working on pages: `shots-live` then reuses its world
rather than seeding one, which is most of its time.
- The social cards are drawn by knotless-serve: `just shots-cards` takes
them from a world into `target/shots/cards` to look at.
- The components in `web/src/lib` are layered, atomic design's way:
`atoms` (one element: an icon, an avatar), `molecules` (a few atoms
that mean something together: a person, a commit row), `organisms` (a
part of a page: a check list, a feed), `templates` (a page's layout,
without its data). A component uses its own layer and those below it,
never one above; `just lint` checks it. Pages under `web/src/routes`
fill a template with what the queries answer.
- The pages' styles are `web/src/app.css`, and every colour, size,
space, radius and weight in it is a token from `web/src/tokens.css`:
take one by name rather than writing a value, and add one there when
none fits. A colour for what it means is its role, `--danger`,
`--success`, `--attention`, `--done` or `--neutral`, never the hue
under it (`--red`), so a theme can draw success in blue. Every colour that is text meets WCAG AA (4.5:1) on what
it is drawn on, in every theme: `harness/contrast.mjs` checks it, in
`web` and `just lint`, and a new colour that is text goes in
`web/src/lib/contrast.json`, which the theme editor, and knotless's
own check of a theme made anywhere (`crates/knotless/src/theme.rs`),
hold people's own themes to too. A menu that opens is a `<details class="popover …">`.
- In Svelte markup, keep a run of inline text one expression or one line:
a block or component beside it splits the text into nodes, which shapes
differently from the same text whole and moves pixels.
web/CLAUDE.mdnew+1−0
@@ -0,0 +1 @@
@AGENTS.md
AGENTS.md+11−86
@@ -29,104 +29,29 @@ is for that loop.
- `just changed` names the files changed since tests last passed and the
tests that cover them; `just t-changed` runs those and marks the pass.
`just t <cargo test args>` runs any selection on the shared PDS.
- The web pages are the SvelteKit app in `web/`; knotless-serve answers
what they show as XRPC queries, each with its lexicon under
`lexicons/` and the views it shares in `dev.knotless.defs`, as
atproto's appviews do. The pages' TypeScript types are made from those
lexicons by @atproto/lex (`npm run lex` in `web/`, which `dev`, `build`
and `check` run first), into `web/src/lib/lexicons`, not kept in git.
`just lex` checks the answers the screenshots recorded against their
lexicons, and the `test` check every answer the serve tests receive.
- Before changing a page, load the `ui` skill (`.claude/skills/ui`):
its house rules (sentence case, empty states, space), and `just
look`, which draws any page in any state from the recording as
pictures to open: empty, signed in, a menu open, a long title. Look
at every state you touch before pushing.
- For the web pages: the pull's `shots` check shows what moved, with
pictures. No screenshot is kept anywhere: the pages are shot as main
draws them and as the change does, in one run. To look before pushing,
`just shots`, which builds `web/` and main's, shoots only the pages
whose HTML differs, drawn from `harness/shots/fixtures.json` with no
PDS, and leaves each that moved under `target/shots/results/` (its
expected, actual and diff). After a change to what a page's answer
holds, `just shots-up` in the background once, then `just
shots-record`. Each checkout's shots have three ports of their own,
from its path; `SHOTS_PORT` sets another base for all of these.
- `shots-live` shoots this checkout's pages from the recording and again
through knotless-serve over a world: one that moved means the
recording is out of date. Keep `just shots-up` running in the
background while working on pages: `shots-live` then reuses its world
rather than seeding one, which is most of its time.
- The social cards are drawn by knotless-serve: `just shots-cards` takes
them from a world into `target/shots/cards` to look at.
- The components in `web/src/lib` are layered, atomic design's way:
`atoms` (one element: an icon, an avatar), `molecules` (a few atoms
that mean something together: a person, a commit row), `organisms` (a
part of a page: a check list, a feed), `templates` (a page's layout,
without its data). A component uses its own layer and those below it,
never one above; `just lint` checks it. Pages under `web/src/routes`
fill a template with what the queries answer.
- The pages' styles are `web/src/app.css`, and every colour, size,
space, radius and weight in it is a token from `web/src/tokens.css`:
take one by name rather than writing a value, and add one there when
none fits. A colour for what it means is its role, `--danger`,
`--success`, `--attention`, `--done` or `--neutral`, never the hue
under it (`--red`), so a theme can draw success in blue. Every colour that is text meets WCAG AA (4.5:1) on what
it is drawn on, in every theme: `harness/contrast.mjs` checks it, in
`web` and `just lint`, and a new colour that is text goes in
`web/src/lib/contrast.json`, which the theme editor, and knotless's
own check of a theme made anywhere (`crates/knotless/src/theme.rs`),
hold people's own themes to too. A menu that opens is a `<details class="popover …">`.
- In Svelte markup, keep a run of inline text one expression or one line:
a block or component beside it splits the text into nodes, which shapes
differently from the same text whole and moves pixels.
- Working on the web pages (`web/`): `web/AGENTS.md`, loaded with
them, says how they are built, shot and checked, and to load the `ui`
skill before changing one.
- Done: all seven of the pull's checks pass. When one fails in a way its
log and pictures do not explain, run that one thing here, alone
(`COV_CLEAN=1` rebuilds everything first, for a cov miss that makes no
sense).
## Lexicons
A new or changed schema under `lexicons/` follows the Lexicon Style Guide
(https://atproto.com/guides/lexinomicon) and passes `just lex`: goat's
linter, less two warnings kept on purpose (`pack`'s inline `bytes` and
`listReports`' `id`), then `harness/lexicons.mjs`. That checks what the
linter does not see: `knownValues` rather than `enum`; objects in new
arrays rather than bare values; open unions; a description on every
`main`; a `maxLength` on strings without a format in what people write
(records and procedure input); accounts by DID, not handle. And that every
schema only grows from main's, as the branch left main: a schema already
published is evolved, not changed, with new fields optional and nothing removed, renamed, retyped,
made required or narrowed. Fix anything either says, or say why not.
Still by hand: no `required` a reader could do without, since it can never
be dropped; a description on any field whose name does not say what it
holds; `maxGraphemes` beside `maxLength` for text people read; reuse of
`dev.knotless.defs` and `com.atproto.repo.strongRef` over new shapes.
`cargo fmt` can put a `?` on a line of its own, which coverage then counts
as a line never run: bind the value first, or use a named function for a
row mapper, rather than lowering the gate.
## Lexicons
A new or changed schema under `lexicons/` follows `lexicons/AGENTS.md`
and passes `just lex`.
## Queries
A query of the index (`crates/knotless-serve/src/index.rs`) is planned
as the tests run it, against tables said to be prod's size
(`crates/knotless-serve/src/plans.rs`), and `test` fails on any that
scans a table that grows with the network: `forge`, `check_run`,
`comment` and the rest of `GROWING`, or sorts many of its rows (a range
leading its index, or what a search finds for each row of a scan, as a
repository's pushes for every repository). Tests on empty tables pass a query
that takes seconds on prod, so this is what holds them. Its log names
the query and its plan. Give it an index, or a query that uses the one
there: an expression index needs the expression as written, and a
column compared to it marked `+` (`json_extract(c.value, '$.pull') =
+p.uri`), as the column's affinity otherwise keeps the index unused.
A scan meant to be, of a few rows an operator reads now and then, says
so in its SQL with why, `-- scans: forge, as only the operator asks`,
where review sees it; a sort meant to be, `-- sorts: forge, …`. On prod,
a statement slower than 100 ms logs `sql`, with the request's trace. To see the plans while fixing one, run its tests
with `KNOTLESS_PLANS=<dir>` and `node harness/plans.mjs <dir>`.
at prod's size as the tests run it, and `test` fails on one that scans
or sorts a table that grows with the network:
`crates/knotless-serve/AGENTS.md` says how to fix or mark one.
## Docs