Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Working on Triplex

Triplex is a pre-1.0, Effect-native fact database for TypeScript. Read
[CONTRIBUTING.md](CONTRIBUTING.md), [ARCHITECTURE.md](ARCHITECTURE.md), and
[Current state](docs/current-state.md) before changing its contracts. The
[modeling guide](docs/agents.md) covers building applications with Triplex.

## Setup and checks

Use Node.js 22 or newer and the pinned pnpm 10.11.0:

```sh
corepack enable
pnpm install --frozen-lockfile
pnpm check
```

`pnpm check` is the single root check: formatting (oxfmt), linting (oxlint),
TypeScript, checked Markdown examples and generated example outputs, unit and
SQLite/workerd integration tests, package builds, and the VitePress docs build.
The docs build also checks the generated `dist/llms.txt` structure and local links.

Run `pnpm pack:check` after package changes to verify tarballs and a clean consumer.
PostgreSQL and FoundationDB integrations require external services or native
libraries and are opt-in with `pnpm test:postgres:integration` and
`pnpm test:foundationdb:integration`. Follow CONTRIBUTING.md for changes that
require these suites. Browser tests use `pnpm test:e2e` after installing Chromium
with `pnpm exec playwright install chromium`. Stress tests are opt-in with
`pnpm --filter triplex-stress stress-test`.

## Repository layout

- `packages/core`: browser-safe model, Effect services, KV storage, Datalog,
configuration, derivations, and public runtime composition.
- `packages/sql`, `packages/sqlite`, `packages/postgres`, `packages/cloudflare`,
`packages/foundationdb`: shared SQL and backend implementations.
- `packages/host`, `packages/http`, `packages/cli`, `packages/dashboard`,
`packages/testkit`: hosting contracts, HTTP, tools, and reusable conformance tests.
- `examples/`: runnable demos and reference hosts.
- `test/integration`, `test/stress`, `e2e/`: cross-package, stress, and browser tests.
- `docs/`: VitePress pages and checked snippets; `docs/.vitepress/llms.ts`
generates `llms.txt`, `llms-full.txt`, and Markdown page variants into `dist/`.
- `scripts/`: documentation and package checks.
- `.github/workflows/`: CI, docs deployment, and Changesets releases;
`alchemy.run.ts` defines the Cloudflare documentation site.

## Conventions and boundaries

- Use ESM and TypeScript with Effect. Pin external dependencies in the root pnpm
catalog and use workspace packages through their public exports.
- Preserve the one-way graph in ARCHITECTURE.md: core must not depend on backends
or Node-only APIs; SQL and backend packages build on core.
- Public exports point to generated `dist` files. Add a Changeset for a
publishable package change; keep private packages out of its frontmatter.
- Mark self-contained documentation examples with `ts check` fences; use ordinary
`ts` fences for fragments. Keep generated outputs in sync with `pnpm docs:outputs`.
- Keep maturity claims accurate: KV and SQLite are supported, PostgreSQL is a
pre-1.0 production candidate, and Cloudflare and FoundationDB are experimental.
- Do not edit generated `dist` files, import sibling project source, or bypass
public package boundaries. Do not deploy manually, publish packages, or merge
without explicit authorization; the existing workflows handle deployment and
releases.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,8 @@ pnpm test:foundationdb:integration

See [CONTRIBUTING.md](CONTRIBUTING.md) for the complete contribution contract.

Part of the [WorldVM](https://worldvm.com) family of experiments.

## License

MIT © 2026 Ben Jacobson.
6 changes: 3 additions & 3 deletions docs/.vitepress/llms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export const siteUrl = "https://triplex.build";
const summary =
"The database that remembers why: an embedded fact database for TypeScript back-office " +
"systems. Every write records who made it, which versioned rules governed it, and when it was " +
"true, so audit questions and open work become queries.";
"true, so audit questions and open work become queries. Triplex is pre-1.0; install with " +
"`npm install @triplex-build/triplex effect@4.0.0-rc.112`.";

const keyFacts = `Install with \`npm install @triplex-build/triplex effect@4.0.0-rc.112\`. Triplex is pre-1.0,
requires Effect 4 (\`effect@4.0.0-rc.112\`; Effect 3 is not compatible), is ESM-only, and targets
const keyFacts = `Triplex requires Effect 4 (\`effect@4.0.0-rc.112\`; Effect 3 is not compatible), is ESM-only, and targets
Node.js 22+ plus modern browsers and edge runtimes for the core. In-memory and SQLite storage are
supported, PostgreSQL is a production candidate, and Cloudflare and FoundationDB are experimental.

Expand Down
1 change: 1 addition & 0 deletions e2e/docs-home.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ test("decision receipt preserves its rules while switching recorded knowledge",
test("agent-readable documentation is served as plain text", async ({ request }) => {
const index = await request.get("/llms.txt");
expect(index.ok()).toBe(true);
expect(index.headers()["content-type"]).toContain("text/plain");
const body = await index.text();
expect(body).toMatch(/^# Triplex\n\n> The database that remembers why/);
expect(body).toContain("(https://triplex.build/agents.md)");
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"clean": "turbo run clean",
"check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm docs:check && pnpm test && pnpm build",
"dev": "turbo watch build",
"docs:build": "vitepress build docs",
"docs:build": "vitepress build docs && node scripts/check-llms.mjs",
"docs:check": "node scripts/check-markdown-code.mjs && tsc --noEmit -p docs/snippets/tsconfig.json && pnpm docs:outputs --check",
"docs:outputs": "tsx --tsconfig docs/snippets/tsconfig.json docs/snippets/home/site-safety/run.ts",
"docs:deploy": "alchemy deploy --stage prod",
Expand Down
49 changes: 49 additions & 0 deletions scripts/check-llms.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import assert from "node:assert/strict";
import { readFile, stat } from "node:fs/promises";
import { dirname, resolve, sep } from "node:path";
import { fileURLToPath } from "node:url";

const outDir = resolve(dirname(fileURLToPath(import.meta.url)), "../dist");
const siteUrl = new URL("https://triplex.build");
const index = await readFile(resolve(outDir, "llms.txt"), "utf8");

assert.match(index, /^# Triplex\n\n> \S[^\n]+\n/, "llms.txt needs an H1 and summary");
const summary = index.split("\n")[2];
assert.match(summary, /pre-1\.0/, "The summary must state maturity");
assert.match(
summary,
/npm install @triplex-build\/triplex/,
"The summary must explain installation",
);
assert.doesNotMatch(index, /<!doctype|<html\b/i, "llms.txt must not be an HTML app shell");

const sections = index.split(/^## .+$/m).slice(1);
assert.ok(sections.length > 0, "llms.txt needs sections");
for (const section of sections) {
const entries = section.split("\n").filter((line) => line.startsWith("- "));
assert.ok(entries.length > 0, "Each section needs documentation links");
for (const entry of entries) {
assert.match(entry, /^- \[[^\]]+\]\([^)]+\): \S/, "Links need titles and notes");
}
}

let localLinks = 0;
for (const [, href] of index.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)) {
const url = new URL(href);
assert.equal(url.protocol, "https:", `Use absolute HTTPS links: ${href}`);
if (url.origin !== siteUrl.origin) continue;

const pathname = decodeURIComponent(url.pathname);
const file = resolve(outDir, `.${pathname}`);
assert.ok(file.startsWith(`${outDir}${sep}`), `Link leaves the docs output: ${href}`);
assert.ok((await stat(file)).isFile(), `Missing built page: ${href}`);
// Check the actual asset, so an HTTP fallback cannot mask a missing page.
if (/\.(md|txt)$/.test(pathname)) {
const content = await readFile(file, "utf8");
assert.ok(content.trim().length > 0, `Empty documentation: ${href}`);
assert.doesNotMatch(content, /<!doctype|<html\b/i, `HTML fallback at ${href}`);
}
localLinks++;
}
assert.ok(localLinks > 0, "llms.txt must link to local documentation");
console.log(`Verified llms.txt structure and ${localLinks} local documentation links.`);
Loading