P9-SDK-Discoverability-AI-Builder.md.
/build planner moves from a single "stuff all ~90 SDKs
into one prompt" call to a retrieve-then-compose pipeline with a foundation-tier +
dependency-closure resolver (so identity/AIM always surfaces); manifests gain
per-endpoint payload schemas and an ingest capability dimension for ETL agents.
Retrieval embeddings stay on the local bge-small model, so a generation-LLM
provider change never breaks discovery. §1–§13 describe the original P9 design; §14 is the
P9.2 delta that supersedes the file-dump path.
ProjexCloud's ~70 cross-cutting SDKs (identity, tenancy, billing, audit, AI gateway, evidence chain, geo, dispatch, …) are useless if a tenant developer has to grep source to find them. P9 fixes that by turning the SDK catalog into a machine-readable, AI-consumable platform. The architecture is four cooperating layers:
sdk-capability.json describing what it provides,
consumes, and when to use it. CI rejects publishes without one.searchByIntent() across the SDK universe.projex init wires Claude Code / Cursor / Windsurf
to the local MCP. Cloud path — /build chat surface inside the tenant
workspace, driven by the same MCP tools.After P1–P8 the SDK stack is broad enough that the bottleneck to ProjexCloud's growth is no longer features — it is composability. A tenant who signs up today gets an empty workspace; the only way to actually build something is to clone the monorepo and learn ~70 packages. That doesn't scale to thousands of tenants.
The MCP ecosystem (Anthropic, Cursor, Windsurf, Cline) has converged on a stable protocol over the last 6 months. Building on it now means every AI coding tool, present and future, becomes a ProjexCloud entry point with no per-tool plugin. Delay means: every onboarding burns operator hours, the AI-native window closes as competitors ship their own MCP catalogs, and the P8 vertical packs (Healthcare, FinServ, Public Sector) have no surface to materialize on.
projex_registry_search_sdks
· scaffolds working code. Onboarding: minutes.Every SDK ships an sdk-capability.json. Without one, CI refuses to publish
it. The manifest is the contract — it tells the registry, MCP, agent, and downstream
tooling exactly what the SDK provides, consumes, when to use it, and what
compliance posture it carries.
A build-time scanner walks packages/* and services/*, validates
every manifest, and emits a single normalized catalog plus an ANN vector index over the
hand-written scenarios. This is what makes "search by intent" possible.
Two binaries, one wire protocol. The hosted half (per-region, behind api-gateway) owns tenant scope and writes. The local half (CLI-bundled, stdio) caches the public catalog and proxies writes. AI tools see one tool list; the split is invisible to them.
CLI path: projex init auto-detects installed AI tools and writes
their MCP configs. The developer prompts their AI tool; the AI calls registry tools and
writes code. Cloud path: the tenant workspace exposes a chat surface at
/build; a hosted agent calls the same MCP tools server-side and returns a
working URL.
The sdk-capability.json file is the foundation of the whole stack. If a manifest
doesn't exist, the SDK doesn't exist to the AI. If a manifest is wrong, the AI suggests it for
the wrong things. The schema is versioned (schema_version: "1.0"); future bumps
are backward-compatible or carry a migration.
{
"schema_version": "1.0",
"name": "sdk-consent",
"version": "1.4.2",
"summary": "GDPR/CCPA consent receipts with cryptographic proof, double opt-in flows, and revocation propagation.",
"tags": ["privacy", "gdpr", "ccpa", "consent", "compliance"],
"provides": {
"endpoints": ["POST /v1/consent", "GET /v1/consent/:subject"],
"events": ["consent.granted.v1", "consent.revoked.v1"],
"models": ["ConsentReceipt", "ConsentScope"],
"hooks": ["beforeConsentGrant", "afterConsentRevoke"],
"ui_components": ["<ConsentBanner/>", "<ConsentPreferences/>"]
},
"consumes": {
"events": ["identity.subject.created.v1"],
"infra": ["sdk-audit", "sdk-vault"],
"config_keys": ["CONSENT_DEFAULT_TTL_DAYS"]
},
"scenarios": [
{
"id": "gdpr-receipt",
"title": "Issue a GDPR-compliant consent receipt",
"when_to_use": "Customer opts in to marketing email; you must store proof.",
"example_code": "await consent.grant({ subject, scope: 'marketing.email' });",
"expected_outcome": "Hash-anchored receipt; event emitted; revocation honored within 30 days."
}
/* … 2–4 more scenarios … */
],
"compliance_posture": {
"regimes": ["GDPR", "CCPA", "SOC2"],
"notes": "Receipts hash-anchored into sdk-audit chain."
},
"pool_placement": "app",
"pricing_skus": [{ "sku": "consent.receipt", "mode": "metered", "unit_description": "per receipt issued" }],
"links": {
"readme": "packages/sdk-consent/README.md",
"source": "packages/sdk-consent",
"prd_section": "P3#consent"
}
}
scenarios[] block is the only part of the
manifest that gets embedded into the vector index. summary and tags
help keyword search; scenarios are what make "I need consent receipts for GDPR" land
on sdk-consent with score ≥ 0.7. Quality bar in CI: ≥ 3 scenarios, no
TBD placeholders, compliance posture filled.
Hand-writing 70 manifests was the single biggest risk (R-1 in the PRD). It is mitigated by splitting the work:
npx @projexlight/sdk-capability scaffold introspects package.json,
route registrations, events.ts, and DB migrations to emit the
provides / consumes / links blocks deterministically.
TBD placeholders.
# .github/workflows/registry-validate.yml (excerpt)
- name: Validate every manifest
run: pnpm registry:validate
# Fails the build with structured errors:
# MANIFEST_MISSING packages/sdk-foo (no sdk-capability.json)
# MANIFEST_INVALID packages/sdk-bar/v1.0.0 (summary > 500 chars)
# SCENARIO_TBD packages/sdk-baz#sc-2 (example_code contains "TBD")
This gate codifies Architecture Doctrine §C — Capability-First SDK Authoring: every new SDK ships its manifest at v1.0. No exceptions.
The registry is two artifacts produced by one build job:
| Artifact | Format | Purpose | Refresh |
|---|---|---|---|
dist/registry.catalog.json |
Normalized JSON | Every manifest + derived dependency graph (who-consumes-whose-events). Drives
list(), get(), findCompatibleSdks(). |
On any manifest change · RPO ≤ 1h via S3 |
dist/registry.embeddings.bin |
ANN vector index (HNSW) | Vectors keyed by (sdk_name, scenario_id). Powers
searchByIntent(query, top_k). |
Deterministic per content hash · CI-cached |
bge-small-en-v1.5
Open question Q-1 (locked 2026-05-25) chose bge-small-en-v1.5 loaded via
@huggingface/transformers as INT8-quantized ONNX. Three properties matter:
search_sdks still works with no network — required for AC-12.text-embedding-3-small within noise, while saving ~$140/mo at projected traffic.// @projexlight/sdk-registry
export interface Registry {
list(): SdkCapabilityManifest[];
get(sdk_name: string): SdkCapabilityManifest | null;
searchByIntent(query: string, top_k?: number): Promise<RegistryHit[]>;
findCompatibleSdks(sdk_name: string): string[];
getScaffold(sdk_names: string[], app_name: string): ScaffoldTree;
}
export function loadRegistry(catalogPath?: string): Promise<Registry>;
pnpm registry:build
twice on identical inputs produces byte-identical artifacts. This rules out
non-deterministic float math in the embedding pipeline (INT8 quantization is required, not
optional) and lets CI cache the index across PRs.
The MCP layer is where the architecture's most consequential design decision lives: split into two cooperating binaries that share one wire protocol.
| registry-mcp HOSTED | registry-mcp-local LOCAL | |
|---|---|---|
| Lives | Per-region, behind api-gateway | Developer's machine (Docker or npx) |
| Transport | SSE over HTTPS (Q-2 decision) | stdio (the MCP default) |
| Catalog source | S3 (canonical) | ~/.projex/cache/ (ETag-refreshed daily) |
| Authoritative for | tenant-scoped reads + ALL writes | public reads only |
| Auth | tenant API key → SixLayer JWT | same key, stored in OS keychain |
| Emits audit events | yes (every tool call) | no (writes are proxied) |
| Distribution | internal service binary | projexcloud/registry-mcp-local (Docker) · @projexlight/registry-mcp-local (npx) |
projex_registry_search_sdks(query)projex_registry_get_manifest(sdk_name)projex_registry_get_example(sdk_name, scenario_id)projex_registry_list_compatible_sdks(sdk_name)projex_registry_list_blueprints()projex_registry_get_blueprint(blueprint_id)
projex_registry_scaffold(sdks[], app_name, target_dir?)projex_registry_deploy(scaffold_id, env)projex_registry_list_my_sdks() tenant-scopedprojex_registry_list_my_blueprints() pack-filteredprojex_registry_request_pack_upgrade(pack_id)
When the hosted side is unreachable, the local MCP gracefully degrades per FR-MCP-L5:
~/.projex/cache/. AI tool notices nothing.{ status: "queued", queued_id: ... }.
The CLI surfaces projex deploy --queue hints. On reconnect,
projex registry drain replays queued operations.AC-12 enforces this in chaos tests: network down → all reads succeed → write queues → reconnect → queue drains within 30 s with byte-identical results to an online write.
This is the path most tenant developers will take. It is built around zero-configuration auto-discovery: the developer never reads the SDK list, never edits MCP configs by hand, and never has to know which region their tenant lives in.
npx init to deployed URL. The developer
types two commands and one prompt; everything else is automatic.projex init actually writes// .claude/mcp.json — auto-generated by `projex init`
{
"mcpServers": {
"projex_registry": {
"command": "npx",
"args": ["-y", "@projexlight/registry-mcp-local"],
"env": {
"PROJEX_HOSTED_MCP": "https://mcp.us-east.projexcloud.com",
"PROJEX_TENANT_ID": "tenant_01HXY...",
"PROJEX_KEYCHAIN_REF": "projex.tenant_01HXY"
}
}
// If Projexlight is detected on this machine, projex_dev_mcp and
// projex_test_mcp blocks are appended here too (see §10 Cohabitation).
}
}
| Command | What it does | Talks to |
|---|---|---|
projex login | OAuth device flow · stores refresh token in OS keychain | Identity SDK |
projex init <app> [--blueprint] | Skeleton repo + AI tool MCP configs + optional blueprint scaffold | Local FS · Registry |
projex install <sdk> | Adds SDK to package.json · drops starter snippet | Local MCP (read) |
projex blueprint list | apply <id> | List blueprints or run installer (clarifying Qs → templates → migrations → seed → smoke tests) | Hosted MCP (write) |
projex deploy [--env] | Package · upload · migrate · roll back on failure (AC-10) | Hosted MCP + Tenant pool |
projex logs [--tail] | Stream deployed-app logs | Tenant pool |
projex registry refresh | Force-pull latest catalog into local cache | Hosted (ETag GET) |
projex registry drain | Replay queued offline writes | Hosted |
npx-only for v1 (Q-6 decision). Most P9 users already have Node 20+.
Non-Node users get a one-liner installer that puts Node on the box and aliases
projex → npx @projexlight/cli; Node becomes invisible. Native
binaries (signed, auto-updating) deferred to P10.
The Cloud Builder is the same substrate aimed at a different persona: a non-developer in a
tenant who wants a working application without filing an IT ticket. It lives at
/build inside the tenant workspace.
The agent does not enforce safety. The agent is treated as untrusted prompt-pipe; real enforcement happens server-side in two places:
cloud_builder.autonomous = true with a per-session
$ ceiling (default $10). Destructive operations always require confirm — autonomous
mode never overrides this.
| Tier | Included builds / month | Overage | Notes |
|---|---|---|---|
| Trial | 3 | Hard cap — block + upgrade prompt | One-hour iteration window per session — refinements don't double-charge. |
| Pro | 20 | $2/build OR LLM token pass-through, lower wins | Same iteration rule. |
| Enterprise | Unlimited | — | Subject to abuse SLO. |
This is the part the user most cares about. Auto-discovery isn't one mechanism — it is a chain of four discrete handshakes that compose into the experience of "the AI just knew what was available." Each handshake is independently observable and independently testable.
When projex init runs, the CLI looks for marker files in the user's home directory:
// @projexlight/cli — detection logic (sketch)
const DETECTORS = {
"claude-code": () => existsSync(path.join(os.homedir(), ".claude")),
"cursor": () => existsSync(path.join(os.homedir(), ".cursor")),
"windsurf": () => existsSync(path.join(os.homedir(), ".windsurf")),
"cline": () => existsSync(path.join(os.homedir(), ".cline")),
// Projexlight cohabitation marker — see §10
"projexlight": () => existsSync(path.join(os.homedir(), ".projexlight", "config.json")),
};
const installed = Object.entries(DETECTORS).filter(([_, fn]) => fn()).map(([k]) => k);
The CLI writes configs only for the tools actually installed. Order matters: detection is per-machine, but every AI client ends up seeing the same MCP tool catalog.
Each AI tool has its own config path; the CLI knows them all and writes the matching block.
Critical property: the CLI is the only sanctioned writer of these files. If a user
hand-edits, the next projex init warns rather than overwrites. This avoids the
"two MCPs in Claude Code config confuse non-technical users" failure mode (R-11).
| AI Tool | Config path | Format |
|---|---|---|
| Claude Code | ~/.claude/mcp.json + ./.claude/mcp.json | JSON · mcpServers |
| Cursor | ~/.cursor/mcp.json | JSON · mcpServers |
| Windsurf | ~/.codeium/windsurf/mcp_config.json | JSON · servers |
| Cline | ~/.cline/mcp_settings.json | JSON · mcpServers |
This handshake is standard MCP, no ProjexCloud customization. When the IDE starts, it spawns
npx @projexlight/registry-mcp-local as a stdio child process, exchanges the MCP
initialize handshake, then calls tools/list. The local MCP responds with the
tool catalog defined in §6.3 — read tools backed by the local cache, write tools that proxy
to the hosted side.
// MCP initialize handshake (excerpt of stdio JSON-RPC)
> { "jsonrpc": "2.0", "id": 1, "method": "initialize",
> "params": { "protocolVersion": "2025-03-26",
> "capabilities": { "tools": {} },
> "clientInfo": { "name": "claude-code", "version": "1.x" } } }
< { "jsonrpc": "2.0", "id": 1, "result":
< { "protocolVersion": "2025-03-26",
< "serverInfo": { "name": "projex-registry-local", "version": "1.0.0" },
< "capabilities": { "tools": { "listChanged": true } } } }
> { "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
< { "jsonrpc": "2.0", "id": 2, "result":
< { "tools": [
< { "name": "projex_registry_search_sdks", "description": "...", "inputSchema": { ... } },
< { "name": "projex_registry_get_manifest", "description": "...", "inputSchema": { ... } },
< { "name": "projex_registry_scaffold", "description": "...", "inputSchema": { ... } },
< ...
< ] } }
This is the handshake users feel. When the developer types
"I need consent receipts for GDPR", the AI tool decides (based on the tool
descriptions in §9.3) to call projex_registry_search_sdks(query: "consent receipts for GDPR").
The local MCP:
bge-small-en-v1.5 in-process (~5 ms).(sdk_name, scenario_id) vectors (~2–10 ms).// Sample response (top 3 of K=5)
{
"hits": [
{ "sdk_name": "sdk-consent", "scenario_id": "gdpr-receipt", "score": 0.91,
"summary": "GDPR/CCPA consent receipts with cryptographic proof...",
"why_match": "scenario 'gdpr-receipt' matches 'consent receipts for GDPR'" },
{ "sdk_name": "sdk-privacy", "scenario_id": "dsar-flow", "score": 0.74, "...": "..." },
{ "sdk_name": "sdk-audit", "scenario_id": "evidence-anchor","score": 0.68, "...": "..." }
]
}
AC-3 enforces this works: a 50-query test suite must hit precision@3 ≥ 0.9 with the target SDK in the top 3. Quarterly catalog-quality audits widen the suite to 200 queries and trigger manifest rewrite PRs when precision regresses.
sdk-policy, not in the prompt.Both paths end up in the same place: a scaffolded application running in the tenant's app pool, with every step in the audit chain, every tool call metered, and every guardrail enforced server-side. The difference is the orchestrator (developer-with-IDE vs. agent-with-prompt), not the substrate.
ProjexCloud's new registry-mcp-local is a peer, not a replacement, of the
existing Projexlight MCPs at projex_mcp/projex_dev_mcp and
projex_mcp/projex_test_mcp. They expose disjoint tool catalogs and zero-collision
names.
| MCP | Tool prefix | Catalog | Lives | Auth |
|---|---|---|---|---|
projex_dev_mcp |
projexlight_* |
Projexlight PRD/feature/scenario/task ops · LLM review · autofix · embedding | Docker, local | Projexlight project API key |
projex_test_mcp |
projexlight_* |
UI test recorder · API functional-test runner | Docker, local | Projexlight project API key |
registry-mcp-local NEW |
projex_registry_* |
ProjexCloud SDK catalog: search · manifest · scaffold · deploy | Docker or npx, local | ProjexCloud tenant API key |
registry-mcp NEW |
projex_registry_* |
Same wire surface · tenant-scoped + write-authoritative | Hosted, per-region | ProjexCloud tenant API key |
When projex init detects ~/.projexlight/config.json, it writes a
single .claude/mcp.json (and equivalents) that registers both Projexlight
MCPs and the new registry-mcp-local. The AI client sees all three in one tool
list and routes calls by name.
// .claude/mcp.json — Projexlight + ProjexCloud cohabiting
{
"mcpServers": {
"projex_registry": {
"command": "npx", "args": ["-y", "@projexlight/registry-mcp-local"],
"env": { "PROJEX_HOSTED_MCP": "https://mcp.us-east.projexcloud.com" }
},
"projex_dev": {
"command": "docker", "args": ["run", "--rm", "-i", "projexlight/projex_dev_mcp:latest"]
},
"projex_test": {
"command": "docker", "args": ["run", "--rm", "-i", "projexlight/projex_test_mcp:latest"]
}
}
}
AC-13 enforces this works: load all three in one Claude Code session, invoke
projex_registry_search_sdks and projexlight_create_feature in the
same conversation, both succeed.
The prd-management blueprint (FR-DF-1..5) rebuilds Projexlight's PRD/feature/
scenario/task surface as a vertical app composed from ProjexCloud SDKs. Projexlight retains
its proprietary AI-review IP (as a private SDK), but every cross-cutting concern
(multi-tenancy, identity, billing, audit, webhooks, approvals, RAG search, AI gateway) is
delegated to ProjexCloud SDKs.
registry.tool.invoked.v1 with full args + outcome.
AC-7 requires that an operator can reconstruct an entire build session from this stream.
Cloud builder transcripts retained 30 days.projex deploy snapshots the tenant pool before
migration; failure auto-rolls-back to that snapshot (AC-10 chaos test). No orphaned
tables, no half-written rows.| Component | Layer | Lives | Owns |
|---|---|---|---|
@projexlight/sdk-capability | L1 | npm package | Manifest schema · validator · scaffold CLI |
sdk-capability.json | L1 | Every SDK's package root | The contract — what an SDK is, in JSON |
@projexlight/sdk-registry | L2 | Build-time + runtime npm package | Catalog builder · embedding index · search API |
dist/registry.catalog.json | L2 | S3 (canonical) + ~/.projex/cache/ (mirror) | Normalized manifests + dependency graph |
dist/registry.embeddings.bin | L2 | S3 + local cache | HNSW vector index over scenarios |
services/registry-mcp | L3 | Per-region hosted service | Tenant-scoped reads + ALL writes · SSE |
@projexlight/registry-mcp-local | L3 | Developer machine · stdio | Cached reads · write proxy · offline mode |
blueprints/ | L3 | Build-time YAML + templates | Declarative SDK compositions |
@projexlight/cli | L4 (CLI) | Developer machine | projex init/install/blueprint/deploy/logs |
apps/cloud-builder | L4 (Cloud) | Tenant App Pool | /build chat surface · agent driver |
sdk-agent-runtime | P6A dep | Tenant pool | Drives cloud builder agent loop |
sdk-ai-gateway | P6A dep | Per-region | LLM call abstraction + per-tenant token caps |
sdk-policy | P3 dep | Per-region | Pack guardrail decisions (HIPAA/FinServ/PubSec) |
sdk-audit | P1 dep | Evidence pool | Hash-anchored event chain |
sdk-meter | P4 dep | Per-region | SKU metering · soft+hard caps |
| Dimension | Target |
|---|---|
Latency · search_sdks p99 | 300 ms cold · 80 ms warm |
| Latency · blueprint apply (5-SDK) | ≤ 60 s end-to-end |
Latency · projex deploy (≤50 files) | ≤ 90 s p99 |
| Throughput · MCP tool calls | 1000 RPS aggregate · 100 RPM/key |
| Availability · hosted registry MCP | 99.9% monthly |
| Durability · registry catalog | RPO ≤ 1h · rebuildable from source ≤ 10 min |
| Time-to-URL (cloud builder, non-dev) | ≤ 5 min (AC-5) |
| Time-to-working-app (CLI, dev) | ≤ 45 min (AC-4, blueprint + 1 prompt) |
Architecture-v3.1.html — overall ProjexCloud architecture & doctrinesSDK-Build-Plan-v3.1.html — SDK roadmap that ~70 manifests will describeAgenticIntegration-v3.1.html — agent runtime, AI gateway, MCP bridge detailsProjectStructure-v3.1.html — monorepo layout (where manifests live)prd/P9-SDK-Discoverability-AI-Builder.md — source PRD for this architecture
The original L2 registry (§5) is a build-time file artifact: a normalized
registry.catalog.json plus an embeddings.bin, scanned once and shipped.
The first tenant-facing planner at /build took the cheapest possible shortcut on top
of it — it loaded all ~90 manifests, trimmed them, and pasted the entire catalog into one
LLM prompt, then trusted the model to pick. Three problems surfaced in production:
bge-small-en-v1.5 model and is
stored as vector(384) rows in Postgres — it is the stable contract and never
calls an external API. Generation (compose the prose plan, explain trade-offs) is a
swappable LLM that only ever sees the already-retrieved top-K. Swap the generation provider any
day; retrieval is untouched. This is what makes discovery durable.
The catalog stops being only a file and becomes an auto-populated, auto-refreshed table in
the global-catalog pool (SDK manifests are identical for every tenant, so this is
global state — not per-tenant rag.* corpus). The existing
migration-runner auto-creates the schema on boot (no manual SQL), and a sync job
reuses the sdk-registry scanner to keep it fresh.
-- catalog schema · global-catalog pool · auto-created by migration-runner
CREATE TABLE catalog.sdk (
name text PRIMARY KEY, -- @projexlight/sdk-billing
summary text NOT NULL,
tags text[] NOT NULL DEFAULT '{}',
tier text, -- 'foundation' | 'domain' (drives §14.3 injection)
content_hash text NOT NULL, -- sha256(manifest) -> skip unchanged on resync
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE catalog.endpoint (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
sdk_name text NOT NULL REFERENCES catalog.sdk(name) ON DELETE CASCADE,
method text NOT NULL,
path text NOT NULL,
kind text NOT NULL DEFAULT 'query', -- NEW: ingest|bulk|query|mutation|webhook
request_schema jsonb, -- NEW: JSON Schema of the body
response_schema jsonb, -- NEW
auth_scopes text[] DEFAULT '{}', -- NEW: e.g. {billing:write}
UNIQUE (sdk_name, method, path)
);
CREATE TABLE catalog.embedding (
ref_kind text NOT NULL, -- 'sdk' | 'endpoint' | 'scenario' | 'ingest'
ref_id text NOT NULL,
card text NOT NULL, -- the NL text that was embedded
embedding vector(384) NOT NULL -- bge-small space; SEPARATE from the 1536 template
);
CREATE INDEX ON catalog.embedding USING hnsw (embedding vector_cosine_ops);
(sdk, path) for correctness. Semantic search finds the
endpoint; the relational row hands over the precise payload contract. An agent never
reconstructs a body from a fuzzy hit. Reuses the already-built VectorBackend
interface in sdk-knowledge-rag via a new PgvectorBackend.
{method, path, description} — an agent
saw the endpoint existed but had to guess the body. A build step now emits
request_schema/response_schema from each SDK's existing Zod/TS
types into the manifest and the catalog.endpoint row. New MCP tool
get_endpoint(sdk, path) returns method + path + payload + auth scope.kind: ingest|bulk|…; a
first-class sdk-ingest exposes
POST /api/ingest/:entity/batch (idempotent upsert envelope) backed by
sdk-lineage + sdk-audit. New tools
search_sdks(kind=ingest) and get_ingest_targets(entity) answer an
ETL agent's "where do I push customer records, with what payload?"
The /build planner is rebuilt as a pipeline. Crucially, semantic retrieval alone is
insufficient for cross-cutting concerns: a user who types "a financial accounting
system" never says "login," so the embedding of the intent is nowhere near the embedding of
sdk-identity. Two deterministic resolvers run between retrieve and compose to
guarantee the auth/AIM foundation always surfaces.
tier='foundation' (sdk-identity, sdk-persona = AIM,
sdk-tenant, sdk-rebac) are merged into the candidate set
regardless of semantic score, then deduped. The data already exists — these SDKs
already carry the foundation tag.consumes/event graph (e.g. tenant.created.v1 is consumed by 10+
SDKs; persona.role.assigned.v1 → sdk-rebac) to pull prerequisites.
Billing in → identity/persona/tenant dragged in with it.ui_components is empty). Planner v2 splits the output: it recommends the auth
SDKs (wire to /api/auth/login, /api/personas/resolve,
rebac.check) and lists the login page and admin page UI under custom work —
so the agent uses the platform's AIM instead of rebuilding identity, badly.
An interactive coding agent must never round-trip to Postgres per query. The store is the durable source-of-truth and freshness signal; the hot path is an in-process index.
| Tier | Serves | Latency | Backed by |
|---|---|---|---|
| Tier 0 · in-process index | Every search the agent issues
(local MCP / hosted MCP keep catalog + 384-dim vectors resident) | sub-ms, zero network | cache artifact + in-memory HNSW |
| Tier 1 · Postgres source | Durability, multi-instance freshness; MCP
instances reload their index on a catalog.sdk version bump /
LISTEN | off the hot path | pgvector store (§14.1) |
| Exact fetch | get_manifest / get_endpoint —
payload schema + auth scope | keyed lookup | relational rows |
Vectors for "which," relational for "exact payload," in-memory for "fast," Postgres for "fresh + shared."
| Epic | Deliverable | Reuses | New |
|---|---|---|---|
| A · RAG Catalog Store | catalog.* schema + pgvector +
PgvectorBackend + auto-sync job + shared bge-small embedder |
migration-runner · sdk-registry scanner · sdk-knowledge-rag VectorBackend | schema, sync job, 1 backend class |
| B · Manifest Contracts | endpoint request_schema/
response_schema + kind classifier + sdk-ingest batch
endpoint | sdk-capability schema · sdk-lineage · sdk-audit | schema fields, Zod→JSONSchema build step, ingest SDK |
| C · Planner v2 | retrieve-then-compose /api/build/plan +
foundation-tier & dependency-closure resolver + provider-agnostic generation adapter +
/build UI split | the new store · foundation tags | pipeline, 2 resolvers, adapter, UI |
| D · Agent Discovery Surface | MCP tools get_endpoint,
search(kind=ingest), get_ingest_targets + in-memory hot index
reload + ETL agent flow | registry-mcp-local | 3 tools, reload hook, example |