Skip to content

docs: pass the data-api docs to the docs standard - #89

Merged
Upd4ting merged 3 commits into
campaign/integrationfrom
docs/w4-pass
Nov 3, 2026
Merged

Upd4ting merged 3 commits into
campaign/integrationfrom
docs/w4-pass

Conversation

@Upd4ting

@Upd4ting Upd4ting commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Why

Goal: finish the AntelopeJS docs rewrite. Every TypeScript example compiles against its repo's own source, hand-written version prose is gone, and the W1-W5 docs standard ships. This is the W4 pass for interface-data-api (outer-loop#35, decision 11 item 5). It brings README.md and docs/ to the Docs standard, in the shape of AntelopeJS/dms packages/dms/docs, and turns standard mode on so CI keeps them there.

Summary

 package.json
   "antelopeJs": {
     "test": ..., "skills": [...],
+    "docs": { "standard": true }
   }
 README.md, docs/*.md (12 files)
+  frontmatter title, description, mode (body H1 moved into title)
~  sentence-case headings, no dashes, unslop + technical-writing applied
~  claims checked against src/ and its TSDoc
-  `as ValueProxy<string>` and `as const` in examples
~  the computed aggregate example compiles (it hid a type error behind `fragment`)
+  README badges and the joined list SVG marked illustration

No page was split, merged, renamed or moved, and no section, table row or example was dropped. Each docs page lost only its body H1, which moved into title. Fence counts are unchanged. The first 11.computed.md example is now checked instead of fragment (examples 15 to 16). No screenshots, so no docs/captures.ts.

Modes: introduction tutorial, every other page reference.

Current contract (interface-data-api#86, interface-api#68)

The DataController signature is the real generic one (tableClass: C, def: P, base: Base extends ControllerClass, src/index.ts:54-62). A guard's this is a DataControllerInstance, an object with no string index, so the guard narrows it before reading a controller field (src/internal/controller-types.ts:7). current is RowFields, whose values are unknown (src/index.ts:132-137). No page mentions proto.

Claims corrected against the source

  • new answers the created row with its _id and without write-only fields, not "an array of inserted IDs" (src/index.ts:315-316).
  • A missing table throws Non-existent Database Schema or Unregistered Database Table (src/index.ts:69-75). @RegisterDataController applies each route's parameter decorators, registers a prefix route and the handler. A field needs an access mode from @Access, @Joined or @Computed.
  • The limit default is the route's maxPage, else 10, capped at maxPage or 100 (src/components.ts:169-171, src/index.ts:233-237). Any sortDirection other than desc sorts ascending. A non-sortable key answers Field is not sortable..
  • Delete answers the number of deleted records. Mandatory and validator failures answer a JSON {code, fields} body, other errors text (src/components.ts:473-477,519-521).
  • Edit with index reads through the index but updates by primary key (src/index.ts:340-357). That is a bug, drafted as an issue (below).
  • String route options turn on for any non-empty string, "false" included. filter_<field> is documented. WithOptions merges options of a route it already wrapped.
  • Validators run only for fields present in the body, after the mandatory check. noMandatory still runs them. @Optional clears @Mandatory.
  • @Foreign has a sixth parameter targetMeta?: DataAPIMeta. noForeign is a string on get and a boolean on list.
  • The filter signature shows its real constraints. An unknown operator answers 400, and a value with a colon needs the eq: prefix. useIndex defaults to the index only for a single-field index.
  • Joined fields that the sort reads are resolved after the count, not before. A list mode with a joined field also selects its localKey.
  • @Computed returns a ValueProxy, a Query or a constant. options.default is a FieldValue. The aggregate example now counts with filter and eq, because getAll rejects a row key without a type claim (issue draft below).
  • The one compatibility sentence about MongoDB releases now points at the @antelopejs/mongodb changelog.

Heading slugs

{#id} anchors are not used. joined#joined-vs-foreign became #joined-compared-with-foreign, and computed#computed-vs-joined became #computed-compared-with-joined. No link in this repo, in antelope-web or in the AntelopeJS code search points at either.

Evidence

  • Before (campaign/integration 6293a02, tooling-configs dab0aea checkDocs in standard mode, Vale 3.26.0): files=12 examples=15 failures=163.
  • After (head 081740a): files=12 examples=16 failures=0.
  • docs-measure on 081740a with VALE_BIN set: exit 0.
  • pnpm run checks on 081740a with GITHUB_BASE_REF=campaign/integration: Checks: 8 passed, 0 failed.
  • Rendered anchors: antelope-web campaign/integration a8f40a6, nuxt dev in sites/interfaces with INTERFACES_DOCS_LOCAL=data-api=<clone>. Every page answers 200, pages=11 links=46 anchored=12 broken=0, no page renders {#, and the renamed heading ids render.
  • CI run 41554255955: every job passed or was skipped. The docs step AntelopeJS/tooling-configs/actions/docs@dab0aea28840981138795aca32381e7fa8ac8d1b installed Vale (standard mode on) and printed Docs: 12 files, 61 local links, 1 compatibility tables, 16 checked examples, 0 API pages; 0 failures. The PR is not docs-only in the docs-only job's sense, because the package.json opt-in is part of it. The docs delta allows that file for this change.

Round 2 (W5 r1 FAIL at 081740a)

  • Blocking, fixed. 11.computed.md now says, after the count example, that the filter/eq subquery checks every device per row and does not use the group_id index, and that getAll(row.key("_id"), "group_id") would, but compiles only with a cast today.
  • Blocking, fixed. The read-only claims in 10.joined.md and 11.computed.md now state the real rule. @Joined and @Computed set read-only access when they run, and an @Access written above them runs later and replaces it (setMode, src/metadata.ts:231-240), so do not combine them. Only the non-indexed sort holds whatever the decorator order (setSortable, :289). The comparison tables say "Read-only, unless a later @Access replaces it".
  • Notes, fixed. 7.foreign-keys.md: schema is needed only with a string table name or when the class metadata names no schema (src/internal/table-reference.ts:33-47). 3.routes.md: noPluck skips the database pluck, and the response still holds only the fields readable in list. A pluck mode that no field declares answers 400 (src/index.ts:225-229). The README install line lists every peer interface (package.json peerDependencies).
  • pnpm run checks failed twice on port 5010, which another agent's ajs project run held. With the port free it gives Checks: 8 passed, 0 failed.
  • On 4ce9f79: docs-measure with VALE_BIN set exits 0, the standard lever gives failures=0, and pnpm run checks with GITHUB_BASE_REF=campaign/integration gives Checks: 8 passed, 0 failed.
  • CI run 41556503124 on 4ce9f79: every job passed or was skipped. The docs step actions/docs@dab0aea installed Vale and printed Docs: 12 files, 61 local links, 1 compatibility tables, 16 checked examples, 0 API pages; 0 failures.

Merge danger

Door: two-way. Docs and one package.json key. A revert restores the old pages and turns standard mode off.

Blast radius: docs. From here on, every interface-data-api PR that touches README.md or docs/ must pass Vale and the standard checks in CI.

Attention

  • Issue drafts (not filed): interface-data-api-edit-index-updates-primary-key (Edit with index writes by primary key and answers 200 with the unchanged row) and interface-database-getall-rejects-typed-row-key (getAll's IndexKeys rejects a ValueProxy row key, so the repo's own tests use .cast<...>()).
  • The aggregate example uses filter with eq where the old text used the group_id index through getAll. The count is the same. The page itself now says the filter does not use the index.
  • Not confirmed by running: the Content-Type of validator 400 answers, the 500 for a failing database operation, aggregates yielding null over an empty set, MongoDB's $lookup translation, and null for a missing foreign record on the list path.

@Upd4ting
Upd4ting marked this pull request as ready for review November 3, 2026 00:39
@Upd4ting

Upd4ting commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

W5 review: FAIL at 081740a

Fresh W5 reviewer. I did not write this PR. I pushed nothing.

Blocking

  1. docs/11.computed.md:84-104 (the count example). The page never says the rewritten aggregate no longer uses an index. The example still declares @Index() on devices.group_id (:58), which suggests the count uses it. But the subquery is now filter((device) => device.key("group_id").eq(row.key("_id"))), which runs a filter per row instead of an index lookup. The PR body's Attention section mentions this, but readers of the page never see the PR body. Fix: one sentence after the example. It should say that the filter does not use the group_id index, and that getAll(row.key("_id"), "group_id") would, but getAll rejects a row key today without a type restatement (issue draft interface-database-getall-rejects-typed-row-key).
  2. The read-only claims are false. This affects 11.computed.md:11 ("sets its access mode to read-only ... whatever the decorator order") and the "Read-only (enforced)" cells at 10.joined.md:193 and 11.computed.md:181. The PR rewrote these sentences, so they are changed claims. setComputed and setJoined set field.mode = ReadOnly when they run (src/metadata.ts:312,357). @Access calls setMode, which overwrites field.mode without a guard (src/metadata.ts:231-240, through onField at :507-518, which runs immediately). Decorators apply bottom-up, so an @Access(AccessMode.ReadWrite) written above @Computed or @Joined makes the field writable. Only the non-indexed sort is independent of order (setSortable, :289). Fix: say the decorator sets read-only access, and that a later @Access replaces it, so do not combine them. Keep "whatever the decorator order" for the sort only.

Checked and confirmed

  • DataController signature (interface-data-api#86). The generics, parameters and defaults match src/index.ts:54-62. The return type is shown as Class<ExtractDefCallbacks<P> & { table: InstanceType<C> }> & Base. That inlines the unexported TableHolder<C> faithfully. "Its prototype holds one method per key of def" matches src/index.ts:80-87.

  • The guard's this type. GetGuard declares this: DataControllerInstance (src/index.ts:142-147). DataControllerInstance = InstanceType<ControllerClass>, which is object (src/internal/controller-types.ts:7, ControllerClass<T = object> in interface-api). The type is exported from the package entry. current: RowFields, and its values are unknown (src/internal/field-types.ts:50). The advice to narrow this first is correct.

  • The count example compiles. It is a checked fence (examples 15 to 16, 0 failures).

  • It counts the same thing. I swapped the docs' filter/eq form for getAll(row.key("_id").cast<...>(), "group_id") in src/tests/components/computed.test.ts in a scratch worktree. The full suite then passed on MongoDB through @antelopejs/mongodb 1.4.4 (194 passing). That run covers the 3/1/0 per-group counts, sorting both ways, paging and the eq:0 filter. The row.key("name").concat("!") example also lost its as ValueProxy<string> cast and still compiles.

  • Version prose. A grep found none. The versions in the README are the generated compat block, which this PR does not change. The MongoDB release sentence now points at the adapter's changelog.

  • Other claims. I checked 52 more claims against src/, its TSDoc and tests. They cover:

    • HTTP methods.
    • Missing id., 404 and 400 answers.
    • The limit default and cap.
    • sortDirection.
    • The List response shape.
    • What New and Edit write and answer.
    • WithOptions merging.
    • String options that turn on for any non-empty value.
    • The order of validators and mandatory checks, and their 400 bodies.
    • @Optional.
    • @Foreign's parameters.
    • Filter operators and useIndex.
    • Where joined and computed fields merge.
    • ListPage order.

    All are true apart from the blocking items and the notes. No true claim was deleted.

  • Counts.

    • Before: 6293a02 with standard mode forced on gives 12 files, 15 checked examples, 163 failures.
    • After: the head gives 16 checked examples, 0 failures.
    • docs-measure with VALE_BIN (Vale 3.26.0) exits 0 at this head.
  • Checks and CI. pnpm run checks with GITHUB_BASE_REF=campaign/integration gives Checks: 8 passed, 0 failed. Two earlier runs failed only because another agent's process held port 5010. CI run 41554255955 is green.

  • Render. I rendered with antelope-web campaign/integration a8f40a6 and INTERFACES_DOCS_LOCAL. The crawl found 12 pages, 489 links, 284 anchored links and 0 broken. No page renders {#. #joined-compared-with-foreign and #computed-compared-with-joined render.

Notes (non-blocking)

  • 7.foreign-keys.md:268. "schema is then needed only to override that schema" contradicts the next sentence. A class schema that differs from schema throws (table-reference.ts:239-247), so nothing can be overridden. This wording was there before the PR.
  • 3.routes.md:254. noPluck returns every field readable in list, not "all database fields". The response still goes through ReadProperties.
  • 3.routes.md:255. A mode declared only through @Listable(false, "m") exists as an empty set and gives no 400.
  • README.md:33. The install line leaves out peers that the docs import, such as interface-api-util.

Could not confirm

The behaviour of an empty-set aggregate (null), the BSON sort order, MongoDB's $lookup translation, the Content-Type of validator 400 answers, and null for invalid or multi-item foreign keys on the list path.

@Upd4ting

Upd4ting commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

W5 review: #89, round 2

PASS+NOTES at head 4ce9f79 (base campaign/integration 6293a02). Fresh W5 reviewer. I did not write this PR. I pushed nothing.

Goal (Antony, 2026-10-30): finish the AntelopeJS docs rewrite on campaign/integration. Every TypeScript example compiles against its repo's own source, hand-written version prose is gone, and the W1-W5 docs standard ships.

Round-1 blocker 1 (index loss): fixed and true

docs/11.computed.md:104 now says the filter checks every device for each row and does not use the group_id index, and that getAll(row.key("_id"), "group_id") would use it but compiles only with a cast.

My own tsc probe against src, typed as ComputedExpression:

  • getAll(row.key("_id"), "group_id") fails with TS2345: Argument of type 'ValueProxy<string | number | boolean | Date | FieldValue[] | FieldValues>' is not assignable to parameter of type 'IndexKeys'.
  • getAll(row.key("_id").cast<string | number | boolean>(), "group_id") compiles (the form src/tests/components/computed.test.ts:140 uses).
  • The docs' filter((d) => d.key("group_id").eq(row.key("_id"))) compiles.

Round-1 blocker 2 (read-only rule): fixed and true

10.joined.md:11, 11.computed.md:11 and both comparison tables now say the decorator sets read-only access when it runs, that an @Access written above it runs later and replaces the mode, and that the non-indexed sort holds whatever the order. setMode (src/metadata.ts:231-240) assigns field.mode with no guard. setComputed/setJoined set ReadOnly. setSortable checks field.joined/field.computed.

My own runtime decorator-order probe (compiled from src, run with node, then removed), reading GetMetadata(P, DataAPIMeta).fields:

Field mode sortable
@Access(ReadWrite) above @Computed ReadWrite -
@Access(ReadWrite) below @Computed ReadOnly -
@Access(ReadWrite) above @Joined ReadWrite -
@Access(ReadWrite) below @Joined ReadOnly -
@Sortable() above or below @Computed ReadOnly {"indexed":false}
@Sortable() above or below @Joined ReadOnly {"indexed":false}

Every row matches the new text.

Round-1 notes: all fixed

  • 7.foreign-keys.md:268 no longer says schema overrides the class schema. It is now needed only with a string table name or when the class metadata names no schema, which matches resolveTableReference.
  • 3.routes.md noPluck now says the response still holds only the fields readable in list (Query.ReadProperties(..., "list") in src/index.ts).
  • pluckMode: "A mode that no field declares answers 400" matches the assert(params.noPluck || pluck, 400, ...).
  • README.md install line now lists all five peerDependencies.

Notes (non-blocking)

  • "does not use the group_id index" is adapter behaviour. The interface does not state it, and no adapter is installed in this clone, so I could not confirm it.
  • In 7.foreign-keys.md, "when the class metadata names no schema" cannot happen through @RegisterTable, which takes both names. The sentence is not false.

Gates

  • docs-measure with VALE_BIN (Vale 3.26.0), --dir on a clean clone: exit 0, Docs: 12 files, 61 local links, 1 compatibility tables, 16 checked examples, 0 API pages; 0 failures.
  • No {#id}. No as cast or suppression in fences.
  • pnpm run checks with GITHUB_BASE_REF=campaign/integration: Checks: 8 passed, 0 failed.
  • CI run 41556503124 is for this head and succeeded, docs step actions/docs@dab0aea with 0 failures. gh pr checks: 5 pass, 2 skipping.
  • The r2 diff stays in README.md and docs/. Commit message has no attribution.

gate-verdict: PASS+NOTES 4ce9f79

@Upd4ting
Upd4ting merged commit c9695f5 into campaign/integration Nov 3, 2026
7 checks passed
@Upd4ting
Upd4ting deleted the docs/w4-pass branch November 3, 2026 02:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant