~bigbes/sr-ht-spec

c833182e7a6698f549f0c2b00a79b513cc157cd5 — bigbes 27 days ago c23aec5
docs: add the project schema and record the filter-polarity trap

Projects are no longer deferred, so the schema section carries them.

Records why the meta-project is an implicit filter rather than a row: a
stored +everything needs a sync job on every space creation, whose only
failure mode is silently omitting a space, and a corpus-wide view must not
be renameable or deletable.

Flags the polarity hazard between the two layers. An empty project selects
nothing while an empty search space list means every space, so passing a
project's members straight into a query turns an empty project into the
whole corpus. The durable fix is for queries to take the filter type
rather than a bare slice, which is deferred while the read surfaces are
mid-build against the current signature.
1 files changed, 52 insertions(+), 9 deletions(-)

M docs/DESIGN.md
M docs/DESIGN.md => docs/DESIGN.md +52 -9
@@ 1107,13 1107,55 @@ CREATE TABLE digest_mark (
);
```

Projects are deliberately absent: a project is a saved filter, so it is a name
plus a space-id list, and adding it before there are several spaces to filter
would be speculative. It arrives with the meta-project in Phase 2.
```sql
CREATE TABLE project (
  id            SERIAL PRIMARY KEY,
  owner         TEXT NOT NULL,
  name          TEXT NOT NULL,
  created       TIMESTAMPTZ NOT NULL DEFAULT now(),
  UNIQUE (owner, name)
);

-- Membership is a SET: the composite key both dedupes and indexes project->space.
CREATE TABLE project_space (
  project_id    INTEGER NOT NULL REFERENCES project(id) ON DELETE CASCADE,
  space_id      INTEGER NOT NULL REFERENCES space(id)   ON DELETE CASCADE,
  PRIMARY KEY (project_id, space_id)
);
CREATE INDEX ix_project_space_space ON project_space (space_id);
```

`comment` is likewise absent — inline comments are post-v1, and the anchoring
model (`doc_id`, heading path, block index, block hash) should be settled by
building the review UI before it is committed to a schema.
**The meta-project is not a row.** `~owner/+everything` resolves to an implicit
"all spaces" filter, and the name is reserved so nothing can create a row that
shadows it. A stored `+everything` would need a **sync job** appending every
newly created space — which the Project section explicitly rules out — and that
job's single failure mode is a meta-project silently missing a space, which is
invisible. It also must not be renameable or deletable: a corpus-wide view is not
an object with a lifecycle. Consequence to remember: a project listing cannot
show the meta-project, and any UI must add it by hand.

**Empty-means-nothing, and the polarity trap that follows.** A freshly created
project has no members and must select **nothing**; the corpus-wide filter is a
distinct `All` flag, not an empty list. This is the opposite polarity from the
search layer, where an empty space list means *every* space:

> **Wiring a project to a search query by passing its member list straight
> through inverts the meaning of an empty project — from "nothing" to "the entire
> corpus".** A silent scope inversion, invisible in any test that only uses
> non-empty projects.

Documented on both types today; the durable fix is for the query to take the
filter type itself rather than a bare slice, so the mistake stops being
expressible. That change is deferred only because the read surfaces were being
built against the current signature at the time.

The meta-project is **corpus-wide, not viewer-scoped**. With no visibility levels
and no ACL rows in v1 there is no "spaces the caller may read" to scope it to;
that phrase implies machinery that does not exist.

`comment` remains absent — inline comments are post-v1, and the anchoring model
(`doc_id`, heading path, block index, block hash) should be settled by building
the review UI before it is committed to a schema.

## Implementation plan



@@ 1259,9 1301,10 @@ degrading, which is the point of listing them.
- **Naming.** `spec.sr.ht` / `spec.srht.bigb.es` follows the
  named-by-function pattern of the two siblings. `docs.sr.ht` collides
  conceptually with upstream `man.sr.ht`.
- **Project URL namespace.** `~user/+project` distinguishes projects from spaces
  (`~user/space`) in one character; alternatives are `/projects/~user/name` or
  reusing hub.sr.ht's own namespace.
- ~~**Project URL namespace.**~~ **Settled: `~user/+project`**, one character
  distinguishing a project from a space (`~user/space`), with `+everything`
  reserved for the meta-project. Isolated in the ref parser, so changing it later
  is one function.
- **Port.** compare.sr.ht is on 5090, dolt.sr.ht on 5306–5308. 5091 is free and
  is what the config block above assumes.
- **MCP transport.** Streamable HTTP on the same chi router (`/mcp`) keeps it to