diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1edde77..b086250 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -323,9 +323,10 @@ jobs: public: # BLOCKING, and a pure function of the diff. This repository is public; a # pointer into the private core repository is a dead end for every reader - # who is not us. The rules are two literal strings with no allowlist, so - # there is nothing to quietly grow — see scripts/lint-public.sh for why it - # exists and what to write instead. + # who is not us, and so is a path that is not in this repository at all. + # lint-public's rules are two literal strings with no allowlist; lint-paths + # resolves every cited repository path and fails on the ones that are not + # there. Each script's header says why it exists and what to write instead. name: "public — no pointers into the private repository" runs-on: ubuntu-latest timeout-minutes: 5 @@ -336,6 +337,14 @@ jobs: # to prevent, so prove both rules fire before trusting either verdict. - run: scripts/lint-public.sh --selftest - run: scripts/lint-public.sh + # The other half of the same failure. lint-public catches the private + # repository by NAME; lint-paths catches a citation of a path that is + # simply not here — which is how one proposals document that this + # repository does not contain was cited six times, from the C header and + # all four bindings' source, every one of which ships inside a published + # package. Selftest first, same reason. + - run: scripts/lint-paths.sh --selftest + - run: scripts/lint-paths.sh prose: # BLOCKING. It was report-only while the tree was still hard-wrapped, which diff --git a/docs/guides/batches.md b/docs/guides/batches.md index 34a7243..660e824 100644 --- a/docs/guides/batches.md +++ b/docs/guides/batches.md @@ -158,7 +158,7 @@ let batch = schema.rows_export( Three payload states, and they are distinct: absent means no export was requested, it was declined (the reason is in `export_declined`), or a call-level verdict preempted it; present-but-empty means the export ran and emitted nothing. The bytes are copied out of the C buffer and freed before the call returns, so no ownership crosses the boundary. -**Document flags** thin the _description_ without ever changing the _verdict_. Passing an export format defaults them to lean — verdicts only — because the usual reason to export is to forward bytes rather than to read a report. Ask for `DOC_VALUES`, `DOC_TRANSFORMS` or `DOC_DEFAULTS` back explicitly if you want them. A plain `rows` call is the all-flags spelling and stays byte-identical to what it always returned. +**Document flags** thin the _description_ without ever changing the _verdict_. Passing an export format defaults them to lean — verdicts only — because the usual reason to export is to forward bytes rather than to read a report. Ask for the values, transforms or defaults back explicitly if you want them — `DocValues` / `DocTransforms` / `DocDefaults` in Go, `DOC_VALUES` / `DOC_TRANSFORMS` / `DOC_DEFAULTS` in Python and TypeScript, `DocFlags::VALUES` / `DocFlags::TRANSFORMS` / `DocFlags::DEFAULTS` in Rust. A plain `rows` call is the all-flags spelling and stays byte-identical to what it always returned. ## Next diff --git a/docs/reference/bindings.md b/docs/reference/bindings.md index 5d5dd66..fb72c2f 100644 --- a/docs/reference/bindings.md +++ b/docs/reference/bindings.md @@ -50,7 +50,7 @@ A binding MAY additionally expose the statically linked, single-version shape (t At ABI revision 3 the C `chs_rows` gained `export_format`, `doc_flags` and `out_bytes`, and `chs_filter_compile` / `chs_filter_free` / `chs_filter_rows` joined the surface (§Filters). What that means for a binding: - **`Rows()` keeps today's behavior exactly**: one `chs_rows` call with `export_format = CHS_EXPORT_NONE` (`-1`) and `doc_flags = CHS_DOC_ALL` (`7`), `out_bytes = NULL`. The document that comes back is byte-identical to revision 2's, so nothing downstream moves. -- **`RowsExport()`** (per `docs/proposals/rows-export.md`: flags 0 by default, the bitmask exposed, `Payload []byte` + `Spans` from `out_bytes` + `row_spans`) and the filter SDK surface (`CompileFilter` / verdict types) were specified for the SDK cycle that FOLLOWS the C surface — this revision's bindings changes were the mechanical pass-through above and the hand-kept `ABI_REVISION` mirrors bumping to 3 in the same cycle, nothing more. One C call per SDK method, always; never a second call, never re-parsing. **That follow-on cycle has landed**, in all four bindings, at some point before the repository split squashed its history: the export channel and the filter surface are in the object-model table above and `tests/parity/manifest.json` enforces both. This paragraph is kept as the statement of the sequencing rule — the C surface first, the SDK surface in the cycle after — not as a description of what is missing. +- **`RowsExport()`** (flags 0 by default, the bitmask exposed, `Payload []byte` + `Spans` from `out_bytes` + `row_spans`) and the filter SDK surface (`CompileFilter` / verdict types) were specified for the SDK cycle that FOLLOWS the C surface — this revision's bindings changes were the mechanical pass-through above and the hand-kept `ABI_REVISION` mirrors bumping to 3 in the same cycle, nothing more. One C call per SDK method, always; never a second call, never re-parsing. **That follow-on cycle has landed**, in all four bindings, at some point before the repository split squashed its history: the export channel and the filter surface are in the object-model table above and `tests/parity/manifest.json` enforces both. This paragraph is kept as the statement of the sequencing rule — the C surface first, the SDK surface in the cycle after — not as a description of what is missing. - **Lean documents stay SDK-derivable.** Under `CHS_DOC_TRANSFORMS` without `CHS_DOC_VALUES` the C layer retains every `cols[]` entry any spec'd detector could fire on, with the full field set (the conservative byte-equality retention rule. A binding runs the SAME detectors of §Transformed over the retained entries — no new classifier exists on either side, and the reason vocabulary does not move into C. Under `CHS_DOC_VALUES` without `CHS_DOC_TRANSFORMS` the reference parse is skipped C-side (`ref` is `null`, no `wire`), so detectors 2 and 3 have nothing to run on — that is the caller's explicit choice, not data loss. - **Filter verdicts map to a four-state type, and two of the states are fail-closed.** `'t'`/`'f'` are answers; `'e'` (the predicate threw on this row — the server would have failed the whole query) and `'d'` (this library declines) are NOT answers, and a caller enforcing visibility MUST hide the row / fail the request on both. A binding MUST NOT collapse `'e'` or `'d'` into `false`-the-answer: under `NOT`, a decline-read-as-false inverts fail-closed into fail-open — the measured leak class. Unknown verdict characters degrade to the decline state, mirroring the unknown- `outcome` rule below. **No SDK may offer filter-backed read-side enforcement until the WHERE-truth rig gates green**; until then the surface is shadow/replay. - A filter handle wraps BOTH pointers' lifetimes: the SDK object MUST keep its schema object alive (a reference, not a copy) and free the filter before the schema — the C layer does not refcount. diff --git a/go/chtypes/chtypes.go b/go/chtypes/chtypes.go index 964938b..c992f31 100644 --- a/go/chtypes/chtypes.go +++ b/go/chtypes/chtypes.go @@ -291,7 +291,7 @@ const ( // vendored ISerialization::deserializeBinary — the reader // BinaryRowInputFormat uses. Framing faults are one code (33), batches // are all-or-nothing, and input_format_allow_errors_* never applies - // (docs/type-coverage.md §11). Requires an artifact built at or after the + // Requires an artifact built at or after the // RowBinary exposure; older artifacts reject with "unknown format". RowBinary // RowBinaryWithDefaults adds the measured per-column marker byte: any @@ -526,7 +526,7 @@ type RowResult struct { // ALIAS is deliberately absent. It is computable by exactly the same // machinery, and it is measured that `ALTER ... MODIFY COLUMN a ALIAS ` RETROACTIVELY CHANGES what already-inserted rows read back as - // (docs/defaults-matrix.md §6.5). An ALIAS is therefore a fact about + // An ALIAS is therefore a fact about // the schema at read time, not about the row, and this library will not // present one as a stored value. A caller that wants to show it must ask // the server, and must label it computed-at-read. diff --git a/go/chtypes/linked.go b/go/chtypes/linked.go index c3055f7..e48ea67 100644 --- a/go/chtypes/linked.go +++ b/go/chtypes/linked.go @@ -23,8 +23,8 @@ package chtypes // carries a copy it asserts identical at build time. WHERE the library is // comes from outside: this file names only `-lchtypes`, and the caller that // wants the linked path supplies the search path and a build-tree rpath -// through CGO_LDFLAGS (the core repo's ci/steps/_lib.sh `linked_env` sets -// them to its lib/build; `CHTYPES_LIB_BUILD` names the same directory for +// through CGO_LDFLAGS (the core repository's build tooling sets them to +// its build tree; `CHTYPES_LIB_BUILD` names the same directory for // unsafe_families.txt discovery). A shipped binary needs an rpath relative // to itself, or the RUNPATH points at the builder's filesystem and the // library is unfindable in a runtime image: Linux is the shipping target, @@ -840,7 +840,8 @@ func (cs *CompiledSchema) Rows(format Format, body []byte, settings map[string]s // RowsExport is Rows with the revision-3 export and document-flag channels // exposed: ONE chs_rows call, never a second, never re-parsing -// (docs/proposals/rows-export.md; the C ABI contract §Rows is normative). +// (the C ABI contract §Rows is normative; include/chtypes.h is its +// public authority). // // exportFormat is ExportNone (no bytes; the docFlags still thin the // document) or a Format this artifact can SERIALIZE — this revision exactly diff --git a/go/chtypes/multiversion.go b/go/chtypes/multiversion.go index f64c349..91c3fd6 100644 --- a/go/chtypes/multiversion.go +++ b/go/chtypes/multiversion.go @@ -337,7 +337,7 @@ import ( // into a TSan-compiled stress driver that mirrors these tests' workload // (steady distinct handles + compile/engine churn + validate, DEFAULT/TTL // evaluation included). Its runs are TSan-clean with ZERO suppressions -// (tests/tsan/RESULTS.md — the run of record: ~33 M concurrent calls, 0 +// (the run of record, in the core repository: ~33 M concurrent calls, 0 // reports, 0 mismatches). SCOPE, stated rather than implied: one version // (25.8, the reference line), one platform (darwin-arm64), that workload, // and the executed interleavings — other vendored lines share this wrapper @@ -535,7 +535,7 @@ func openLibrary(path string) (*Library, error) { // Key on the RESOLVED path, not the spelling: dlopen refcounts one image // per file, so `./x/libchtypes.so` and its absolute spelling — or a // legacy-name symlink like the libchtypes.so -> libchtypes_s1.so bridge - // ci/steps/stage-lib-build.sh stages — are the SAME image, and missing + // the core repository's build staging creates — are the SAME image, and missing // the map here would run chs_init a second time on live state (the "AT // MOST ONCE PER PROCESS" invariant below). key := path diff --git a/go/chtypes/multiversion_test.go b/go/chtypes/multiversion_test.go index 0f558ab..16f2936 100644 --- a/go/chtypes/multiversion_test.go +++ b/go/chtypes/multiversion_test.go @@ -85,7 +85,7 @@ func testRegistryDir(t *testing.T) string { // skipNoArtifacts is the one skip every registry test lands on when the // registry it was pointed at holds nothing. It is the right verdict — a // hosted CI runner has no artifacts, and the artifact-backed proof runs in -// the core repository's certify workflow against this same tree — but never a +// the core repository's server-truth suites against this same tree — but never a // quiet one: the message names the directory, what was wrong with it, and the // one command that fills it. func skipNoArtifacts(t *testing.T, dir, detail string) { diff --git a/go/chtypes/parity_test.go b/go/chtypes/parity_test.go index 153ceb2..e465200 100644 --- a/go/chtypes/parity_test.go +++ b/go/chtypes/parity_test.go @@ -351,7 +351,7 @@ var goValues = map[string]any{ "DocDefaults": int(chtypes.DocDefaults), "DocAll": int(chtypes.DocAll), - // docs/fetch.md §6 — the machine-readable codes the four CLIs print. + // docs/guides/fetch.md §6 — the machine-readable codes the four CLIs print. "CodeArtifactMissing": string(chtypes.CodeArtifactMissing), "CodeArtifactUntrusted": string(chtypes.CodeArtifactUntrusted), "CodeArtifactCorrupt": string(chtypes.CodeArtifactCorrupt), diff --git a/go/chtypes/testdata/parity.json b/go/chtypes/testdata/parity.json index afc916d..9136296 100644 --- a/go/chtypes/testdata/parity.json +++ b/go/chtypes/testdata/parity.json @@ -2,7 +2,7 @@ "schema": 1, "title": "The chtypes binding parity contract, machine-readable", "about": [ - "This file is the ONE machine-readable source of truth for what every binding must expose. docs/reference/bindings.md is the prose that explains why; this is what the four test suites actually run against, and scripts/check-parity-doc.sh asserts the two agree on the object-model table.", + "This file is the ONE machine-readable source of truth for what every binding must expose. docs/reference/bindings.md is the prose that explains why; this is what the four test suites actually run against, and python/tests/test_parity.py asserts the two agree on the object-model table.", "Each entry is a LOGICAL capability, not a name. The four columns are that capability's spelling in each language, and a spelling that follows the language's own idiom (CompileDDL / compile_ddl / compileDdl / compile) is the point, not a divergence. What must never drift is which capabilities exist and, where a `value` is given, what they answer.", "A binding that genuinely should not have a capability declares it as {\"absent\": \"\"}. An absent with no reason fails the manifest's own integrity check, so a gap cannot be recorded silently.", "The `unlisted` map is the other half of the contract: public names a binding exports that the others deliberately do not need. Each one carries a reason, the no-undeclared-surface check fails on anything that is neither a capability spelling nor listed there, and an entry naming something the binding no longer exports fails too \u2014 an excuse with nothing behind it hides the next real gap.", @@ -16,9 +16,9 @@ "filter": "The revision 3/4 filter and block surface (§Revision 3, §Revision 4).", "results": "The result types every binding hands back (§Result types).", "vocabulary": "Shared value sets. These carry a `value`: the four bindings must answer with the same bytes, not merely have a symbol.", - "errors": "The refusal/decline split and the artifact error family (§Conformance rule 12, docs/fetch.md §6).", + "errors": "The refusal/decline split and the artifact error family (§Conformance rule 12, docs/guides/fetch.md §6).", "discovery": "The three canonical connect-time queries and their parsers (§Discovery).", - "fetch": "Installing and verifying artifacts (docs/fetch.md).", + "fetch": "Installing and verifying artifacts (docs/guides/fetch.md).", "cli": "The `chtypes` command every SDK ships." }, "floors": { @@ -31,7 +31,7 @@ "id": "registry.construct", "group": "registry", "kind": "ctor", - "what": "Open a registry: an explicit artifact directory, or the docs/fetch.md §1 search path when none is given", + "what": "Open a registry: an explicit artifact directory, or the docs/guides/fetch.md §1 search path when none is given", "go": "NewRegistry", "python": "Registry", "ts": "Registry", @@ -928,7 +928,7 @@ "id": "errors.artifact-missing", "group": "errors", "kind": "type", - "what": "No artifact for this line on the search path (docs/fetch.md §6, exit 1)", + "what": "No artifact for this line on the search path (docs/guides/fetch.md §6, exit 1)", "go": "ErrArtifactMissing", "python": "ArtifactMissingError", "ts": "ArtifactMissingError", @@ -987,37 +987,37 @@ { "id": "errors.artifact-code-missing", "group": "errors", "kind": "constant", "value": "CHTYPES_ARTIFACT_MISSING", - "what": "The docs/fetch.md §6 machine-readable code, which the four CLIs print identically", + "what": "The docs/guides/fetch.md §6 machine-readable code, which the four CLIs print identically", "go": "CodeArtifactMissing", "python": "CODE_ARTIFACT_MISSING", "ts": "CODE_ARTIFACT_MISSING", "rust": "CODE_ARTIFACT_MISSING" }, { "id": "errors.artifact-code-untrusted", "group": "errors", "kind": "constant", "value": "CHTYPES_ARTIFACT_UNTRUSTED", - "what": "docs/fetch.md §6", + "what": "docs/guides/fetch.md §6", "go": "CodeArtifactUntrusted", "python": "CODE_ARTIFACT_UNTRUSTED", "ts": "CODE_ARTIFACT_UNTRUSTED", "rust": "CODE_ARTIFACT_UNTRUSTED" }, { "id": "errors.artifact-code-corrupt", "group": "errors", "kind": "constant", "value": "CHTYPES_ARTIFACT_CORRUPT", - "what": "docs/fetch.md §6", + "what": "docs/guides/fetch.md §6", "go": "CodeArtifactCorrupt", "python": "CODE_ARTIFACT_CORRUPT", "ts": "CODE_ARTIFACT_CORRUPT", "rust": "CODE_ARTIFACT_CORRUPT" }, { "id": "errors.artifact-code-pinned", "group": "errors", "kind": "constant", "value": "CHTYPES_ARTIFACT_PINNED", - "what": "docs/fetch.md §6", + "what": "docs/guides/fetch.md §6", "go": "CodeArtifactPinned", "python": "CODE_ARTIFACT_PINNED", "ts": "CODE_ARTIFACT_PINNED", "rust": "CODE_ARTIFACT_PINNED" }, { "id": "errors.artifact-code-unpublished", "group": "errors", "kind": "constant", "value": "CHTYPES_ARTIFACT_UNPUBLISHED", - "what": "docs/fetch.md §6", + "what": "docs/guides/fetch.md §6", "go": "CodeArtifactUnpublished", "python": "CODE_ARTIFACT_UNPUBLISHED", "ts": "CODE_ARTIFACT_UNPUBLISHED", "rust": "CODE_ARTIFACT_UNPUBLISHED" }, { "id": "errors.artifact-code-source-unreachable", "group": "errors", "kind": "constant", "value": "CHTYPES_SOURCE_UNREACHABLE", - "what": "docs/fetch.md §6", + "what": "docs/guides/fetch.md §6", "go": "CodeSourceUnreachable", "python": "CODE_SOURCE_UNREACHABLE", "ts": "CODE_SOURCE_UNREACHABLE", "rust": "CODE_SOURCE_UNREACHABLE" }, @@ -1082,7 +1082,7 @@ { "id": "fetch.ensure", "group": "fetch", "kind": "function", - "what": "Install one line, verified, into the registry directory (docs/fetch.md)", + "what": "Install one line, verified, into the registry directory (docs/guides/fetch.md)", "go": "Ensure", "python": "ensure", "ts": "ensure", "rust": "ensure" }, { @@ -1106,7 +1106,7 @@ { "id": "fetch.registry-search-path", "group": "fetch", "kind": "function", - "what": "The docs/fetch.md §1 search path, in order, for this host", + "what": "The docs/guides/fetch.md §1 search path, in order, for this host", "go": "RegistrySearchPath", "python": "registry_search_path", "ts": "registrySearchPath", "rust": "registry_search_path" }, { @@ -1154,7 +1154,7 @@ { "id": "fetch.release-public-key", "group": "fetch", "kind": "constant", "value": "fdb5f06a8d4c9918d049a5f1748fa2e3b3238c3f2000986d5bb9e31beff778fc", - "what": "The raw 32-byte ed25519 release public key, hex, as docs/fetch.md §4 publishes it", + "what": "The raw 32-byte ed25519 release public key, hex, as docs/guides/fetch.md §4 publishes it", "go": "ReleasePublicKeyHex", "python": "RELEASE_PUBLIC_KEY", "ts": {"absent": "TS exports RELEASE_PUBLIC_KEYS, a LIST of trusted keys for rotation, rather than one scalar. The list's first element is this value; the parity test checks that instead."}, "rust": "fetch::RELEASE_PUBLIC_KEY_HEX" }, { @@ -1184,21 +1184,21 @@ { "id": "fetch.env-registry", "group": "fetch", "kind": "constant", "value": "CHTYPES_REGISTRY", - "what": "The environment variable naming an explicit registry (docs/fetch.md §1)", + "what": "The environment variable naming an explicit registry (docs/guides/fetch.md §1)", "go": {"absent": "Go spells the name inline in RegistrySearchPath and exports no constant for it. NEEDS A DECISION — the other three export one."}, "python": "ENV_REGISTRY", "ts": {"absent": "TS spells the name inline in resolveRegistryDir. NEEDS A DECISION."}, "rust": "REGISTRY_ENV" }, { "id": "fetch.env-autofetch", "group": "fetch", "kind": "constant", "value": "CHTYPES_AUTOFETCH", - "what": "The environment variable turning lazy fetch on (docs/fetch.md §6)", + "what": "The environment variable turning lazy fetch on (docs/guides/fetch.md §6)", "go": {"absent": "Spelled inline; see fetch.env-registry."}, "python": "ENV_AUTOFETCH", "ts": {"absent": "Spelled inline; see fetch.env-registry."}, "rust": "AUTOFETCH_ENV" }, { "id": "fetch.command", "group": "fetch", "kind": "constant", - "what": "This SDK's own fetch command, as the docs/fetch.md §7 message spells it. The VALUE is deliberately per-language — it is the command a caller of THIS SDK runs", + "what": "This SDK's own fetch command, as the docs/guides/fetch.md §7 message spells it. The VALUE is deliberately per-language — it is the command a caller of THIS SDK runs", "go": "GoFetchCommand", "python": {"absent": "The Python message builds the command inline in errors.py's ArtifactMissingError. NEEDS A DECISION — Go, TS and Rust export it."}, "ts": "FETCH_COMMAND", "rust": "FETCH_COMMAND" }, @@ -1256,7 +1256,7 @@ "LockFile.Write": "Write a lock file.", "NewLockFile": "Construct a lock file.", "LockKey": "The platform/line key a pin is stored under.", - "ExitCode": "Map an error to the docs/fetch.md §6 exit status; the other CLIs map it internally.", + "ExitCode": "Map an error to the docs/guides/fetch.md §6 exit status; the other CLIs map it internally.", "ErrorCode": "The artifact error code as a named string type.", "ErrorCode.ExitCode": "Accessor.", "ErrorCode.Sentinel": "Accessor.", @@ -1266,7 +1266,7 @@ "ArtifactError.Unwrap": "errors.Unwrap support.", "SchemaError.Error": "error interface.", "UnsupportedError.Error": "error interface.", - "SystemRegistryDirs": "The two system roots of docs/fetch.md §1.", + "SystemRegistryDirs": "The two system roots of docs/guides/fetch.md §1.", "DefaultRegistryDirFor": "The per-user cache for another platform.", "ValidPlatform": "Platform-key validation.", "QuoteIdentifier": "Backtick-quoting for reconstruct; the others keep it private.", @@ -1367,7 +1367,7 @@ "resolvePlatform": "Platform key resolution.", "isPlatformKey": "Platform-key validation.", "cacheRegistryDir": "The per-user cache for a platform.", - "systemRegistryDirs": "The two system roots of docs/fetch.md §1.", + "systemRegistryDirs": "The two system roots of docs/guides/fetch.md §1.", "looksLikeRegistry": "Whether a directory holds artifacts.", "resolveRegistryDir": "The first search-path entry that held something.", "selectArtifact": "Pick one release row for a spelling.", @@ -1426,7 +1426,7 @@ "Error": "The one error enum.", "Error::code": "The ClickHouse code, when there is one.", "Error::is_unsupported": "The explicit both-arms predicate; see errors.base.", - "Error::artifact_code": "The docs/fetch.md §6 code, when there is one.", + "Error::artifact_code": "The docs/guides/fetch.md §6 code, when there is one.", "Error::Registry": "Registry-layer error.", "Error::Load": "Load failure.", "Error::NotAnArtifact": "Load failure.", @@ -1460,7 +1460,7 @@ "DEFAULT_TIMEZONE": "The UTC default.", "REGISTRY_ENV": "Environment variable name.", "AUTOFETCH_ENV": "Environment variable name.", - "SYSTEM_ARTIFACT_ROOTS": "The two system roots of docs/fetch.md §1.", + "SYSTEM_ARTIFACT_ROOTS": "The two system roots of docs/guides/fetch.md §1.", "cache_dir_for": "The per-user cache for a platform.", "install_dir_for": "Where a fetch for another platform writes.", "search_path_for": "The search path for another platform.", diff --git a/go/chtypes/transform.go b/go/chtypes/transform.go index 098ecf6..89a3d35 100644 --- a/go/chtypes/transform.go +++ b/go/chtypes/transform.go @@ -105,7 +105,6 @@ const ( // its own clock and the caller must send as an explicit column. A separate // reason from default_filled because the claim is different: the tenant is // being shown a value the *gateway* invented, not one the server chose. - // docs/defaults-matrix.md §5.4(3) asks for exactly this. ReasonDefaultMaterialized = "default_materialized" // The storage layer's own verdicts on rows the type layer accepted, diff --git a/include/chtypes.h b/include/chtypes.h index 5ae96b9..cb28339 100644 --- a/include/chtypes.h +++ b/include/chtypes.h @@ -49,7 +49,7 @@ extern "C" { * The RowBinary family reads the value in ClickHouse's own storage encoding * (ISerialization::deserializeBinary — the exact reader BinaryRowInputFormat * uses), so every parse-time text guard is bypassed exactly as it is on a real - * server (docs/type-coverage.md §11): one error code (33) for any framing + * server: one error code (33) for any framing * fault, all-or-nothing batches, input_format_allow_errors_* never applies. * CHS_ROW_BINARY_WITH_DEFAULTS adds the measured per-column marker byte * (any nonzero byte = compute the column's DEFAULT, read no value bytes). @@ -608,8 +608,8 @@ typedef struct chs_bytes * then logs only a count), reported verbatim. The batch verdict, rows_read * and rows_skipped are exactly what they were when skips were silent. * - * THE EXPORT CHANNEL (revision 3; docs/proposals/rows-export.md, normative - * text in the C ABI contract §Rows). `export_format` is CHS_EXPORT_NONE or an + * THE EXPORT CHANNEL (revision 3; the normative text is the C ABI + * contract §Rows, for which this header is the public authority). `export_format` is CHS_EXPORT_NONE or an * enum chs_format value this artifact can SERIALIZE — this revision exactly * CHS_JSON_COMPACT_EACH_ROW. With an export requested, out_bytes (required * non-NULL, always initialized to {NULL,0} at entry) receives the batch's diff --git a/python/pyproject.toml b/python/pyproject.toml index 788e421..fc3862a 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -54,7 +54,7 @@ src = ["src", "tests"] # the classes that have actually cost time here — unused imports and names (F), # style errors that hide real ones (E), import order drift across 20 files (I), # syntax this project's floor already allows (UP), and the mutable-default / -# loop-variable-capture family (B). `just lint` and ci/steps/static-checks.sh -# both run this, so a finding is a gate failure, not advice. +# loop-variable-capture family (B). `just lint` and the core repository's +# static checks both run this, so a finding is a gate failure, not advice. [tool.ruff.lint] select = ["E", "F", "I", "UP", "B"] diff --git a/python/src/chtypes/registry.py b/python/src/chtypes/registry.py index 2679587..d5972fb 100644 --- a/python/src/chtypes/registry.py +++ b/python/src/chtypes/registry.py @@ -370,8 +370,9 @@ def rows( `BatchResult.rows` as `Outcome.SKIPPED`, carrying the server's own caught error verbatim (2026-08-27). - **The revision-3 export channel** (the C ABI contract §Rows; - docs/proposals/rows-export.md) — still ONE `chs_rows` call, never a + **The revision-3 export channel** (the C ABI contract §Rows, for + which `include/chtypes.h` is the public authority) — still ONE + `chs_rows` call, never a second, never re-parsing: `export` is None (no bytes — today's path, byte-identical documents) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index f09aa5f..bbd5cf8 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -2,7 +2,7 @@ not run at all — it skips, loudly, by name. The fetch, CLI and pure-Python tests need none and always run; that is what this repository's CI proves on hosted runners, and the artifact-backed proof is the core repository's -`certify` workflow against this same tree. +server-truth suites against this same tree. The registry comes from the search path (docs/guides/fetch.md §1): `$CHTYPES_REGISTRY`, else the per-user artifact cache for this host (`chtypes.default_registry_dir()`: diff --git a/rust/src/schema.rs b/rust/src/schema.rs index 7f422fb..0c3b1f4 100644 --- a/rust/src/schema.rs +++ b/rust/src/schema.rs @@ -256,7 +256,8 @@ impl Schema { /// [`Schema::rows`] with the revision-3 export and document-flag channels /// exposed: ONE `chs_rows` call, never a second, never re-parsing - /// (`docs/proposals/rows-export.md`; the C ABI contract §Rows is normative). + /// (the C ABI contract §Rows is normative; `include/chtypes.h` is its + /// public authority). /// /// `export` is `None` (no bytes; `doc_flags` still thins the document) or /// `Some(format)` for a [`Format`] this artifact can SERIALIZE — this diff --git a/rust/tests/parity.rs b/rust/tests/parity.rs index 8077306..0b15037 100644 --- a/rust/tests/parity.rs +++ b/rust/tests/parity.rs @@ -456,7 +456,7 @@ fn rust_values() -> BTreeMap<&'static str, Val> { m.insert("DocFlags::DEFAULTS", i(DocFlags::DEFAULTS.bits() as i64)); m.insert("DocFlags::ALL", i(DocFlags::ALL.bits() as i64)); - // docs/fetch.md §6 — the machine-readable codes the four CLIs print. + // docs/guides/fetch.md §6 — the machine-readable codes the four CLIs print. m.insert("CODE_ARTIFACT_MISSING", s(chtypes::CODE_ARTIFACT_MISSING)); m.insert( "CODE_ARTIFACT_UNTRUSTED", diff --git a/scripts/check-standalone.sh b/scripts/check-standalone.sh index 0383a5a..0308796 100755 --- a/scripts/check-standalone.sh +++ b/scripts/check-standalone.sh @@ -21,7 +21,7 @@ # Without a registry — a hosted CI runner, a fresh clone — the suite still # runs: the fetch, fixture and CLI tests need no artifact, and every test that # does SKIPS, by name, in the census; the golden set and the registry suite -# are then the core repository's certify workflow's to prove. --no-artifacts +# are then the core repository's server-truth suites' to prove. --no-artifacts # reproduces that runner here: an empty XDG_CACHE_HOME and no # $CHTYPES_REGISTRY, so this machine's own cache is invisible. # --require-artifacts is the opposite rule, for CI's artifact-backed run: diff --git a/scripts/check-suite.sh b/scripts/check-suite.sh index fddb0e9..952adf3 100755 --- a/scripts/check-suite.sh +++ b/scripts/check-suite.sh @@ -23,7 +23,7 @@ # and no test may have skipped for want of a registry. # # The artifact-backed proof beyond these — the server-truth suites, the -# oracle, the rigs — is the core repository's certify workflow. +# oracle, the rigs — is the core repository's server-truth suites. set -euo pipefail SCRIPTS="$(cd "$(dirname "$0")" && pwd)" diff --git a/scripts/lint-paths.sh b/scripts/lint-paths.sh new file mode 100755 index 0000000..b941650 --- /dev/null +++ b/scripts/lint-paths.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# lint-paths.sh — no tracked file may cite a repository path that does not exist. +# +# WHY THIS EXISTS. `docs/proposals/rows-export.md` was cited six times — in +# include/chtypes.h and in all four bindings' source, every one of which ships +# inside the crate, the sdist, the module and the npm tarball — for a file that +# is not in this repository. Four more dead citations were found the same way +# (`docs/defaults-matrix.md`, `docs/type-coverage.md`, `docs/fetch.md` after it +# moved under guides/, and a `scripts/check-parity-doc.sh` that has never +# existed while the check it named was real and living in the Python suite). +# +# Each was a pointer a public reader cannot follow. lint-public.sh catches the +# private repository by NAME; this catches the other half of the same failure — +# a path that simply is not there. Between them a sweep that "removed" a +# pointer by changing its shape cannot pass quietly again. +# +# scripts/lint-paths.sh check every tracked file +# scripts/lint-paths.sh --selftest prove the rule fires, and does not overfire +set -euo pipefail +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +scan() { + ( cd "$1" && git ls-files -z | python3 -c ' +import os, re, sys + +# Top-level directories whose names are unambiguous repository paths. +TOP = r"(?:docs|examples|scripts|include|goldens|spec|tests)" +# A real extension is required: a bare word after a slash is a glob or prose. +PAT = re.compile(r"(? "$tmp/docs/guides/fetch.md" + printf 'ok\n' > "$tmp/rust/tests/integration.rs" + printf 'see docs/gone/missing.md\n' > "$tmp/planted.md" + printf 'see docs/guides/fetch.md\n' > "$tmp/live.md" + printf '// see tests/integration.rs\n' > "$tmp/rust/lib.rs" + printf 'run pytest tests/integration.rs from rust/\n' > "$tmp/sibling.md" + git -C "$tmp" add -A + out="$(scan "$tmp" 2>&1)" && { echo "SELFTEST FAILED: the planted dead path was not caught" >&2; exit 1; } + printf '%s\n' "$out" | grep -q 'planted.md' || { echo "SELFTEST FAILED: rule did not fire on planted.md" >&2; exit 1; } + printf '%s\n' "$out" | grep -q 'live.md' && { echo "SELFTEST FAILED: a path that exists was flagged" >&2; exit 1; } + printf '%s\n' "$out" | grep -q 'rust/lib.rs' && { echo "SELFTEST FAILED: a crate-relative path was flagged" >&2; exit 1; } + echo "lint-paths: selftest ok — fires on a dead path, silent on live and crate-relative ones" + exit 0 +fi + +scan "$HERE" +echo "lint-paths: ok — every cited repository path exists" diff --git a/tests/parity/manifest.json b/tests/parity/manifest.json index afc916d..9136296 100644 --- a/tests/parity/manifest.json +++ b/tests/parity/manifest.json @@ -2,7 +2,7 @@ "schema": 1, "title": "The chtypes binding parity contract, machine-readable", "about": [ - "This file is the ONE machine-readable source of truth for what every binding must expose. docs/reference/bindings.md is the prose that explains why; this is what the four test suites actually run against, and scripts/check-parity-doc.sh asserts the two agree on the object-model table.", + "This file is the ONE machine-readable source of truth for what every binding must expose. docs/reference/bindings.md is the prose that explains why; this is what the four test suites actually run against, and python/tests/test_parity.py asserts the two agree on the object-model table.", "Each entry is a LOGICAL capability, not a name. The four columns are that capability's spelling in each language, and a spelling that follows the language's own idiom (CompileDDL / compile_ddl / compileDdl / compile) is the point, not a divergence. What must never drift is which capabilities exist and, where a `value` is given, what they answer.", "A binding that genuinely should not have a capability declares it as {\"absent\": \"\"}. An absent with no reason fails the manifest's own integrity check, so a gap cannot be recorded silently.", "The `unlisted` map is the other half of the contract: public names a binding exports that the others deliberately do not need. Each one carries a reason, the no-undeclared-surface check fails on anything that is neither a capability spelling nor listed there, and an entry naming something the binding no longer exports fails too \u2014 an excuse with nothing behind it hides the next real gap.", @@ -16,9 +16,9 @@ "filter": "The revision 3/4 filter and block surface (§Revision 3, §Revision 4).", "results": "The result types every binding hands back (§Result types).", "vocabulary": "Shared value sets. These carry a `value`: the four bindings must answer with the same bytes, not merely have a symbol.", - "errors": "The refusal/decline split and the artifact error family (§Conformance rule 12, docs/fetch.md §6).", + "errors": "The refusal/decline split and the artifact error family (§Conformance rule 12, docs/guides/fetch.md §6).", "discovery": "The three canonical connect-time queries and their parsers (§Discovery).", - "fetch": "Installing and verifying artifacts (docs/fetch.md).", + "fetch": "Installing and verifying artifacts (docs/guides/fetch.md).", "cli": "The `chtypes` command every SDK ships." }, "floors": { @@ -31,7 +31,7 @@ "id": "registry.construct", "group": "registry", "kind": "ctor", - "what": "Open a registry: an explicit artifact directory, or the docs/fetch.md §1 search path when none is given", + "what": "Open a registry: an explicit artifact directory, or the docs/guides/fetch.md §1 search path when none is given", "go": "NewRegistry", "python": "Registry", "ts": "Registry", @@ -928,7 +928,7 @@ "id": "errors.artifact-missing", "group": "errors", "kind": "type", - "what": "No artifact for this line on the search path (docs/fetch.md §6, exit 1)", + "what": "No artifact for this line on the search path (docs/guides/fetch.md §6, exit 1)", "go": "ErrArtifactMissing", "python": "ArtifactMissingError", "ts": "ArtifactMissingError", @@ -987,37 +987,37 @@ { "id": "errors.artifact-code-missing", "group": "errors", "kind": "constant", "value": "CHTYPES_ARTIFACT_MISSING", - "what": "The docs/fetch.md §6 machine-readable code, which the four CLIs print identically", + "what": "The docs/guides/fetch.md §6 machine-readable code, which the four CLIs print identically", "go": "CodeArtifactMissing", "python": "CODE_ARTIFACT_MISSING", "ts": "CODE_ARTIFACT_MISSING", "rust": "CODE_ARTIFACT_MISSING" }, { "id": "errors.artifact-code-untrusted", "group": "errors", "kind": "constant", "value": "CHTYPES_ARTIFACT_UNTRUSTED", - "what": "docs/fetch.md §6", + "what": "docs/guides/fetch.md §6", "go": "CodeArtifactUntrusted", "python": "CODE_ARTIFACT_UNTRUSTED", "ts": "CODE_ARTIFACT_UNTRUSTED", "rust": "CODE_ARTIFACT_UNTRUSTED" }, { "id": "errors.artifact-code-corrupt", "group": "errors", "kind": "constant", "value": "CHTYPES_ARTIFACT_CORRUPT", - "what": "docs/fetch.md §6", + "what": "docs/guides/fetch.md §6", "go": "CodeArtifactCorrupt", "python": "CODE_ARTIFACT_CORRUPT", "ts": "CODE_ARTIFACT_CORRUPT", "rust": "CODE_ARTIFACT_CORRUPT" }, { "id": "errors.artifact-code-pinned", "group": "errors", "kind": "constant", "value": "CHTYPES_ARTIFACT_PINNED", - "what": "docs/fetch.md §6", + "what": "docs/guides/fetch.md §6", "go": "CodeArtifactPinned", "python": "CODE_ARTIFACT_PINNED", "ts": "CODE_ARTIFACT_PINNED", "rust": "CODE_ARTIFACT_PINNED" }, { "id": "errors.artifact-code-unpublished", "group": "errors", "kind": "constant", "value": "CHTYPES_ARTIFACT_UNPUBLISHED", - "what": "docs/fetch.md §6", + "what": "docs/guides/fetch.md §6", "go": "CodeArtifactUnpublished", "python": "CODE_ARTIFACT_UNPUBLISHED", "ts": "CODE_ARTIFACT_UNPUBLISHED", "rust": "CODE_ARTIFACT_UNPUBLISHED" }, { "id": "errors.artifact-code-source-unreachable", "group": "errors", "kind": "constant", "value": "CHTYPES_SOURCE_UNREACHABLE", - "what": "docs/fetch.md §6", + "what": "docs/guides/fetch.md §6", "go": "CodeSourceUnreachable", "python": "CODE_SOURCE_UNREACHABLE", "ts": "CODE_SOURCE_UNREACHABLE", "rust": "CODE_SOURCE_UNREACHABLE" }, @@ -1082,7 +1082,7 @@ { "id": "fetch.ensure", "group": "fetch", "kind": "function", - "what": "Install one line, verified, into the registry directory (docs/fetch.md)", + "what": "Install one line, verified, into the registry directory (docs/guides/fetch.md)", "go": "Ensure", "python": "ensure", "ts": "ensure", "rust": "ensure" }, { @@ -1106,7 +1106,7 @@ { "id": "fetch.registry-search-path", "group": "fetch", "kind": "function", - "what": "The docs/fetch.md §1 search path, in order, for this host", + "what": "The docs/guides/fetch.md §1 search path, in order, for this host", "go": "RegistrySearchPath", "python": "registry_search_path", "ts": "registrySearchPath", "rust": "registry_search_path" }, { @@ -1154,7 +1154,7 @@ { "id": "fetch.release-public-key", "group": "fetch", "kind": "constant", "value": "fdb5f06a8d4c9918d049a5f1748fa2e3b3238c3f2000986d5bb9e31beff778fc", - "what": "The raw 32-byte ed25519 release public key, hex, as docs/fetch.md §4 publishes it", + "what": "The raw 32-byte ed25519 release public key, hex, as docs/guides/fetch.md §4 publishes it", "go": "ReleasePublicKeyHex", "python": "RELEASE_PUBLIC_KEY", "ts": {"absent": "TS exports RELEASE_PUBLIC_KEYS, a LIST of trusted keys for rotation, rather than one scalar. The list's first element is this value; the parity test checks that instead."}, "rust": "fetch::RELEASE_PUBLIC_KEY_HEX" }, { @@ -1184,21 +1184,21 @@ { "id": "fetch.env-registry", "group": "fetch", "kind": "constant", "value": "CHTYPES_REGISTRY", - "what": "The environment variable naming an explicit registry (docs/fetch.md §1)", + "what": "The environment variable naming an explicit registry (docs/guides/fetch.md §1)", "go": {"absent": "Go spells the name inline in RegistrySearchPath and exports no constant for it. NEEDS A DECISION — the other three export one."}, "python": "ENV_REGISTRY", "ts": {"absent": "TS spells the name inline in resolveRegistryDir. NEEDS A DECISION."}, "rust": "REGISTRY_ENV" }, { "id": "fetch.env-autofetch", "group": "fetch", "kind": "constant", "value": "CHTYPES_AUTOFETCH", - "what": "The environment variable turning lazy fetch on (docs/fetch.md §6)", + "what": "The environment variable turning lazy fetch on (docs/guides/fetch.md §6)", "go": {"absent": "Spelled inline; see fetch.env-registry."}, "python": "ENV_AUTOFETCH", "ts": {"absent": "Spelled inline; see fetch.env-registry."}, "rust": "AUTOFETCH_ENV" }, { "id": "fetch.command", "group": "fetch", "kind": "constant", - "what": "This SDK's own fetch command, as the docs/fetch.md §7 message spells it. The VALUE is deliberately per-language — it is the command a caller of THIS SDK runs", + "what": "This SDK's own fetch command, as the docs/guides/fetch.md §7 message spells it. The VALUE is deliberately per-language — it is the command a caller of THIS SDK runs", "go": "GoFetchCommand", "python": {"absent": "The Python message builds the command inline in errors.py's ArtifactMissingError. NEEDS A DECISION — Go, TS and Rust export it."}, "ts": "FETCH_COMMAND", "rust": "FETCH_COMMAND" }, @@ -1256,7 +1256,7 @@ "LockFile.Write": "Write a lock file.", "NewLockFile": "Construct a lock file.", "LockKey": "The platform/line key a pin is stored under.", - "ExitCode": "Map an error to the docs/fetch.md §6 exit status; the other CLIs map it internally.", + "ExitCode": "Map an error to the docs/guides/fetch.md §6 exit status; the other CLIs map it internally.", "ErrorCode": "The artifact error code as a named string type.", "ErrorCode.ExitCode": "Accessor.", "ErrorCode.Sentinel": "Accessor.", @@ -1266,7 +1266,7 @@ "ArtifactError.Unwrap": "errors.Unwrap support.", "SchemaError.Error": "error interface.", "UnsupportedError.Error": "error interface.", - "SystemRegistryDirs": "The two system roots of docs/fetch.md §1.", + "SystemRegistryDirs": "The two system roots of docs/guides/fetch.md §1.", "DefaultRegistryDirFor": "The per-user cache for another platform.", "ValidPlatform": "Platform-key validation.", "QuoteIdentifier": "Backtick-quoting for reconstruct; the others keep it private.", @@ -1367,7 +1367,7 @@ "resolvePlatform": "Platform key resolution.", "isPlatformKey": "Platform-key validation.", "cacheRegistryDir": "The per-user cache for a platform.", - "systemRegistryDirs": "The two system roots of docs/fetch.md §1.", + "systemRegistryDirs": "The two system roots of docs/guides/fetch.md §1.", "looksLikeRegistry": "Whether a directory holds artifacts.", "resolveRegistryDir": "The first search-path entry that held something.", "selectArtifact": "Pick one release row for a spelling.", @@ -1426,7 +1426,7 @@ "Error": "The one error enum.", "Error::code": "The ClickHouse code, when there is one.", "Error::is_unsupported": "The explicit both-arms predicate; see errors.base.", - "Error::artifact_code": "The docs/fetch.md §6 code, when there is one.", + "Error::artifact_code": "The docs/guides/fetch.md §6 code, when there is one.", "Error::Registry": "Registry-layer error.", "Error::Load": "Load failure.", "Error::NotAnArtifact": "Load failure.", @@ -1460,7 +1460,7 @@ "DEFAULT_TIMEZONE": "The UTC default.", "REGISTRY_ENV": "Environment variable name.", "AUTOFETCH_ENV": "Environment variable name.", - "SYSTEM_ARTIFACT_ROOTS": "The two system roots of docs/fetch.md §1.", + "SYSTEM_ARTIFACT_ROOTS": "The two system roots of docs/guides/fetch.md §1.", "cache_dir_for": "The per-user cache for a platform.", "install_dir_for": "Where a fetch for another platform writes.", "search_path_for": "The search path for another platform.", diff --git a/ts/src/schema.ts b/ts/src/schema.ts index 29820ef..e49f574 100644 --- a/ts/src/schema.ts +++ b/ts/src/schema.ts @@ -49,7 +49,8 @@ export interface EngineOptions { /** * Options for `Schema#rows` — the revision-3 export and document-flag - * channels (the C ABI contract §Rows; docs/proposals/rows-export.md). Whatever the + * channels (the C ABI contract §Rows, for which include/chtypes.h is the + * public authority). Whatever the * options, `rows()` is always ONE `chs_rows` call — never a second call, * never re-parsing. */ diff --git a/ts/test/chtypes.test.ts b/ts/test/chtypes.test.ts index 9467d5d..b94ce34 100644 --- a/ts/test/chtypes.test.ts +++ b/ts/test/chtypes.test.ts @@ -3,8 +3,8 @@ * registry. Nothing here asserts on an exit code or a self-report: every case * asserts on the document the library produced. * - * Level 3 (semantic) conformance is the rigs' job — `tests/acceptance` and - * `tests/arbiter` score an implementation against ground truth captured from real + * Level 3 (semantic) conformance is the rigs' job — the core repository's + * acceptance and arbiter rigs score an implementation against ground truth captured from real * ClickHouse servers — and no unit test can stand in for it. * * Float expectations are deliberately absent: macOS's `long double` is 53-bit, so