SDKs · 6. Known issues

Every defect below was found by source read and, where the wording says "verified", reproduced against the published artifact. Six are blocking: a consumer who does the obvious thing hits them. The rest are staleness and documentation contradictions that will mislead you if you trust the affected file.

None of these affects @cuecrux/client or corecrux-client, the two SDKs chapter 0 recommends. Five of the six blocking defects are in SDKCrux packages; the sixth is a documentation contradiction in the Crux repository.

This page is reference, and it is standing rather than provisional: it is dated, it will be re-dated when it is re-verified, and defects are removed only when they are fixed, not when they become inconvenient. Nothing here is an apology. A page like this exists so you can decide with the same information we have.

6.0 The register

#WhereDefectVerified how
B1@cuecrux/engine-client 1.1.1Declares types: dist/index.d.ts; the build emits dist/src/index.d.ts. The published package has no resolvable typingsPublished tarball listing and three tsc resolution modes
B2@cuecrux/engine-client 1.1.1search() and trustReport() call routes the Engine does not serveAbsent from the Engine's OpenAPI description; absent from its route registration
B3@cuecrux/engine-client 1.1.1No way to send a credential, though the Engine declares X-API-Key and bearer as global securitySource read of all nine methods
B4@cuecrux/memory 0.1.0getFactsByEntity() types the daemon's {"facts": […]} wrapper as Fact[]Daemon handler source against the SDK's declared type
B5cuecrux-receipt 0.1.1Public-scoped but depends transitively on two restricted packages. It would install brokenManifest dependency chain
B6@cuecrux/policy-cli 0.14.1Public @cuecrux scope, not private, no publishConfig, invisible to the publish guardThe guard's scan roots
S1@cuecrux/memory, 6.7Recorded daemon spec is 0.3.1; the daemon is 0.5.52Both version strings
S2@cuecrux/engine-client, 6.8Recorded Engine spec has 157 paths; the Engine has 164. pnpm test:api-parity fails todayPath counts from both documents
S7Crux/docs/sdk-release-lifecycle.md, 6.9Describes as "gated future work" two changes both workflows already implement; its adoption gates are unchecked though doneThe workflow files
S8Crux/docs/developer-portal.md, 6.10Prescribes OpenAPI regeneration for two hand-written clientsThe SDK sources
S9Both repositories, 6.11Neither mentions the other, though they share an npm scopeFull-text search of both
S3–S6SDKCrux, 6.12bom.json 2026-01-20; docs/context/ 2025-11-04; a committed packages/watch/dist 2025-11-06; three dead README linksFile timestamps and git log

6.1 B1: engine-client ships no resolvable typings

The defect. @cuecrux/engine-client@1.1.1 declares "types": "dist/index.d.ts" and exports["."].types = "dist/index.d.ts" (package.json:13). Its declaration build is tsc -b tsconfig.dts.json, which inherits composite: true from the repository's base config without setting rootDir. Under composite with no explicit rootDir, TypeScript roots the output at the tsconfig directory, so declarations land in dist/src/. tsup is configured without a dts option, so nothing fills the gap.

Evidence, from the published artifact. npm pack @cuecrux/engine-client@1.1.1 on 2026-07-27 yields fifteen files. The relevant ones:

package/dist/index.cjs
package/dist/index.mjs
package/dist/src/index.d.ts          <- the declarations are here
package/dist/src/fetch-guard.d.ts
package/dist/src/types.gen.d.ts
package/package.json                 <- which points at dist/index.d.ts

There is no package/dist/index.d.ts. Importing the package under TypeScript 5.7.3 fails in every resolution mode:

moduleResolutionResult
nodenexterror TS2307: Cannot find module '@cuecrux/engine-client' or its corresponding type declarations.
bundlererror TS2307, identical
node10error TS7016: Could not find a declaration file for module '@cuecrux/engine-client'. '…/dist/index.cjs' implicitly has an 'any' type.

tsc --traceResolution shows exactly where it gives up:

Entering conditional exports.
Matched 'exports' condition 'types'.
package.json scope '…/node_modules/@cuecrux/engine-client' has invalid type for target of specifier '.'
Failed to resolve under condition 'types'.

Consequence. The package installs and runs, dist/index.mjs exists and the import condition resolves, but a TypeScript consumer gets no types at all, and under strict the import is a hard compile error rather than a silent any. The exports map also blocks the obvious deep-import workaround: import … from "@cuecrux/engine-client/dist/src/index.js" fails with TS2307 too, because no subpath is exported.

Workaround, verified. A paths mapping in your tsconfig.json compiles clean:

{
  "compilerOptions": {
    "paths": {
      "@cuecrux/engine-client": [
        "./node_modules/@cuecrux/engine-client/dist/src/index.d.ts"
      ]
    }
  }
}

Verified 2026-07-27: with that mapping added, a file importing createEngineClient compiles with zero errors under strict and moduleResolution: nodenext. Runtime resolution is unaffected - paths is a type-only redirect.

The same latent bug sits in six sibling packages, each declaring a types path its build does not emit: @cuecrux-internal/web, @cuecrux-internal/engine, @cuecrux-internal/engine-sdk, @cuecrux-internal/mocks, @cuecrux-internal/watch, and cuecrux-receipt. None of them is published, so none has bitten yet. @cuecrux/memory gets it right; it declares types: dist/src/index.d.ts (package.json:13) - as does @cuecrux/factory, which builds declarations through tsup instead.

6.2 B2: search() and trustReport() call routes the Engine does not serve

The defect. Two of @cuecrux/engine-client's nine methods address routes that do not exist.

search() issues GET /v1/search (index.ts:130). The source acknowledges that the operation was dropped from the Engine's OpenAPI description and asserts "the endpoint is still served" (index.ts:4). That assertion no longer holds.

trustReport() issues GET /v1/answers/{id}/trust-report (index.ts:194).

Evidence. The Engine's OpenAPI description, read 2026-07-27, contains 164 paths. /v1/search is not among them; the only paths containing "search" are /v1/embeddings/search and /v1/community/search. No path contains "trust-report"; the only paths containing "trust" are three under /v1/intel/. A read of the Engine's route registration on the same date confirms that neither route is mounted, trust-report appears nowhere in the Engine source at all.

Consequence. Both methods 404 at runtime and throw a bare Error reading search failed: 404 or trust-report failed: 404, with no status property to branch on. The release note for 1.1.0 still headlines a trust-report fix for the second of these.

Workaround. There is none within the package. Use answers() for retrieval-backed answers, and call whichever Engine route you actually need directly. Note that @cuecrux-internal/mocks registers a handler for GET */search, not GET */v1/search, so it will not intercept search() either, a test suite mocking this package will not catch the 404.

6.3 B3: engine-client cannot send a credential

The defect. createEngineClient(baseUrl, fetchImpl?) takes no options object, sets no Authorization and no X-API-Key on any of its nine methods, and exposes no hook to add one (index.ts:96). Every request it issues is anonymous.

Evidence. The Engine's OpenAPI description declares two global security schemes, ApiKeyAuth, an apiKey in the header X-API-Key, described as "Obtain a key from the CueCrux console"; and BearerAuth, HTTP bearer with bearerFormat: JWT, applied at the document's security level, so they cover every route. The client sets neither. Its README's advice, "No internal headers are injected. Pass whatever authentication header your account requires" (README.md:20), names no mechanism because none exists.

Consequence. Silent. There is no error, no warning and no type-level hint; you get whatever the Engine returns to an anonymous caller, which may be a 401, may be a degraded result, and will not be what you expected.

Workaround. Wrap the fetch you pass in. The full pattern is in 3.5.

Related, same file. The @cuecrux-internal/core CueCruxClient has no auth surface either - CueCruxClientOptions carries no token or key field.

6.4 B4: getFactsByEntity() mistypes the daemon's wrapper

The defect. @cuecrux/memory's getFactsByEntity(entity) is declared Promise<Fact[]> and implemented as requestJson<Fact[]>(...) (client.ts:52, client.ts:133). The daemon returns a wrapped object.

Evidence. The handler for GET /v1/facts/entity/{entity} ends (facts.rs:625):

(StatusCode::OK, axum::Json(serde_json::json!({"facts": facts}))).into_response()

The two other clients handle this correctly. @cuecrux/client returns Promise<{facts: Fact[]}> (index.ts:122). corecrux-client does data.get("facts", []) (client.py:235). The source of the error is an inline comment asserting that the handler "returns the registered Fact schema as an array" (client.ts:131) - a reasonable inference from a spec gap, and wrong.

Consequence. No exception is thrown. The method returns the wrapper object typed as an array: .length is undefined, iteration yields nothing, and .map() throws a TypeError one frame later. Code that treats "no facts" as a valid empty result will silently do the wrong thing.

Workaround, if you are running a vendored copy:

const raw = (await client.getFactsByEntity(entity)) as unknown as { facts: Fact[] };
const facts = raw.facts ?? [];

This is the only functional bug in @cuecrux/memory. deleteFact is safe despite the same class of spec gap, because it discards the response body.

6.5 B5: cuecrux-receipt is public-scoped but depends on restricted packages

The defect. cuecrux-receipt declares publishConfig.access: 'public' with no registry override, so it targets public npm (package.json:32). Its runtime dependency is @cuecrux-internal/engine-sdk at workspace:* (package.json:24), which is published to GitHub Packages with access: restricted, and which itself depends on @cuecrux-internal/core, also restricted.

Evidence. The .npmrc routes @cuecrux-internal:registry=https://npm.pkg.github.com (.npmrc:2). A consumer installing from public npm has no such mapping and no GitHub Packages token, so the dependency resolves against registry.npmjs.org, where it does not exist.

Consequence. Publishing this package as configured would produce an uninstallable package: npm install cuecrux-receipt would fail at dependency resolution with a 404 on @cuecrux-internal/engine-sdk. It has not bitten anyone, because there is no release workflow for it and it is in no publish allowlist, the package is not on npm today. This is a loaded gun, not a fired one.

Workaround. Build it from the SDKCrux checkout, or reimplement the verification: it is canonicalJson plus BLAKE3 plus Ed25519 verify, described in 5.8.

6.6 B6: @cuecrux/policy-cli is invisible to the publish guard

The defect. apps/policy-cli is named @cuecrux/policy-cli, the public scope, is not marked private, and has no publishConfig (package.json:2). It depends on @cuecrux-internal/core, which is restricted, so publishing it would produce the same broken install as B5.

Evidence. The publish guard loads workspace packages from exactly two directories (verify-lib.ts:22):

const areas = [path.join(root, 'packages'), path.join(root, 'packages', 'internal')];

apps/ is never scanned, so no mode of verify-publish-targets can see this package, warn about it, or reject it. The workspace globs do include apps/*, so it is a full workspace member - pnpm -r publish would consider it.

Consequence. Today: none. @cuecrux/policy-cli is not on npm. The exposure is that the guard designed to stop an accidental public publish has a hole precisely where the one accidentally public-scoped package lives.

Workaround. If you maintain this repository, add "private": true to that manifest, or add apps to the guard's scan roots. If you consume it, you are building from source and nothing changes.

6.7 S1: the memory client's recorded spec is nine daemon minors old

@cuecrux/memory generates its types from a recorded copy of the daemon's OpenAPI document. That copy reports info.version 0.3.1 and contains 18 paths. The daemon's workspace version is 0.5.52 (Cargo.toml:45).

The spec was dumped at Crux commit 5b3c008, recorded in a source comment (client.ts:6). That commit is dated 2026-06-12.

Consequence. The generated types cannot describe routes added since, session archive and unarchive, the witness smoke route, and the receipts list route among them. Existing types remain valid where the wire format did not change, which is why the package still works. The refresh procedure is prose only: "fetch GET /v1/openapi.json from a daemon built from Crux main and replace openapi/openapi.json, then regenerate" (README.md:42). There is no sync script and no parity check for this package, unlike @cuecrux/engine-client, which has both.

6.8 S2: the recorded Engine spec is seven paths behind

pnpm test:api-parity byte-compares the recorded Engine spec against a sibling ../Engine checkout. It fails today.

DocumentPaths
The Engine's own OpenAPI description164
The copy recorded in packages/engine-client/openapi/openapi.json157

Both counts verified 2026-07-27. The seven paths present in the Engine and absent from the SDK's copy: /v1/receipts, /v1/receipts/{snapshotId}/reconstruct, /operator/reconstruction-inputs/{tenantId}, /internal/artifacts/{artifactId}/living-status, /v1/artifacts/{artifactId}/living-status, /v1/ops/admin/tenant-data, /v1/ops/admin/tenant-data-export.

Consequence. Three, in increasing order of annoyance. The parity test is red, so the signal it was built to give is lost. The generated types.gen.ts cannot type the seven missing routes. And PUBLIC_API.md, which is regenerated from this recorded copy and CI-enforced for freshness against it, is transitively stale; it is faithful to the recording, and the recording is behind.

A note on that file: PUBLIC_API.md does not document the SDK. It is a generated inventory of the Engine's HTTP endpoints, including roughly ninety internal/, ops/, crux/ and intel/ routes that no SDK method exposes. There is no generated reference for the TypeScript API of any package in the repository.

6.9 S7: the release-lifecycle doc describes shipped work as gated

Crux/docs/sdk-release-lifecycle.md is accurate on versioning, tag decoupling, the n−1 support policy, the release procedure and the fail-closed immutability rule. Two of its sections are not.

ClaimLineContradicting evidence
"Target state: both items are workflow changes, gated until the supply-chain release pipeline merges":44Both changes are already live
npm provenance, id-token: write and Trusted Publishing described as future work:48sdk-typescript.yml:63 grants id-token: write; :79 publishes with --provenance; :61 states there is no NPM_TOKEN
PyPI Trusted Publishing described as future work:53sdk-python.yml:70 states there is no PYPI_TOKEN; :81 uses the OIDC action
"Until cutover, the existing token-based publishes remain":59No token-based publish exists in either workflow
Adoption-gate checklist, all boxes unchecked:82At least the two workflow-edit gates are complete
"Each SDK declares the API version it was generated/tested against… Generated clients are regenerated per daemon release":22Neither SDK declares such a version, in its README or its metadata. Both are hand-written, not generated
"Pre-1.0… Say so in the README until 1.0":27Neither README carries a pre-1.0 stability notice
Policy extended to packages published from other repositories:5SDKCrux references this policy nowhere; its own release doc describes a different, non-tag-gated procedure

Consequence. A reader assessing supply-chain posture from this document concludes that Trusted Publishing and provenance are pending. They are live. The document undersells the estate it governs, which is an unusual failure mode and still a failure mode: it is the document a procurement reviewer would read.

6.10 S8: the developer portal prescribes regeneration for hand-written clients

Crux/docs/developer-portal.md is otherwise accurate; its endpoints, ports and OpenAPI location are correct, and line 61 carries the clearest canonicality statement in either repository. Two gaps.

It prescribes a workflow with no applicable target. "Generated clients should be pinned to the daemon release they target and regenerated from /v1/openapi.json when the HTTP API moves" (:67). Neither in-repo SDK is generated. Both are hand-written: sdks/typescript/src/index.ts and sdks/python/src/corecrux_client/client.py are handwritten method-per-route clients with no generator step in their build. The only genuinely generated client in the portfolio is @cuecrux/memory, which lives in a different repository this document does not mention.

It cites RFC 7807 where the SDK cites RFC 9457. The portal says "Error format: RFC 7807 Problem Details" (:13); the TypeScript SDK says 9457 (index.ts:34). 9457 obsoletes 7807 and the wire format is identical, so this is a citation inconsistency, not a behavioural one.

6.11 S9: neither repository acknowledges the other

This is the finding with the widest blast radius, and it is a documentation defect rather than a code one.

DocumentClaimProblem
Crux/docs/developer-portal.md :61"The in-repo SDKs are the supported public SDK surface"Never mentions SDKCrux, @cuecrux/memory or @cuecrux/engine-client, so a reader cannot tell whether they are out of scope deliberately or by omission
SDKCrux/README.md :7The repository "contains the entire CueCrux SDK surface"It contains neither Crux SDK. The claim is false as written

Both statements cannot be true. Neither document mentions that the @cuecrux/ npm scope is shared across two repositories with two independent release pipelines. That is the trap chapter 0 exists to close.

6.12 Stale artefacts, in one table

None of these breaks a consumer directly. Each will mislead you if you read it as current.

ArtefactDatedProblem
SDKCrux/bom.json2026-01-20A CycloneDX 1.6 SBOM with 1,252 components, generated before @cuecrux/memory existed. CI writes a fresh SBOM to an artifact directory, never back to the repository root, so the committed copy is never refreshed
SDKCrux/docs/context/2025-11-04A generated repository index, 379 per-file records. No record for packages/memory, for connector tests added since, or for five of the current workflows
SDKCrux/packages/watch/dist/index.js and index.cjs2025-11-06Force-added build output in a repository whose .gitignore excludes dist/. Roughly nine months stale
SDKCrux/packages/engine-client/dist/index.mjs2026-06-12Also force-added. Carries no .d.ts and no .cjs, so it does not satisfy the main or types fields it appears to back
SDKCrux/packages/factory/dist/index.mjs2026-02-21As above
SDKCrux/coverage-summary/coverage-summary.json2026-06-16A local developer artefact with absolute filesystem paths, accidentally committed. Nothing reads it
SDKCrux/docs/releases/engine-client-1.1.0.md-Documents 1.1.0 while the package is 1.1.1, and headlines a trust-report fix for a route that does not exist (B2)

Three README links in SDKCrux resolve to nothing. Verified 2026-07-27: dev_guides/sdk-assurance.md (README.md:58, :129), docs/architecture/sdk-autonomy.md (:130), and ../ApiCrux/README.md (:131). No docs/architecture/ directory exists and there is no ApiCrux repository in the workspace. A conformance script in the same repository also checks ApiCrux/, so it cannot pass.

6.13 What this page does not claim

Stated as plainly as the defects, because a disclosure with an unstated scope is worth less than one with a stated one.

  • This is not an exhaustive audit of correctness. It records what a source read and a set of reproductions found on 2026-07-27. Absence from this page is not evidence of absence.
  • No claim here is a security finding. None of these defects is a vulnerability. B5 and B6 are supply-chain hygiene: they describe how a bad publish could happen, not one that did.
  • **The runtime behaviour of @cuecrux/client and corecrux-client was verified by type-check and signature inspection, not by an end-to-end run against a live daemon.** The examples in chapters 1 and 2 compile and their signatures match the published artifacts; they were not executed against a running daemon as part of this work, and we say so rather than implying otherwise.
  • Version numbers move. Every version, path count and date here carries its verification date. Re-run the checks below rather than trusting this page indefinitely.

Reproducing the two most important checks takes under a minute:

# B1, do the published typings resolve?
npm pack @cuecrux/engine-client@1.1.1
tar tzf cuecrux-engine-client-1.1.1.tgz | grep 'd\.ts'

# 0.2, what does each registry actually serve?
npm view @cuecrux/client version
npm view @cuecrux/engine-client version
curl -s https://pypi.org/pypi/corecrux-client/json | jq -r .info.version

Sources