JobCache ingestion and CareerVector consumer AI
These are two independent systems that share Sporewright's mathematics. They do not share a tensor, queue, identity, feedback log, or persistence boundary.
- JobCache routes public corpus work and converges on one shared CrateDB.
- CareerVector routes private product work for one workspace and learns in CareerVector D1.
- In both systems the operator is a root-policy and resource owner. It is not an address segment and it is not a provider. Provider/model pairs and execution classes are the options that do work.
- In both systems an attempt is evidence about a decision. It is not part of the address and it does not become a second scheduler beside the tensor.
1. JobCache: collection, fold, and enrichment
The solid paths below are present in the code. Dashed amber paths are the next extension: Cloudflare AI enrichment using the same task, receipt, deposit, and fold protocol rather than a separate data pipeline.
What the code actually does
| Concern | As-built implementation |
|---|---|
| queue inputs | crates/sporewright-server/src/queue.rs derives due discovery and observation targets from CrateDB |
| routing | crates/sporewright-server/src/tick.rs and router.rs resolve execution classes and publish bounded work |
| delivery | rabbitmq.rs owns private per-class queues; main.rs /lease consumes and conditionally stamps the Crate ledger |
| participant write authority | object_store.rs mints one expiring S3 PUT for one deposit key; no DB, AMQP, or bucket credential |
| durability | Corbet S3 is canonical; raw_overflow.rs verifies encrypted-raw B2 promotion, while structured deposits use the same key in either inbox |
| corpus fold | ingest.rs asks CrateDB itself to fetch each object with COPY _deposit_staging, then performs set-based upserts |
| current projections | routing outcomes, current observation/field state, facts, searchable ad columns, and trust evidence |
| cleanup | only the exact folded lease is cleared; only then is the transient deposit deleted |
| enrichment substrate | jobcache/migrations/cratedb/schema.sql has embed-text, tasks, work_cache, text_chunks, embeddings, and job_vec; the current orchestrator queue does not yet activate that path |
The new ingestion path is deliberately much thinner than the compatibility
FactTreeStore command drain. Extraction participants already produce canonical
facts before depositing. Cloudflare AI therefore belongs after the current
observation has folded, where the content hash is stable and repeated scrapes
cannot spend another inference on identical text.
D1, Durable Objects, Queues, or none?
None for the first version. The existing S3 mailbox and Crate task ledger
already provide durable ingress and idempotency. The Workers AI asynchronous
Batch API already queues non-interactive embedding work, returns a request ID,
and accepts a payload up to 10 MB. The operator executor can persist that request
ID in work_cache, poll it, and return a normal result deposit. That is the
shortest path with the fewest truths.
| Candidate | Decision | Reason |
|---|---|---|
CrateDB tasks + work_cache |
use | task state, content hash, model epoch, result pointer, and retries stay beside the corpus they describe |
| Corbet S3 deposit | use | already the authorized, replayable write boundary into the one corpus writer |
| Workers AI async Batch API | use | the provider already queues the batch; it avoids another Worker, database, and retry protocol |
| Cloudflare Queue | reserve | the correct Cloudflare primitive if we later need push delivery or provider-independent fan-out; it is still another at-least-once ledger today |
| D1 | do not put in the ingest path | a D1 database is another single-writer SQLite truth and the free database is capped at 500 MB; duplicating public ad bodies before CrateDB buys nothing |
| Durable Object | do not use for bulk staging | it is a single-threaded coordination actor, useful for a live shared token bucket or session—not a warehouse or ingestion queue |
Cloudflare's current documentation supports these boundaries: Workers AI asynchronous Batch API, Queue batching and retries, D1 limits, and Durable Object coordination rules.
The vector-space gate
CrateDB currently declares 1024-dimensional corpus vectors while CareerVector's live query embedder emits 768-dimensional EmbeddingGemma vectors and explicitly skips kNN on a width mismatch. Corpus and query vectors must use one pinned model epoch; two embedding models do not share a meaningful vector space.
The least disruptive candidate is Cloudflare's 1024-dimensional
@cf/qwen/qwen3-embedding-0.6b, because it fits the existing Crate columns and
has a longer input window than the old BGE-M3 schema assumption. It must first
beat the existing 400-ad retrieval benchmark. If it does not, migrate the Crate
columns to the measured winner's width; never silently project, pad, or compare
different spaces. See Cloudflare's current model dimensions.
2. CareerVector: consumer-facing Smart Routing
Here the provider APIs really are analogous to JobCache's workers. More exactly, our executors are workers: a local WebLLM runtime, a trusted workspace peer, a BYOK connector, or the platform operator's Included AI broker. Each executor offers provider/model options and returns receipt-bound evidence.
Who is what?
| Thing | Role in the model | Not this |
|---|---|---|
| platform operator | writes root policy, owns shared keys/quotas and quality/privacy floors | not an address layer; not an AI provider |
| workspace owner | writes authoritative workspace policy, consent, BYOK and curiosity budget | not merely another learned signal |
| address | workspace.capability.stage.consumer.instance?; selects the interacting branch |
not provider, model, key, attempt number, or runtime host |
| option | a routable provider/model execution route | not an individual browser or historical call |
| executor/worker | local runtime, trusted peer, BYOK connector, or Included AI broker that realizes an option | not the tensor itself |
| attempt | one realized call, classified into normalized outcome dimensions | not an alternative policy engine |
The platform operator therefore belongs in the picture, but above the field and beside the server-owned executor—not inserted into every semantic address.
As-built gap that matters before shared Included AI
The adaptive route, receipts, attempt chain, learning projector, D1 budget
authority, server /llm/* execution, and consent-first WebLLM runtime exist.
Included AI and workspace-peer execution are not yet complete.
There is also one concrete security seam to correct first:
resolveChainConfigForWorkspace()constructs an executableproviderKeysmap containing both workspace BYOK and entitled server keys inlib/server/src/chain-config.ts.GET /chaincurrently returns that entire payload to the browser. The route is inapi/src/routers/misc.ts.- The dashboard happens not to retain
providerKeys, but transport already disclosed them; “the UI ignores it” is not a boundary.
Split the type into two views:
- internal executable route: keys plus provider/model chain, readable only by the server executor;
- public routing view: opaque route IDs, availability, consent/readiness, ranked explanation, and budget state—never shared credentials.
Browser-owned BYOK can remain browser-executable where the user explicitly chose that mode, but it must be loaded from a workspace-owned secret seam or local vault and merged client-side—not bundled with operator credentials in the public route descriptor. Operator-owned Included AI keys never cross the server boundary. Prompts, outputs, CV data, workspace identity, and LLM feedback never enter the JobCache CrateDB pipeline.
Consequence
The reusable principle is not “put every worker behind one central service.” It is:
one addressed field composes top-down policy with bottom-up evidence; every executor pulls or realizes work under a causal receipt; every result returns normalized feedback along that same path.
JobCache applies it to public data collection and enrichment. CareerVector applies it to private AI work. The operator participates in both as policy and resource authority, while the actual execution remains distributed.