Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a6889c1
design: slice S0 settles the harness decisions and writes the probe e…
darko-mijic Oct 1, 2026
951874d
build: S0 foundation, the toolchain, the fixture app, four tiers and …
darko-mijic Oct 1, 2026
e75b484
wip: S0 probes as build job 2 left them, and the handover inside the …
darko-mijic Oct 1, 2026
61046dc
design: slice S0 gives the native harness its own Spec and rewrites t…
darko-mijic Oct 1, 2026
bee2a7e
design: slice S0 writes the harness design into its Spec and rules on…
darko-mijic Oct 1, 2026
d92c92c
wip: the design pass's prototype applied as it stands
darko-mijic Oct 1, 2026
ba5eddd
build: S0 fold-in, the harness closed and probes 1 to 5 bound to thei…
darko-mijic Oct 1, 2026
2c7c20a
evidence: the native run on the clean commit ba5eddd
darko-mijic Oct 1, 2026
07b1d0f
design: slice S0 records probes 1 to 5 on their facts
darko-mijic Oct 1, 2026
247a3d3
design: slice S0 rules on three reviews of the fold-in
darko-mijic Oct 1, 2026
8e14191
wip: fold-in 2, the harness list closed and the probe list stopped ha…
darko-mijic Oct 1, 2026
5f63f97
build: S0 second fold-in, the review findings on the harness and the …
darko-mijic Oct 1, 2026
2f045a0
evidence: the native run on the clean commit 5f63f97
darko-mijic Oct 1, 2026
551c4c4
wip: slice S0 handed over a second time, after the second fold-in
darko-mijic Oct 1, 2026
e45cb75
wip: the handover procedure says how the next main thread is started …
darko-mijic Oct 1, 2026
23a91bf
docs: what slice S0 learned about binding an example to a test, for t…
darko-mijic Oct 1, 2026
116f515
build: S0 watch mode, npm run dev keeps the generated files current
darko-mijic Oct 1, 2026
a3bf29d
design: slice S0 rules on the review of the second fold-in
darko-mijic Oct 1, 2026
f15e3de
design: slice S0 gives the watch mode its Design entry and the sessio…
darko-mijic Oct 1, 2026
45abf7a
build: S0 third fold-in, the review of the fixes and of the watch mod…
darko-mijic Oct 1, 2026
035fd44
evidence: the native run on the clean commit 45abf7a
darko-mijic Oct 1, 2026
a8c44b5
build: S0 fourth fold-in, the two reviews of the third fold-in closed
darko-mijic Oct 1, 2026
5c8e616
docs: sdp feedback, code anchors and the backlog at defined
darko-mijic Oct 1, 2026
dde604e
evidence: the native run on the clean commit 5c8e616
darko-mijic Oct 1, 2026
62d43c3
build: S0 fifth fold-in, the record amendment and the second inspecti…
darko-mijic Oct 1, 2026
61c3879
design: no pull request per unit, CI on every push, and a stopping ru…
darko-mijic Oct 1, 2026
bf80ec4
evidence: the native run on the clean commit 61c3879
darko-mijic Oct 1, 2026
772bb05
design: slice S0 closed
darko-mijic Oct 1, 2026
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
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CI
# Every push runs CI, on any branch, so a unit needs no pull request to be checked.
on:
push:
workflow_dispatch:
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
- uses: actions/setup-python@v7
with:
python-version: "3.x"
- run: npm ci
- run: python3 design/tools/check.py
- run: npm run typecheck
- run: npm run lint
- run: npm run format:check
- run: npm test
native:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
- uses: actions/cache@v6
with:
path: .cache
key: convex-local-backend-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('harness/backend-release.json') }}
- run: npm ci
- run: npm run codegen
- run: git diff --exit-code -- fixture/convex
- run: npm run test:native
- uses: actions/upload-artifact@v7
if: always()
with:
name: native-evidence
path: evidence/runs/*.json
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
.DS_Store
node_modules/
.DS_Store
generated/
.cache/
evidence/runs/
.env.local
7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
design/
docs/
vendor/
generated/
**/_generated/
evidence/**/*.json
package-lock.json
1 change: 1 addition & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "singleQuote": false, "trailingComma": "all" }
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# libar-platform

The Convex transactional domain platform. `docs/` holds the inputs. `design/` holds the design as a Software Delivery Protocol corpus of Specs. The code built from the design sits beside them; there is none yet.
The Convex transactional domain platform. `docs/` holds the inputs. `design/` holds the design as a Software Delivery Protocol corpus of Specs. The code built from the design sits beside them: `harness/`, `fixture/` and `tests/`.

## Which reader you are

Expand All @@ -11,7 +11,7 @@ The Convex transactional domain platform. `docs/` holds the inputs. `design/` ho

- `docs/convex-transactional-domain-platform-decisions.md`, "the doc", says what the platform is. Its decisions are proposals until the owner rules. Only the owner states `ready` on a Spec, settles an open question or edits the doc.
- A Spec is intended truth. Report code that disagrees with a Spec as a gap, and the main thread rules which side changes.
- Read the corpus through its graph. Run `npm ci` once, then `npx sdp q '<recipe body>' --root design --json` with a body from `node_modules/@libar-dev/software-delivery-protocol/docs/agent-surface/recipes.md`. Always `npx sdp`: a bare `sdp` on macOS is an unrelated tool.
- Read the corpus through its graph. Run `npm ci` once, then, from the repository root, `npx sdp q '<recipe body>' --json` with a body from `node_modules/@libar-dev/software-delivery-protocol/docs/agent-surface/recipes.md`. Always `npx sdp`: a bare `sdp` on macOS is an unrelated tool.
- Before you write or edit a Spec, read `design/PLAN.md` section 6 and `node_modules/@libar-dev/software-delivery-protocol/.agents/skills/sdp-authoring/SKILL.md`.
- `python3 design/tools/check.py` prints `OK` when the Specs, the README and the ledger agree. Run it before you say they do.
- Name the tier behind a claim about behavior: compiled, pure test, `convex-test`, or native backend.
Expand Down
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A transactional domain platform for Convex. Bounded contexts own their state and their journal, one business operation is one mutation, and work leaves the transaction only when something has to wait, spread load or reach an external system.

This repository holds the design and, from the first build slice on, the code built from it. There is no code yet.
This repository holds the design and, from the first build slice on, the code built from it. The first slice built the test harness, the fixture app and the probes.

## Where to start

Expand All @@ -14,12 +14,37 @@ This repository holds the design and, from the first build slice on, the code bu

```sh
npm ci
npx sdp view design
open design/generated/design-review/index.md
npx sdp view
open generated/design-review/index.md
```

`python3 design/tools/check.py` validates the corpus and checks that the Specs, the index and the review ledger agree.

## Status

The design has been through three review rounds and carries open findings, all listed in `design/reviews/consensus-ledger.json`. It is being finished by building it, one slice at a time, starting with the repository layout and the probes.

## Running the code

Use Node 24 and run `npm ci` in the repository root. The repository has one `package.json`. The root `convex.json` names `fixture/convex` as the function directory of the fixture app.

Each tier has its own command and its own directory:

- Domain tier: `npm run test:pure` runs `tests/pure`. These tests need no backend and no simulator. Until slice S1 adds a domain, they test the pure parts of the harness.
- Simulator tier: `npm run test:simulator` runs `tests/simulator` on `convex-test`. Every test name starts with `convex-test` and the version it ran on. A simulator pass is not native proof.
- Native tier: `npm run test:native` runs `tests/native`. Every test owns its processes and temporary storage, stops its processes and removes its storage when it ends. Bound examples deploy the fixture app.
- Compile check: `npm run typecheck` compiles the fixture app, the harness and the tests. `npm run test:types` runs the type tests in `tests/types`. A compile check says nothing about a running backend.

The claim tiers in `AGENTS.md` map to these names: compiled means compile check, pure test means domain tier, `convex-test` means simulator tier, and native backend means native tier.

`npm test` runs the type tests, the domain tier and the simulator tier. None of them needs a backend.

The native tier downloads the pinned backend release into `.cache/` on first use and checks the hash of the executable on every run. `harness/backend-release.json` pins the release for macOS on Apple silicon and for Linux on x86-64. On another platform, or to run another build, set `CONVEX_BACKEND_BINARY` to a `convex-local-backend` executable. The run's record then names the release only when the executable's hash is the pinned one.

The harness gives every Convex CLI call the address and the admin key of the backend it started, and an environment of its own. A deploy key or a deployment named in your shell or in a `.env` file is never used.

`npm run codegen` starts a disposable backend and rewrites `fixture/convex/_generated` and `fixture/convex/annex/_generated`. Run it after you add or rename a fixture function or change its arguments, and commit the result. CI fails when the committed files differ from what codegen writes.

`npm run dev` starts one disposable backend and runs `convex dev` against it. Each time you save a file under `fixture/convex`, it rewrites both `_generated` directories. It runs until you press Ctrl-C, then stops the backend and removes it. Commit the generated files as with `npm run codegen`: CI still fails when they are stale. It refuses to start when a `.env.local` file exists in the repository root, because `convex dev` would write the backend's URL into it.

Every native run writes a record to `evidence/runs/`. [`evidence/README.md`](evidence/README.md) says what a record holds and which records are kept.
3 changes: 3 additions & 0 deletions convex.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"functions": "fixture/convex/"
}
1 change: 0 additions & 1 deletion design/.gitignore

This file was deleted.

26 changes: 14 additions & 12 deletions design/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ The CLI is the `sdp` binary of the pinned `@libar-dev/software-delivery-protocol

```sh
# from the repository root
npx sdp validate design
npx sdp validate
```

Both forms were run against the scaffold in this directory on 2026-09-30 and printed `validate: 0 errors · 0 warnings`. No `--exclude` is needed; `design/` carries no fixtures. Output goes to `design/generated/` (graph, contracts, registrar manifest), which `design/.gitignore` ignores.
The Protocol's root is the repository root since slice S0, so that one graph holds the Specs under `design/specs/` and the test anchors that verify them, which live outside `design/`. No `--exclude` is needed. Output goes to `generated/` at the repository root (graph, contracts, registrar manifest), which `.gitignore` ignores. A test imports its example's step contract from `generated/contracts/`, so `npx sdp build` runs before the tests.

Other verbs, same path and root rules:

- `npx sdp validate design --watch` is the authoring loop; it re-runs on carrier changes and stays alive.
- `npx sdp view design` writes the Design Review to `design/generated/design-review/` (an index plus one page per Spec and Pack). This is the human-readable rendering of the corpus.
- `npx sdp q '<body>' --root design --json` evaluates a recipe body from the package's `docs/agent-surface/recipes.md` against this corpus. Recipes 3 (one Spec's guarantees), 5 (Pack review), 6 (concept search), 7 (readiness divergence), 8 (warnings as data), 9 (promotion preflight) and 11 (the lower ladder) are the useful ones here.
- `npx sdp new spec specs/<family>/<name>.sdp.md --id ID --kind KIND --altitude ALT --title TITLE --outcome OUTCOME` scaffolds an idea-rung stub. PATH is relative to the current directory, so run it from `design/`.
- `npx sdp validate --watch` is the authoring loop; it re-runs on carrier changes and stays alive.
- `npx sdp view` writes the Design Review to `generated/design-review/` (an index plus one page per Spec and Pack). This is the human-readable rendering of the corpus.
- `npx sdp q '<body>' --json` evaluates a recipe body from the package's `docs/agent-surface/recipes.md` against this corpus. Recipes 3 (one Spec's guarantees), 5 (Pack review), 6 (concept search), 7 (readiness divergence), 8 (orphans and gaps), 9 (promotion preflight) and 11 (the lower ladder) are the useful ones on paper. With code in the tree these join them: 2 (the drift alarm, which lists what is built and does not state `ready`), 4 (what a changed file reaches), 10 (declared against enabled verifiers), 20 (the open-question register), 21 (what a Spec rests on, with each target's rung and floor), 22 (the mention audit) and 23 (entry search by name).
- `npx sdp q '<body>' --root node_modules/@libar-dev/software-delivery-protocol/specs --json` asks the same questions of the Protocol's own Specs, which the authoring skill cites.
- `npx sdp new spec design/specs/<family>/<name>.sdp.md --id ID --kind KIND --altitude ALT --title TITLE --outcome OUTCOME` scaffolds an idea-rung stub. PATH is relative to the current directory, so from the repository root it starts with `design/specs/`.

Exit code 1 means an error; warnings exit 0. The corpus must exit 0 with zero errors and zero warnings.

Expand Down Expand Up @@ -158,10 +159,11 @@ Relations shown are the minimum. `epic` means `spec:platform.transactional-domai
| `spec:platform.decision-method` | rule | feature | defined | refines epic | The seven rules of "How decisions are made" |
| `spec:platform.vocabulary` | model | feature | defined | refines epic | CMS, journal, stream version, receipt, obligation, effect, attempt, generation, operation, baseline event, do-nothing option, plus: context, parent, use case, read model, projection, tenant scope, actor, request key, effect key, needs attention, write pause, layer, profile. OQ6 clashes (receipt, generation) as `[non-blocking]` open questions |
| `spec:platform.acceptance-contract` | rule | feature | defined | refines epic | Scenarios test behavior; layer = first layer making it available; the four tiers; fixture app separate from example app; test-only functions never ship; disposable backend per test; the evidence record; readiness stated as specified, implemented, tested under named conditions, operationally accepted |
| `spec:platform.native-harness` | rule | feature | defined | refines acceptance-contract | E-13 and E-15, which the doc does not state: the fixture issuer and ordinary clients, the admin key for setup and reading only, two compositions, faults and assertions, the four controls, the run's record. Added by slice S0 with four examples |
| `spec:platform.existing-systems` | rule | feature | defined | refines epic | No deletion or migration authorized; the five bullets |
| `spec:laws.law01-sanctioned-writes-only` ... `spec:laws.law12-one-retry-owner` | rule | feature | defined | refines epic (Law 4 also decidedBy D6) | One law each, the sentence verbatim in `## Rule`, the reworded note in narrative |
| `spec:decisions.d01-one-mutation-per-operation` ... `spec:decisions.d19-operations-travel-with-capability` | decision | feature | defined | refines epic; dependsOn per table below | Provenance line in narrative; `context` = the concern and the do-nothing option; first `alternative` = the do-nothing option; further `alternative`s = the doc's rejected options; `decision` = the ruling; `rationale`; `consequence` = costs, gaps, open items; the doc's "Probe" lines as `[non-blocking]` open questions naming the probe number |
| `spec:facts.fact-ledger` | rule | feature | defined | refines epic | Statuses; nothing probed yet; sources support facts not the design; each fact names its decisions |
| `spec:facts.fact-ledger` | rule | feature | defined | refines epic | Statuses; the probes that have run; sources support facts not the design; each fact names its decisions |
| `spec:facts.probe-plan` | workflow | feature | defined | refines fact-ledger; dependsOn F14 to F17 | Seven flows, one per probe, each ending with the decisions it serves |
| `spec:facts.f01-serializable-mutations-under-occ` ... `spec:facts.f17-migrations-fits-generation-backfill` | constraint | story | defined | refines fact-ledger | statement = the fact; `flavor: convex-fact`; `target` per 3.2; `measurableBy` = source and probe |

Expand Down Expand Up @@ -273,7 +275,7 @@ Fact slugs: `f01-serializable-mutations-under-occ`, `f02-component-calls-commit-

## 4. Scenarios to examples

Every row of the doc's acceptance table becomes at least one `example` Spec. When a row enumerates cases (three injection points, three order sizes, two broken subsystems), each case is one sibling example binding a different point, per SDP's point-per-example law. Titles are the doc's scenario text. The tier comes from the acceptance contract: domain (pure), simulator, native, or end to end.
Every row of the doc's acceptance table becomes at least one `example` Spec. When a row enumerates cases (three injection points, three order sizes, two broken subsystems), each case is one sibling example binding a different point, per SDP's point-per-example law. An "or" in the scenario's text enumerates cases the same way (a worker or agent namespace, rate or capacity refusal). An "or" in the pass condition does not: the example binds the alternative its design produces. A verification bullet adds assertions to a case and never stands in for one. Titles are the doc's scenario text. The tier comes from the acceptance contract: domain (pure), simulator, native, or end to end.

| Sc | Scenario (doc text) | Parent Spec | Example ID | Tier | Pkg |
|---|---|---|---|---|---|
Expand Down Expand Up @@ -329,7 +331,7 @@ Five packages, disjoint by directory. Sequence: A first, because every other pac

Definition of done, every package:

1. `npx sdp validate design` exits 0 on the merged corpus. While a foreign ID from the package's cross-package list is not yet authored, the only tolerated error is `conformance/referential-integrity` naming that ID.
1. `npx sdp validate` exits 0 on the merged corpus. While a foreign ID from the package's cross-package list is not yet authored, the only tolerated error is `conformance/referential-integrity` naming that ID.
2. No warning. An example with no test bound is data below `ready`, not a warning.
3. Every Spec in the package's inventory exists with the stated kind, altitude, readiness and minimum relations; every scenario assigned to the package has its example(s).
4. The pack manifest lists every Spec of the package in inventory order.
Expand Down Expand Up @@ -601,7 +603,7 @@ Completeness:
18. All 43 scenario rows of the doc have examples per section 4, with the enumerated cases split into siblings.
19. Every component in section 2 has its Specs; every Spec in 3.4 exists.
20. Every open question of the doc (OQ1 to OQ6) is recorded on the Spec section 3.4 assigns it to.
21. Every assumed fact (F14, F15, F17) states `scoped` behind a `[blocking]` question that names its probe, is named as an `assumption` or open question on each Spec that relies on it, and has its probe in the probe plan. F16 was found documented and states `defined`.
21. Every assumed fact (F17; F14 and F15 until their probes ran in slice S0) states `scoped` behind a `[blocking]` question that names its probe, is named as an `assumption` or open question on each Spec that relies on it, and has its probe in the probe plan. F16 was found documented and states `defined`.

Convex correctness, checked against the sources the doc lists (S1 to S15) and the recheck in section 9, never from memory:

Expand All @@ -616,8 +618,8 @@ Convex correctness, checked against the sources the doc lists (S1 to S15) and th
30. Scheduling: `ctx.scheduler` calls from a mutation commit with it (F8); at most 1000 per mutation; a scheduled action is never retried by Convex (F9), so external calls are claim, call, settle with retries owned by the obligation module; scheduled functions carry no auth, so the obligation record carries authority (D13).
31. `_scheduled_functions`: the design uses only the documented states (Pending, InProgress, Success, Failed, Canceled) and the 7-day retention, and never assumes `cancel` stops an in-flight action.
32. Restore: backups exclude pending scheduled functions (F12), so restore rebuilds dispatch from obligations and starts with dispatch off (D19).
33. `ConvexError`: rejection data uses values the `v` validators accept; the design states that a throw in a mutation prevents the commit and that data intact across a component boundary is F14, assumed, with Probe 2 named.
34. Reactivity of a parent query over a component query is stated as F15, assumed, with Probe 5 named.
33. `ConvexError`: rejection data uses values the `v` validators accept; the design states that a throw in a mutation prevents the commit and that data intact across a component boundary is F14, probed by Probe 2.
34. Reactivity of a parent query over a component query is stated as F15, documented and probed by Probe 5.
35. Workflow component: only waits and external effects are steps; per-step retries are off or ownership is passed deliberately; the definition version is saved at start; deploy behavior is stated as trigger and promise (D16). `@convex-dev/migrations`: a batch is a checkpointed mutation, so backfill versus live commands is an OCC race per batch (D9, Probe 6).
36. Timeouts: queries and mutations have 1 second; the max-lines limit and every per-command bound respect it; actions have 30 minutes (Convex runtime) or 10 minutes (Node).

Expand Down
Loading
Loading