Skip to content

Reconcile main with published npm line (v0.2.86); bump to 0.2.87 - #31

Merged
andrei-hasna merged 7 commits into
mainfrom
reconcile/knowledge-main-to-0.2.86
Jul 24, 2026
Merged

Reconcile main with published npm line (v0.2.86); bump to 0.2.87#31
andrei-hasna merged 7 commits into
mainfrom
reconcile/knowledge-main-to-0.2.86

Conversation

@andrei-hasna

Copy link
Copy Markdown
Contributor

Why

main had diverged from the deployed npm runtime. Fixes based on main were
targeting stale code the published runtime no longer uses.

What

Merge the published release tag npm/knowledge/v0.2.86 into main (real merge
commit, not cherry-pick) so:

Verification (clean local mode, station env vars unset)

tree tests pass fail
origin/main 175 170 3 (machine-sync-ledger timeouts)
npm/knowledge/v0.2.86 182 175 5 (adds MCP-stdio + sync-status timeouts)
this reconcile 184 177 5 (identical to published baseline)

Follow-ups (do NOT re-fix against old main first)

After merge + publish of 0.2.87, rebase the open audit PRs (#25/#26/#27 and the search
stack #28-30) onto the reconciled main before re-review — they would otherwise re-target
code the published runtime no longer uses.

…re + ApiStore)

Introduce src/item-store.ts: a single ItemStore interface with two transports
(LocalItemStore over the on-box JSON store, ApiItemStore over the /v1 HTTP API +
bearer key via @hasna/contracts). A mode resolver picks the transport from the
client-flip env (HASNA_KNOWLEDGE_API_URL + HASNA_KNOWLEDGE_API_KEY and/or
*_STORAGE_MODE); an explicit --store/store_path override pins local.

Route every knowledge-item CLI command and MCP item tool through the Store,
deleting the per-command if(cloud){...}else{local} split-brain branches. self_hosted
and cloud both use the identical ApiItemStore client path.

Pin @hasna/contracts to the fleet-dist tarball so bun install resolves (0.5.x is
not on the public registry). Bump version to 0.2.84.

Build green; full test suite 173/173 pass.
…e registry descriptor truthful

The parallel RemoteKnowledgeClient (src/remote-client.ts) was a second HTTP
transport using raw fetch, entirely separate from the sanctioned ApiStore
(@hasna/contracts client wrapped by src/cloud-store). Its data methods were
never invoked by any CLI command, MCP tool, or SDK method and POSTed to
/api/v1/knowledge/* endpoints the server never implemented. Removed the class,
service.remoteClient(), the SDK remote.client/remote.contract block, the MCP
knowledge_storage remote_contract field, and the dead 'remote status/contracts'
test coverage.

The server-served /v1/registry descriptor advertised the same phantom
/api/v1/knowledge/* endpoints. Replaced remote-client.ts with a truthful
src/registry-contract.ts that declares only the endpoints src/serve actually
routes (/v1/registry, /v1/notes CRUD, /health, /version, /ready, /openapi.json).

Every knowledge-item path routes through the ItemStore (Local/Api) and the
sqlite catalog stays gated behind assertLocalCatalogMode; no raw fetch or
ungated Database open remains on the client. Full suite: 178 pass.
…mmands

In self_hosted/cloud (api) mode the shared corpus is the cloud knowledge-items.
The corpus retrieval commands (search/ask/build/context) already route over
those items; this closes the remaining cloud-mode gaps so no corpus command
silently touches local storage or throws the local-catalog guard by surprise:

- mcp knowledge_get (getKnowledgeRecord): in api mode restrict `auto` to the
  cloud-backed `item` kind and refuse an explicit local-catalog kind with a
  clear message, instead of falling through to sqlite snapshots that throw.
- cli ask/build: skip ensureStore() local db.json seeding in api mode.
- inventory (cli + mcp): add service.cloudInventory() so `inventory` reports
  the shared cloud item corpus via the cloud transport instead of reading the
  local db.json / opening the local sqlite catalog (which refused on flipped
  fleet machines with a leftover knowledge.db). Extracted itemOnlyInventory()
  shared by the local no-db path and the cloud path.

The local RAG-indexing pipeline (sources/chunks/embeddings/wiki/sync/machines/
reindex) remains local-only behind the single openKnowledgeDb choke-point guard
— it has no cloud server counterpart (serve.ts exposes only notes CRUD), so it
refuses loudly in cloud mode (never split-brain) rather than routing.

Adds tests/cloud-inventory.test.ts. All 180 tests pass; build + tsc clean.
…tore in all 3 modes

The SDK exposed no item CRUD and its inventory() called the local-sqlite
service.inventory(), so app code could not route items through the unified
Store and diverged from the CLI/MCP in api mode. Fix at the source:

- KnowledgeService: add itemStore() + item CRUD (list/get/create/update/
  delete/deleteMany) routing through resolveItemStore, and resolveInventory()
  that dispatches to the cloud item corpus in api mode and the local catalog
  otherwise.
- SDK: expose client.items.* and make client.inventory() async via
  resolveInventory so it never reads a divergent store.
- CLI + MCP + project-panel: use the single resolveInventory dispatch instead
  of per-caller cloud/local branching; project-panel is now async so it no
  longer hits the local-catalog guard on a flipped box.
- index.ts: export the Store contract (ItemStore, KnowledgeItem,
  resolveItemStore, cloud-store helpers) for SDK consumers.
- Rebuild bundles + dist types.
…ent DSN surface

Enforce the ONE-Store standard across the remaining leaks the adversarial
review found:

- SPLIT-BRAIN READ: itemResources() (MCP knowledge://project/items/{id} list)
  read the local db.json directly via readStoreLocked/loadStore, with no
  api-mode branch — enumerating stale local items in self_hosted/cloud while
  every item tool routed to the cloud, and creating a spurious on-box db.json
  during the flip. Route it through itemStoreFor(...).listAll() like ok_list;
  delete the now-dead readStoreLocked/loadStore/withLock imports.

- DEAD/DUPLICATE TRANSPORT: remove the generated raw-fetch KnowledgeApiClient
  (x-api-key) that was exported on the SDK surface but used by nothing (the
  ApiStore uses @hasna/contracts). Delete src/generated/knowledge-api-client.ts,
  its generator scripts/generate-sdk.mjs, and the generate:sdk build step.

- CLIENT-SURFACE DSN HELPERS: delete getStorageDatabaseUrl / getStorageDatabaseEnv
  / getStorageDatabaseEnvName / STORAGE_DATABASE_ENV / KNOWLEDGE_STORAGE_ENV /
  KNOWLEDGE_STORAGE_FALLBACK_ENV / StorageEnv at the source and drop the
  DATABASE_URL reporting fields (configured/env/activeEnv) from getStorageStatus.
  A raw DB DSN is never a client concept. Update tests + README accordingly.

Build + full test suite (180) green.
…rop dead remote command

- context-pack: truncateText reserved maxChars-1 then appended a 3-char
  ellipsis, yielding maxChars+2. fitPackToBudget's length>N truncation
  branches could never satisfy their threshold, so the budget while-loop
  spun forever and 'context pack'/knowledge_context_pack hung in local
  mode. Reserve the ellipsis width so results never exceed maxChars.
- project-panel: hasUriScheme accepted any scheme (generic scheme:) while
  the contract only allows a fixed list, so cloud knowledge items carrying
  URLs like s3:// blew up parseContract with a ContractValidationError.
  Delegate to the contract's UriSchema and guard the notes evidenceRef.
- remote: the prior refactor removed service.remoteContract/remoteClient
  (and the SDK/MCP surface + tests) but left the CLI 'remote contracts|
  status' handler calling them, so both crashed with 'remoteContract is
  not a function'. Removed the dead command, help, and completions.
- Regression tests for the fit loop and unsupported evidence URI schemes.
… divergence

main had diverged from the deployed npm runtime. The published line carried a
Store-unification + cloud-routing refactor (6 commits) that never landed on main,
while main carried two CLI fixes (#23, #24) the published line lacked. This merge
re-converges both histories with a real merge commit so ancestry is preserved
(future `git merge-base --is-ancestor` passes) and no commits are lost.

Brings in from npm/knowledge/v0.2.86:
- refactor(store): unify item CRUD behind one Store (LocalStore + ApiStore) (1506111)
- refactor: remove dead raw-fetch RemoteKnowledgeClient, truthful registry (2b6bd21)
- fix: close residual cloud-mode routing gaps for catalog commands (2d235a9)
- fix: route SDK item CRUD + inventory through the unified Store in all 3 modes (92c3fcc)
- fix: close split-brain read + drop dead API client and client DSN surface (5213a51)
- fix: stop context-pack hang, repair cloud project-panel, drop dead remote command (8daa0ea)

Retains on top of the refactor:
- fix(cli): don't leak internal Error stack on usage/validation errors (#23)
- fix(cli): make '<sub> --help' print per-command usage (#24)

Both #23/#24 regression tests pass on the merged tree. Version bumped to 0.2.87
(> published 0.2.86) with CHANGELOG so npm and main reconverge on publish. Bundled
artifacts (bin/, dist/) rebuilt from the merged source.
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