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
11 changes: 11 additions & 0 deletions .changeset/spec-data-provenance-anchors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@objectstack/spec': patch
---

Provenance comments in `data/` were re-anchored

Comment and docblock lines under `src/data` (all but the files other open work
holds) that cited tracker numbers which no longer resolve on GitHub now cite
the commit in this repository's history that decided the matter, and say in
their own words what was decided. Comments only: no type, schema, export or
runtime behaviour changes.
4 changes: 2 additions & 2 deletions content/docs/references/data/feed.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ enums here configure the record activity component (`RecordActivityProps` in
`FeedItemType` is not backend-free: it has no backend *import*, yet it is the
TARGET of the map UI consumers apply to the `sys_activity.type` column — a
backend *coupling* — and that column's vocabulary is OPEN and
author-extensible (maintainer ruling 2026-08-24, #11507). `FeedItemType` is
author-extensible (maintainer ruling 2026-08-24, commit 88b9d749a). `FeedItemType` is
therefore the built-in guidance half of that map, never the value domain of
an authoring surface: `RecordActivityProps.types` accepts contributed kinds
beyond it (#11658), and consumers must map unknown `sys_activity.type` values
beyond it (commit 1a6a19c31), and consumers must map unknown `sys_activity.type` values
to a fallback rather than drop them. `SYS_ACTIVITY_BUILTIN_TYPES` below is
the published built-in vocabulary of the `sys_activity.type` column,
co-located with `FeedItemType` because UI consumers map one onto the other.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* conformance suite — every boolean case `AGGREGATION_CASES` requires a
* backend to ANSWER (#11152) must be a pair this table accepts, so the two
* tables in this package cannot contradict each other on the boolean axis
* (#16685) — the cross-pin reaches exactly as far as the `flag` cases.
* (commit ed7243d52) — the cross-pin reaches exactly as far as the `flag` cases.
*/

import { describe, it, expect } from 'vitest';
Expand Down Expand Up @@ -140,7 +140,7 @@ describe('isAggregateCompatibleWithFieldType — the pairs the card is about', (
// `AGGREGATION_ROWS.flag` is the boolean aggregand (declared `type:
// 'boolean'` by every harness); each case over it is a pair #11152 pins
// on six backends. A table refusing one of them would refuse a pair the
// spec elsewhere REQUIRES an answer to (#16685).
// spec elsewhere REQUIRES an answer to (commit ed7243d52).
const booleanCases = AGGREGATION_CASES.filter((c) => c.field === 'flag');
expect(sorted(new Set(booleanCases.map((c) => c.function)))).toEqual(sorted(AggregationFunction.options));
for (const c of booleanCases) {
Expand Down
6 changes: 3 additions & 3 deletions packages/spec/src/data/aggregate-field-type-compatibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Aggregate × field-type compatibility — the ONE table saying which
* `AggregationFunction` may be applied to a field of which `FieldType`
* (#16353; director ruling, decision batch #59, 2026-09-06: "both legs, table
* in spec"; the boolean rows by decision batch #80, 2026-09-08, #16685 — see
* in spec"; the boolean rows by decision batch #80, 2026-09-08, commit ed7243d52 — see
* below). A `DatasetMeasure` pairs an `aggregate` with a `field`; this
* table is the contract both consumer legs execute — the compile-time refusal
* in the dataset compiler (#16099) and the authoring-time lint rule — so the
Expand Down Expand Up @@ -65,11 +65,11 @@
* aggregand to `int` on Postgres so that the one dialect storing a real
* `boolean` column answers the same numbers (#11635). Batch #59's "every
* other pair: refused" never named booleans — it was a blanket default —
* and the director ruling of decision batch #80 (2026-09-08, #16685,
* and the director ruling of decision batch #80 (2026-09-08, commit ed7243d52,
* maintainer verbatim 「其他同意」, option A) holds that the specific ruling
* #11152 stands over that default: the four rows carry both members and
* nothing else moves. `avg(flag)` is the win-rate / SLA-violation-rate
* shape (#11065) — the reason `AGGREGATION_CASES` exists — so a table that
* shape (commit 20950404c) — the reason `AGGREGATION_CASES` exists — so a table that
* refused it would refuse a pair every backend is REQUIRED to answer.
* - **everything else** — the text family, option types, references, files,
* structured JSON, `vector`, and the computed `formula` / `autonumber` — is
Expand Down
6 changes: 3 additions & 3 deletions packages/spec/src/data/aggregation-conformance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
* verbatim 「12745 A回,其他同意。」, superseding #11249's `false`/`true`)
* pins that **booleans aggregate as numbers on every face, with no
* per-aggregate exception** — `min(flag)`/`max(flag)` answer `0`/`1`, the
* same numeric domain `sum`/`avg` already answer in (#11065). So a boolean
* same numeric domain `sum`/`avg` already answer in (commit 20950404c). So a boolean
* aggregand takes NO boolean read-presentation on any face, and
* {@link AggregationExpectation.value} stays a `number` for every case.
*
Expand Down Expand Up @@ -224,7 +224,7 @@ export interface AggregationRow {
/**
* [#11152] The non-null BOOLEAN aggregand — 3 true / 3 false, so `sum` and
* `avg` cannot agree with a face that dropped the booleans (`0` / `null`,
* the #11065/#11151 defect) or that counted rows instead of trues.
* the commit 20950404c / #11151 defect) or that counted rows instead of trues.
*
* The distribution is the `FLAG_BY_ID` the #11635 suite landed, adopted here
* verbatim so the two never disagree on grouped values: `west` holds
Expand Down Expand Up @@ -383,7 +383,7 @@ export const AGGREGATION_CASES: readonly AggregationCase[] = [
// ── [#11152] the boolean aggregand: numbers on every face, by ruling ──────
//
// The whole vocabulary over `flag` (3 true / 3 false). Two rulings pin the
// values: #11065 settled `sum`/`avg` (a boolean is an aggregand worth 1 or
// values: commit 20950404c settled `sum`/`avg` (a boolean is an aggregand worth 1 or
// 0 — driver-memory answered `0`/`null` while SQLite answered `2`/`0.4`,
// found from an application because no conformance cell could see it), and
// #11152 (maintainer 2026-08-28, superseding #11249's `false`/`true`)
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/src/data/api-derivation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ describe('api-derivation (#3391)', () => {
expect(DATA_ACTION_TO_API_OPERATION.bulk).toBe('bulk');
});

// [#6259] `batch: 'bulk'` was a producer-less row: `callData` has had no
// [commit 6968885ef] `batch: 'bulk'` was a producer-less row: `callData` has had no
// `batch` arm since #5856, and REST gates `/batch` on the literal `'bulk'`.
// Two pins, because the finding had two halves — the row AND the prose
// that told readers `batch` was a live runtime action.
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/src/data/api-derivation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export const API_METHOD_DERIVATION: Record<LegacyApiMethod, DerivationRule> = {
* `apiMethods`).
*
* [#6259] The `batch: 'bulk'` row was removed, and the line above no longer
* calls `batch` a runtime `callData` action. It was the one entry with no
* calls `batch` a runtime `callData` action (both by commit 6968885ef). It was the one entry with no
* producer on either side: `callData` branches on a closed set that has not
* contained `batch` since that arm was retired (#5856), and every REST caller
* of `apiAccessDenialFromEnable` passes a canonical literal — including the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const SINGLE_RECORD_WRITE_ONLY: Record<string, string> = {
// `00d3f09c5` is the one that caught the previous record's OWN grid anchor as
// wrong rather than merely shifted: `3790-3805` there is
// `runBulkActionAggregate` and says nothing about selection. That is the
// #10274 class, and the reason a citation refresh re-READS instead of moving
// class commit d1ba685ec gates, and the reason a citation refresh re-READS instead of moving
// numbers — arithmetic on a wrong anchor produces a fresh-looking span still
// describing the wrong function. The second claim,
// `hooks/useBulkExecutor.ts:298-303`, sits in a file that is byte-identical
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ describe('write-door alignment: redactUrlPassword removes exactly what urlUserin
// `urlUserinfoUsername` shares the password half's boundary parse by
// construction; this pins the redactor to the same grammar from the other
// side: stripping the password must never move or rewrite the username the
// #8696 injection path will read off the redacted/stored row.
// commit 90a12fb18's injection path will read off the redacted/stored row.
for (const url of [...CARRYING, ...CREDENTIAL_FREE]) {
expect(urlUserinfoUsername(redactUrlPassword(url)), url).toBe(urlUserinfoUsername(url));
}
Expand Down Expand Up @@ -412,7 +412,7 @@ describe('passthrough secret redaction (#9040) — the nested spellings the key-
keyVaultNamespace: 'encryption.__keyVault',
kmsProviders: {
// The identity halves the client also reads are NOT credential
// material (#8876's asymmetry) and stay served.
// material (commit d634e665b's asymmetry) and stay served.
aws: { accessKeyId: 'AKIAFAKEFAKEFAKEFAKE' },
azure: { tenantId: 'tenant-id', clientId: 'client-id' },
gcp: { email: 'svc@example.iam.gserviceaccount.com' },
Expand Down
16 changes: 8 additions & 8 deletions packages/spec/src/data/datasource-credential-redaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ const STILL_WRITABLE_CREDENTIAL_KEYS: Record<string, readonly string[]> = {

/**
* Secret-bearing paths inside a driver's passthrough `config` slot — the
* FOURTH spelling of the stored credential (#9040).
* FOURTH spelling of the stored credential (commit 24206416a).
*
* Since the nested-position finding this table is a RESIDUE, not the nested
* judgment: the credential-name scrub runs at every object depth (see
Expand All @@ -150,16 +150,16 @@ const STILL_WRITABLE_CREDENTIAL_KEYS: Record<string, readonly string[]> = {
* Only mongo declares a passthrough today (`options`, spread verbatim into
* `MongoClientOptions`); postgres/mysql/turso/sqlite/memory have closed
* strict-object contracts with no client-bound record slot (measured for
* #9040 — memory's `initialData` is seed DATA, deliberately not judged here:
* commit 24206416a — memory's `initialData` is seed DATA, deliberately not judged here:
* redacting a seeded row's own `password` FIELD would corrupt data the driver
* serves, which is not this module's question). Every path is measured against
* `mongodb@7.5.0`, the client the driver spreads `options` into:
*
* - `options.auth.password` — resolved into `MongoCredentials`; the login
* secret itself, and the one path the WRITE door also refuses
* (`MONGO_OPTIONS_CREDENTIAL_PATHS` in `driver/common.zod.ts`; #8696
* (`MONGO_OPTIONS_CREDENTIAL_PATHS` in `driver/common.zod.ts`; commit 90a12fb18
* measured a bound secret outranking it at connect). `auth.username` is
* deliberately not here — a username is not credential material (#8876).
* deliberately not here — a username is not credential material (commit d634e665b).
* - `options.proxyPassword` — SOCKS5 proxy password, honoured
* (`c.options.proxyPassword`, measured).
* - `options.tlsCertificateKeyFilePassword`, `options.key`,
Expand Down Expand Up @@ -189,7 +189,7 @@ const STILL_WRITABLE_CREDENTIAL_KEYS: Record<string, readonly string[]> = {
* one slot is the login password, the proxyPassword posture — but never
* SERVED. The same families' identity halves (`aws.accessKeyId`,
* `azure.tenantId` / `clientId`, `gcp.email`) are read by the client too
* but are not credential material (#8876's asymmetry), and the unmeasured
* but are not credential material (commit d634e665b's asymmetry), and the unmeasured
* neighbours (`kmip.endpoint`, `keyVaultNamespace`, `schemaMap`) mirror no
* credential spelling — deliberately not here: entries land on this table
* with a measurement quoted, never by name-shape.
Expand Down Expand Up @@ -218,7 +218,7 @@ const PASSTHROUGH_SECRET_PATHS: Readonly<Record<string, readonly (readonly strin
* The nested config paths this module hides for `driver`, dotted-path-ready —
* the passthrough sibling of {@link redactableConfigKeys}, exported so the
* write-path inverse (`service-datasource`'s `restoreRedactedConfig`) mirrors
* exactly the set the read path hides (#9040): a nested redaction the restore
* exactly the set the read path hides (commit 24206416a): a nested redaction the restore
* side did not mirror would turn an untouched "Save" on an affected legacy row
* into silent credential deletion.
*/
Expand All @@ -229,7 +229,7 @@ export function passthroughSecretPaths(driver: unknown): readonly (readonly stri

/**
* The config-relative subset of {@link passthroughSecretPaths} the WRITE door
* also refuses (#9040) — today `options.auth.password` on mongo, projected
* also refuses (commit 24206416a) — today `options.auth.password` on mongo, projected
* from the write door's own closed list (`MONGO_OPTIONS_CREDENTIAL_PATHS`) so
* the two doors cannot drift. What the credential-migration planner consults:
* a stored row carrying one of these holds a LIVE login credential the binder
Expand Down Expand Up @@ -492,7 +492,7 @@ export interface RedactedDatasourceConfig {
* shapes inside a driver contract whose leaf is `z.never()`. None exist
* today; the walk is what keeps "reading the schema is reading the
* refusal list" true at depth the day one lands.
* 4. The passthrough spellings (#9040, {@link passthroughSecretPaths}): the
* 4. The passthrough spellings (commit 24206416a, {@link passthroughSecretPaths}): the
* CLIENT-MEASURED secret names (`proxyPassword`, `key`, `passphrase`, …)
* that mirror no top-level key, so neither the schema nor the name set can
* derive them. The table is the residue for exactly that class — an entry
Expand Down
32 changes: 16 additions & 16 deletions packages/spec/src/data/datasource.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,11 @@ export type ExternalDatasourceSettingsParsed = z.infer<typeof ExternalDatasource

/**
* Refusal for the contradictory pair "`external.credentialsRef` bound + a
* mongo `config.url` whose userinfo names NO user" (#9041) — the "absence must
* be loud" half of the #8696 family, refused at the one door that sees both
* halves at once.
* mongo `config.url` whose userinfo names NO user" (commit d491625c1) — the
* "absence must be loud" half of commit 90a12fb18's bound-secret injection,
* refused at the one door that sees both halves at once.
*
* Why the pair cannot work as written, all measured (on the #9041 card and
* Why the pair cannot work as written, all measured (for commit d491625c1 and
* re-verified against `buildMongoAuth` in service-datasource's driver
* factory): `MongoClient` credentials need a username as well as a password,
* and with `url` present the discrete `username` field is ignored
Expand All @@ -349,15 +349,15 @@ export type ExternalDatasourceSettingsParsed = z.infer<typeof ExternalDatasource
* into a guaranteed handshake failure. And refusing at CONNECT would
* contradict `MongoConfigSchema.url`'s published contract ("bind the secret …
* and it is injected at connect time") while planting a per-branch asymmetry
* inside the factory — the defect class #8696 closed. Hence this door.
* inside the factory — the defect class commit 90a12fb18 closed for mongo. Hence this door.
*
* Scope fences, each deliberate (#9041's triage, adopted verbatim):
* Scope fences, each deliberate (the triage's, as commit d491625c1 landed them):
*
* - **mongo arm ONLY** (judged through {@link resolveDriverId}, so a stored
* legacy `driver: 'mongo'` row is judged identically to `'mongodb'` — the
* same alias mechanism the #9040 read-path redaction uses). The postgres
* same alias mechanism commit 24206416a's read-path redaction uses). The postgres
* arm injects on a user-less DSN by a different, measured mechanism
* (#8873: `pg` sends a password only when the server asks) and is NOT
* (commit 096106522: `pg` sends a password only when the server asks) and is NOT
* assumed to share this defect.
* - **"names no user" means {@link urlUserinfoUsername} answers
* `undefined`** — no userinfo at all. The present-but-empty forms
Expand All @@ -372,7 +372,7 @@ export type ExternalDatasourceSettingsParsed = z.infer<typeof ExternalDatasource
* #9147 — see {@link CREDENTIALS_REF_MONGO_NO_USERNAME_REFUSED}. It is a
* separate message because the remedy differs: there the discrete
* `username` field is live, so the fix is `config.username`, not the URL's
* userinfo. #9041 fenced it out; #9147 widened the same refinement into it.
* userinfo. Commit d491625c1 fenced it out; #9147 widened the same refinement into it.
*/
const CREDENTIALS_REF_MONGO_URL_NO_USER_REFUSED =
'this mongo `config.url` names no user in its userinfo while `external.credentialsRef` binds '
Expand All @@ -394,7 +394,7 @@ const CREDENTIALS_REF_MONGO_URL_NO_USER_REFUSED =
* bound while the mongo `config` authors no `url` AND names no `username`.
*
* Same defect, one branch over, and the branches were measured to agree on this
* input before either was refused — which is why this inherits #9041's ruling
* input before either was refused — which is why this inherits commit d491625c1's ruling
* rather than re-opening it (the standing meta-rule: a sibling spelling of an
* already-ruled silent discard defaults into the existing refusal set).
*
Expand All @@ -416,7 +416,7 @@ const CREDENTIALS_REF_MONGO_URL_NO_USER_REFUSED =
* same measured asymmetry that made the URL branch's refusal the right answer
* rather than an unconditional injection.
*
* ## Why a SEPARATE message, and not #9041's
* ## Why a SEPARATE message, and not commit d491625c1's
*
* The remedy differs, and a refusal naming a remedy that does not apply is
* worse than no refusal — the failure mode this module's own history section
Expand All @@ -430,8 +430,8 @@ const CREDENTIALS_REF_MONGO_URL_NO_USER_REFUSED =
* ## Scope fences
*
* - **mongo arm ONLY**, judged through {@link resolveDriverId} — identical to
* #9041's fence, so a stored legacy `driver: 'mongo'` row is judged the
* same. The postgres arm is NOT widened to (#8873 measured `pg` receiving
* commit d491625c1's fence, so a stored legacy `driver: 'mongo'` row is judged the
* same. The postgres arm is NOT widened to (commit 096106522 measured `pg` receiving
* the bound password regardless of the DSN naming a user), and neither is
* any other driver.
* - **"names no username" is `undefined` or `''`** — the two spellings that
Expand All @@ -442,14 +442,14 @@ const CREDENTIALS_REF_MONGO_URL_NO_USER_REFUSED =
* would leave this refusal prescribing `config.username` while the platform
* still accepted the one spelling of `config.username` that keeps the
* binding silent — the prescription must land somewhere enforced. Note the
* deliberate asymmetry with #9041's fence, which DOES exclude its
* deliberate asymmetry with commit d491625c1's fence, which DOES exclude its
* present-but-empty forms: there `MongoClient` itself throws on them
* (`URI contained empty userinfo section`), so only the `undefined` case is
* silent. Here nothing throws — `username: ''` connects, anonymously — so
* the silent set is the falsy set. Each fence follows the measurement on
* its own branch rather than the other branch's shape.
* - **A non-string `username` is the config gate's finding, not this one** —
* same posture as #9041 takes toward a non-string `url`.
* same posture as commit d491625c1 takes toward a non-string `url`.
* - **"bound" mirrors the connect path's truthy check**, exactly as above: an
* empty-string `credentialsRef` is not a binding.
*/
Expand Down Expand Up @@ -692,7 +692,7 @@ export const DatasourceSchema = lazySchema(() => strictObject(
// author's trust on a slot that cannot pay it back.
reportDriverConfigIssues(ctx, ds.driver, ds.config, ['config']);

// #9041 (url branch) + #9147 (composed branch) — see
// commit d491625c1 (url branch) + #9147 (composed branch) — see
// CREDENTIALS_REF_MONGO_URL_NO_USER_REFUSED and
// CREDENTIALS_REF_MONGO_NO_USERNAME_REFUSED. Neither can live in
// `MongoConfigSchema` (a config-level refinement sees only `config`;
Expand Down
Loading
Loading