adapttable
AGENTS.md72 starsChanged 6 months agoMITView on GitHub
Working rules for this repository. They apply to AI agents and human contributors alike; PRs are reviewed against them. @adapttable/core owns the engine — state, the filter model, operators, URL serialization, chips, mutations, registries, and the state machines behind every interaction. The framework bindings adapt them into hooks (@adapttable/react) or signals (@adapttable/angular) or refs (@adapttable/vue) and render structure. Adapters own the look. Switching the kit in the live demo must change how the whole table looks, including the Filters popover…
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,439 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository3 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- llms-full.txtllms.txt
- llms.txtllms.txt
The file
We split this AGENTS.md into the rules it gives an agent
8 rules. About 0 are common, 4 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 26
Every visible control (input, select, checkbox, button — anything the end user clicks) is a required slot the adapter fills with its own …
- Distinctive: line 31
Do not add user-facing controls to core or the binding, and do not give a slot a default implementation.
- Very specific: line 37
If a kit's overlay or portal misbehaves inside the filter popover, fix it in that adapter (disablePortal, getPopupContainer, or that kit's …
- Very specific: line 46
When a kit needs a core name its binding lacks, add the re-export to the binding — never import core from the kit.
- Distinctive: line 124
Never weaken tests or assertions to pass.
- Very specific: line 139
All adapters at parity · mobile card layout handled (or the behavior stated explicitly) · RTL correct · keyboard accessible with …
- Very specific: line 151
Check real versions first — packages/*/*/package.json and npm view <pkg> version.
- Distinctive: line 231
Prefer each kit's native overlay primitive so portaling, z-index and focus handling come for free.
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
AdaptTable — agent and contributor guide
Working rules for this repository. They apply to AI agents and human contributors alike; PRs are reviewed against them.
Architecture: core is headless
@adapttable/core owns the engine — state, the filter model, operators, URL
serialization, chips, mutations, registries, and the state machines behind
every interaction. The framework bindings adapt them into hooks (@adapttable/react) or signals
(@adapttable/angular) or refs (@adapttable/vue) and render structure. Adapters own the look. Switching the kit in
the live demo must change how the whole table looks, including the Filters
popover and drawer: an MUI table filters with MUI controls, a Mantine table
with Mantine controls.
Unify the model, never the pixels. Framework-neutral logic — a decision about state, layout, labels, URL state, keyboard maps, or sizes that does not touch a framework's lifecycle or rendering — lives in core, once. Bindings adapt that logic; they do not keep a second copy of it. The standing pattern is Chrome + slots:
- The binding ships
*Chromecomponents that own structure only — layout, the recursion over groups, keyboard wiring, localized labels, and thedata-adapttable-partnames — driven by the models and state machines in core. - Every visible control (input, select, checkbox, button — anything the end
user clicks) is a required slot the adapter fills with its own kit's
component. Slots have no native fallback in core or the binding: a kit
cannot silently render raw HTML.
adapter-unstyledsupplies native controls because native IS its kit; shadcn/tailwind build on unstyled. - Do not add user-facing controls to core or the binding, and do not give a slot a default implementation. Invisible chrome (live regions, announcers, layout structure) is fine there.
- Feature parity means every kit has the feature with its own components. A shared look is not parity — copying one adapter's raw-HTML control into another adapter is the same defect as drawing it in core.
- If a kit's overlay or portal misbehaves inside the filter popover, fix it
in that adapter (
disablePortal,getPopupContainer, or that kit's native select) — never by weakening the slot contract. data-adapttable-partnames and placement are part of the public contract: the same part lands on the same element in every adapter, and documentedclassNameskeys are honored by every adapter that renders the part.- A kit talks only to its binding: a React kit imports only
@adapttable/react/…, a Vue or Angular kit only its own binding. Only a binding imports@adapttable/core, and it re-exports what its kits need. When a kit needs a core name its binding lacks, add the re-export to the binding — never import core from the kit. Core's deprecation notes point to core (@adapttable/core/binding), never to a framework package. ESLint enforces this on kit sources. The two Vue generic filter SFCs are a type-only exception:NativeChecklistFilter.vueimports its originalChecklistFilterProps, andNativeFilterTree.vueimports its originalFilterTreeBuilderProps, from@adapttable/core/binding. Vue's macro resolver needs these original declarations directly. Their runtime Chrome, models and helpers still come from the Vue binding; other core imports remain restricted.
Framework packages
packages/react/reactis the React binding; its eight supported kits live underpackages/react/adapter-*.packages/angular/angularis the Angular binding, with signal controllers, templates, component renderers and structural Chrome.@adapttable/angular-unstyled(packages/angular/adapter-angular-unstyled) and@adapttable/ng-zorro(packages/angular/adapter-ng-zorro) provide native HTML and NG-ZORRO controls through that binding.packages/angular/ai-angularadds optional agent, assistant and speech bindings over neutral@adapttable/ai.- The Angular binding, all nine Angular adapters and
@adapttable/ai-angularare published on npm. Check the current manifest and registry version before choosing a changeset bump; package versions are independent. packages/vue/vueis the Vue binding. Its eight published adapters live underpackages/vue/adapter-*and supply native controls through the binding.
Product decisions — settled, do not reopen
- Core is headless; adapters own appearance. Chrome + slots (above) is the settled widget architecture — do not reopen core-drawn controls or per-kit copies of the layout.
- Junior-friendly, senior-targeted: easy defaults for everyone, full control for experts — never trade one for the other.
- One word per concept:
serveris the remote-tier word,useQuerySourcethe query-library hook. No synonyms, no parallel callbacks, no config-object API. - The showcase is the only demo app. Competitor comparisons stay factual and dated.
- Everything is opt-in: omitting a prop renders nothing and costs nothing.
- The table never owns the data. Every write — edits, adds, deletes, reorders — is a callback to the host; the table asks, the host does.
Process
- Branch off
mainfor every change (feat/…,fix/…,chore/…); merge via PR. Never commit directly tomain. - One concern per commit. A commit that belongs to an issue names it with
Refs #Nin its body; the pull request body carries oneCloses #Nline per issue it completes, so the closing list is reviewed — and corrected if needed — before merge. A bare(#N)in a subject links but never closes. An issue closes when the PR that lists it merges: work that is only committed locally leaves its issue open, and closing one by hand is never the way it happens. - Screenshots and scratch artifacts are temporary: keep them in
.playwright-mcp/or/tmp, never the repo root, and delete them when done.
The quality gate — green before every push, zero suppressions
pnpm check is the full local library gate. Husky runs format:check on
every commit (fast). Pre-push runs pnpm check:if-needed then
pnpm e2e:if-needed, using the same path classify as the PR Detect job:
docs, workflows, and README skip lint, types, coverage and build; a package
change still runs the full sequence:
format:check → lint → lint:root → check:readmes → check:docsurface
→ check:parts → typecheck → test:coverage → test:scripts → build
→ publint → smoke:dist → budget → test:e2e- Coverage floors are enforced per package — do not lower a threshold to get green; raise real coverage or raise the design question.
- Zero ignore comments. No
eslint-disable,@ts-ignore,@ts-expect-error,prettier-ignore, or coverage excludes to dodge a rule. Fix the root cause; if a rule seems genuinely wrong, raise it. - Never weaken tests or assertions to pass. If a test fails, the code or the test is wrong — find out which.
pnpm test:e2eis part of the bar —pnpm checkdoes not run it. Pre-push runspnpm e2e:if-needed: skip when the diff vsorigin/mainhas no browser-relevant package, showcase, docs or E2E changes; run only changed E2E specs when that is all that changed; otherwise the full suite. Shared, React and Angular package edits and docs-only edits run Playwright. The PR workflow uses the same path set; README/metadata-only edits still skip it. Needs Chromium once:pnpm exec playwright install chromium.- A change is not done until the gate is green with real command output — evidence, not assertions. Anything visual or keyboard-driven is also verified in a real browser.
Feature definition of done
All adapters at parity · mobile card layout handled (or the behavior stated
explicitly) · RTL correct · keyboard accessible with screen-reader
announcements where the feature speaks · new labels localized in every
locale in packages/shared/i18n · state serializes to URL / Saved Views where the
feature is stateful · docs page + llms.txt registration + showcase
coverage where visual · tests with the coverage floors met.
Changesets and version bumps
The bump follows the change, per SemVer. Pick it from what the change does, not from habit:
- Check real versions first —
packages/*/*/package.jsonandnpm view <pkg> version. Never guess from memory or chat. - Docs, demo media, changelog-only, bug fixes, UI corrections, internal
refactors →
patch. New user-facing feature or new public export →minor; that is what SemVer means and labelling onepatchhides it from everyone reading the changelog. Breaking API →major, only with the maintainer's approval — never a default, never inferred from the work being finished. - A replaced path is deprecated in place and keeps working. Deprecations are
not removed piecemeal: they stay until the maintainer decides to ship a
major, and that release removes every deprecated path at once and carries
the breaking changes parked for it. Until then,
@deprecatedplus a note naming the replacement is the whole of the change. - Versioning is independent per package — never tell users to match versions; exact pins resolve core automatically.
Release mechanics: merge the PR carrying its changeset → the bot opens a
"Version Packages" PR → merging that publishes to npm. A main push with
neither a pending changeset nor an unpublished version skips the Release
job; publish is build → publint → changeset publish, not verify:release.
pnpm version-packages preserves the tested lockfile graph and updates only
linked workspace specifiers, then validates with a frozen install. Dependency
upgrades belong in a separately tested PR. CI and local gates recognize release
metadata by content, including starter pins and lockfile specifiers; any source,
manifest contract or resolved dependency change runs the normal checks.
Generated vs hand-written files
Committed but generated — never hand-edit:
llms-full.txt— built bypnpm llmsfromdocs/*.mdin theDOCSarray order ofscripts/build-llms-full.mjs. Edit docs →pnpm format→ regenerate → commit, in that order.apps/showcase/<adapter>/**/index.htmland the redirect stubs beside them — written bynode scripts/build-showcase-html.mjsfromapps/showcase/matrix.mjs. Edit the matrix → regenerate → commit;scripts/showcase-html.test.mjsfails the gate when they disagree.packages/*/CHANGELOG.md— written by changesets only.pnpm-lock.yaml.
Generated at build time and gitignored: apps/docs/src/content/docs/ and
apps/docs/public/llms*.txt (from sync-docs.mjs — the canonical markdown
is root docs/; always edit there), plus dist/, coverage/, .astro/,
.turbo/.
Hand-written: root docs/*.md, llms.txt (the llmstxt.org index), README.
A new docs page needs two registrations or it silently misses the LLM
surface: the DOCS array in scripts/build-llms-full.mjs AND a link in
llms.txt.
Showcase rules
- Each adapter demo renders only its own kit's components — mount the real
@adapttable/*adapters, never cross-import kits, never mock the table. - A demo page is registered once, in
apps/showcase/pages.mjs: Vite's build inputs and the sitemap's/demo/URLs are both generated from that manifest, andpnpm check:sitemapverifies the composed site against it in the docs workflow. A page that must ship without being indexed — a redirect stub — setsindexable: false. - The demo is adapter-first: each adapter has a landing page
(
/demo/mantine/) and one page per feature (/demo/mantine/pivot/), so a reader searching for a Mantine pivot table lands on one. Those pages are not written by hand — the adapters, the features and every word on the pages live inapps/showcase/matrix.mjs, whichpages.mjsexpands into the manifest,scripts/build-showcase-html.mjswrites the static HTML from, andsrc/matrix/MatrixPage.tsxrenders. Add a feature there and it appears in the build, the sitemap, the nav and the served HTML at once. - Column groups, RTL, realtime, rows, nested tables and accessibility are
matrix features — one page per kit, same as filtering. Pagination is not a
demo destination; the docs page owns that search.
/demo/rtl/is a redirect stub to/demo/mantine/rtl/. - Overlay contracts:
filtersMode="popover"(default) is a lightweight anchored card with no backdrop. It anchors under its trigger (flipping for RTL), closes on Escape and outside click, restores focus on Escape, setsaria-expandedon the trigger, and sits above sticky headers and pinned cells with zero bleed-through.filtersMode="drawer"keeps a real backdrop that dims and blocks the background — each kit's native Drawer provides it.- Prefer each kit's native overlay primitive so portaling, z-index and focus handling come for free.
- Demo media in
apps/docs/public/media/stays — including files nothing currently references; they are source material for re-cuts and re-uploads. The only defect is identical bytes under two names, which get consolidated.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

