The Shared Seam Packages

Status (2026-08-17): @cv/corpus-shared is integrated by CareerVector, JobCache Core and the new JobCache App. Pure participant/privacy/search/snapshot/fact/delivery/lifecycle contracts are shared; each product still owns its routes, authorization and local store. CareerVector remains the first deployed installation. JobCache App is a complete second code installation, but its Worker and D1 are not deployed.

System Architecture establishes a mirror between CareerVector's workspace-private edge and JobCache App's session-private edge. The public products differ; the seam mechanism must not.

Naming and ownership

There are two distinct kinds of “shared” code:

Home Owns
lib/corpus-shared/ (@cv/corpus-shared) pure cross-product contracts and algorithms: participant profiles, privacy envelopes, canonical hashing, fact and delivery types, request keys and immutable result identity
jobcache/shared/ and its existing packages JobCache Core domain: corpus persistence, ads/roles, extraction, search, recommendation and market metrics

@cv/* is the repository's established package namespace and does not mean that JobCache Core belongs to CareerVector. JobCache App and JobCache Core remain JobCache. “Core” is a concern/privacy zone, not a third deployable required by this architecture.

A future seam route/store installer belongs in the existing lib/*-platform family once the CareerVector implementation exposes stable ports. Until then, route code remains in its host rather than pretending an incomplete abstraction is reusable.

What is shared

Row Shared? Boundary
product perspectives no RADAR and JobCache browse/dashboard are different products
public API no workspace authorization and session authorization remain product-owned
result/fact/privacy contracts yes pure @cv/corpus-shared code
local store schema and reduction mechanics yes neutral local-subject model behind a store port
fast and slow collectors yes identical reliability and privacy protocol
seam-facing claim/ack/deliver routes yes reusable installer after store and auth ports are proven
JobCache Core search/recommendation/metrics no JobCache-owned Core implementation

The shareable unit stops at each product's public API and at JobCache Core's domain logic. It does not absorb UI copy, workspace/session authorization, CrateDB queries or ranking.

One coherent participant profile

The host selects one participant kind:

Installation Kind Local identity Pseudonym
CareerVector person workspace_id person_key
JobCache App observer session_id observer_key

Prefix, secret binding, writer/reducer identity and graph kind are derived from that profile, not independently configurable strings. This prevents impossible combinations such as an observer signed with the person secret. The host separately supplies its store, auth and application adapters. Participant references are versioned HMAC values minted by the shared algorithm; only the host retains the inverse map to its local identity.

Product-specific behavior is not represented by boolean flags. If a difference belongs to RADAR or a JobCache dashboard, it stays above the shared boundary.

Privacy contracts

Every outbound envelope is one of:

  • pseudonymous individual — contains a versioned person_key or observer_key and an allowlisted payload;
  • anonymous aggregate — contains no participant key and includes the satisfied cohort threshold and reducer version.

Unknown classes, unknown fields and local product IDs fail validation. Pseudonymous and anonymous payloads are different schemas, not optional fields in one permissive object.

Result identity

The shared layer distinguishes:

Identifier Meaning
request key canonical equivalence key for deduplicating the same requested work
result ID immutable identity of canonical ordered result bytes and all result-changing metadata
local pointer the one current snapshot selected for a workspace/session

The same result ID can never name different bytes. limit, corpus revision, retrieval/ranking version and other result-changing context participate in identity. A public route still authorizes through the local pointer; a deterministic key is not public authorization.

Pull and delivery protocol

The shared contract requires:

  • a typed participant reference minted only by the product edge: new products use person:vN:<sha256>, while CareerVector's already-persisted pk_<40-hex> identity is represented explicitly as compatibility epoch 0 until Core has a tested alias/backfill;
  • a stable SHA-256-derived factId over the complete canonical fact material, including purpose, privacy envelope, occurrence and payload;
  • a leaseToken bound to the authenticated claimant;
  • exact ack partitions: acceptedFactIds plus itemized rejected[{ factId, reason }];
  • durable quarantine for rejected facts and durable ack receipts;
  • a byte-bounded claim, not only an item count;
  • a composite (timestamp, durable source sequence) cursor for every independently mutable event stream;
  • distinct shown and picked streams, so a positive label arriving after a shown acknowledgement is still claimable;
  • a required stable batchId and durable delivery receipt;
  • strict result-row and allowlisted semantic-metadata validation on ingress;
  • an explicit privacy context on delivery: the workspace-specific route accepts a pseudonymous person reference and refuses anonymous aggregates or observer references;
  • endpoint-owned allowlists for fact types, purposes, participant kinds, aggregate dimensions and reducer versions; a producer cannot broaden its domain by choosing a weaker envelope;
  • a named worker credential plus an explicit operation allowlist for claim, ack, deliver, erase, reduce and coverage; the general JobCache control token has no authority on the all-workspace seam;
  • content-derived delivery result IDs when Core does not provide one;
  • immutable result rows and an atomic result-plus-pointer commit.

Cursor advancement follows durable acceptance, never proposal. A retry cannot create a second logical fact or delivery.

Current extraction map

Current code Destination/state
lib/server/src/corpus/contract.ts pure types/algorithms move to @cv/corpus-shared; product integration stays
lib/server/src/corpus/seam.ts verified JobCache Core role-search client plus CareerVector integration; legacy read doors remain interim and direct participant writes fail closed
lib/server/src/corpus/lanes.ts neutral reduction/collector logic is an extraction candidate; CareerVector policy stays
lib/server/src/corpus/scope-all.ts CareerVector store adapter; not cross-product API
api/src/routers/internal-corpus.ts proving claim/ack/delivery implementation with authenticated leases, receipts, composite cursors and immutable grants; later becomes an installer over explicit auth/store ports
migrations/corpus-seam.sql CareerVector installation, including composite cursors and the participant-erasure outbox/receipt tables
api/src/routers/radar*.ts, ui/.../JobRadar.svelte CareerVector product code; stays
jobcache/shared/src/corpus-lifecycle.ts Core participant tombstone, fail-closed writers, retryable cleanup and deterministic receipt implementation
jobcache/api/ second edge installation: observer profile, opaque session/D1 adapter, immutable local grants, reductions, delivery and erasure outbox
jobcache/mcp/ thin JobCache perspective over the App API; deliberately owns no Core secret
JobCache Core corpus/search code stays under JobCache shared/domain packages and the current jobcache/interface Core facade

The two edge installations intentionally do not share route handlers yet. The shared unit is the strict pure contract and content-identity machinery; D1 stores and public authorization remain product-owned. Extract a generic installer only when the now-concrete ports remain identical under further use, rather than erasing real product differences pre-emptively.

Separate from Sporewright and the LLM system

Sporewright owns generic addressed routing, uncertainty, curiosity and feedback mathematics. CareerVector's LLM routing is a separate CareerVector application. Neither owns corpus privacy envelopes, D1 seam schemas, collectors or JobCache Core persistence.

Source: wiki/content/architecture/SHARED-SEAM-PACKAGE.md