Uh oh!
There was an error while loading. Please reload this page.
context-graph: T1/T2 enrichment + hypaware.completion capability - #110
Merged
Conversation
…mpletion capability Adds the enrichment layer (T1/T2) on top of the T0 context graph, plus a new inference capability the tiers need. hypaware.completion (kernel types) — text generation, mirroring the embedder (own capability; provider is an explicit plugins[] choice; localhost base_url keeps it on-machine). Two providers, both v1: - @hypaware/completion-anthropic — Claude /v1/messages (Haiku for T1, Opus for T2 via per-call model). - @hypaware/completion-openai — /v1/chat/completions (OpenAI/Ollama). @hypaware/context-graph-enrich — one plugin, two daemon sources: - enrich-propose (T1): recall-tuned over-proposer; writes prospects. - enrich-curate (T2): salience-ordered (vector-distance novelty) -> recall (vector-search) + expand (SQL over published node/edge) + source deref -> prune/merge/deepen/commit. The prospect lifecycle lives in the plugin's own datasets (enrichment_prospects/_resolutions); a contract projects committed-only knowledge into the graph, so rejected prospects never reach it (derive-don't-store; the graph projector has no retract path yet). Plus enrich propose/curate/status commands. The completion + enrich plugins are excluded from default activation (model-provider opt-in), mirroring the embedder. vector-search and completion are resolved lazily (the resolver orders by requires.plugins, not requires.capabilities, and the completion provider is swappable). Verified end-to-end against a real ai_gateway_messages corpus: Haiku T1 proposed, Opus T2 committed/rejected, committed items projected as graph nodes (with props.confidence) and produced edges, rejected prospects absent. 47 unit tests; full suite green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
philcunliffe
commented
Jun 15, 2026
ContributorAuthor
Dual-agent review — |
| Source | Finding (severity, evidence) | Intersects |
|---|---|---|
| Claude | blocker — default text_column missing (config.js:15) | Config field chain; Risks #1 |
| Claude | major — NUL byte corrupts candidateKey (propose.js:88) | Targets (runProposeTick) |
| Claude | major — watermark skips source rows (propose.js:51-69,75,113) | Concurrency surface; Risks #2 |
| Claude | major — runProposeTick/runCurateTick untested (propose.js:31) | Targets; Risks #3 |
| Claude | major — state.js watermark untested (state.js:23) | Concurrency surface; Risks #3 |
| Claude | minor — @typedef style violation (state.js:17) | Targets (state.js) |
| Claude | minor — inline import() type (commands.js:93) | Direct callers (commands.js) |
| Claude | minor — sqlQuote untested (sql.js:54) | Risks #5 |
| Codex | major — propose watermark skips rows (propose.js:41,51,75,113) | Concurrency surface; Risks #2 |
| Codex | major — provider portability broken (kernel-types:1586, prompts.js:85,107, completion-openai/client.js:112,124) | Cross-package usage; Risks #4 |
| Codex | minor — unvalidated SQL identifiers (config.js:60,150, propose.js:41,44, curate.js:225) | Config field chain; Risks #5 |
| Codex | minor — NUL byte makes file binary (propose.js:88) | Targets (runProposeTick) |
Codex review
Fix Validations
No bug-fix claims to validate; this PR is feature/additive.
Findings
1) Behavioral Correctness
- Severity: major
- Confidence: high
- Evidence:
hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:41,hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:51,hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:75,hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:113,hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:143 - Why it matters: T1 can permanently skip source rows because
maxCursoris computed from all fetched rows before processing, then written even if the deadline breaks out before some groups are completed; timestamp-onlyWHERE ts > cursoralso skips rows sharing the boundary timestamp. - Suggested fix: Store a tuple cursor such as
(timestamp, id), order by both, and advance only to the last fully processed row/group; alternatively avoid writing the cursor on partial deadline exits.
2) Contract & Interface Fidelity
- Severity: major
- Confidence: high
- Evidence:
collectivus-plugin-kernel-types.d.ts:1586,hypaware-core/plugins-workspace/context-graph-enrich/src/prompts.js:85,hypaware-core/plugins-workspace/context-graph-enrich/src/prompts.js:107,hypaware-core/plugins-workspace/completion-openai/src/client.js:112,hypaware-core/plugins-workspace/completion-openai/src/client.js:124 - Why it matters:
@hypaware/context-graph-enrichis advertised as provider-swappable, but it sends Anthropic-nativetool_choice,thinking, andoutput_configthrough the provider-specificparamsfield; the OpenAI provider blindly forwards those params while translating tools to OpenAI function tools, so OpenAI-compatible enrichment will fail or not force structured output. - Suggested fix: Add a provider-neutral forced-tool/thinking abstraction to
CompletionRequestand translate inside providers, or make enrichment branch on provider capability metadata instead of passing Anthropic params unconditionally.
6) Security Surface
- Severity: minor
- Confidence: high
- Evidence:
hypaware-core/plugins-workspace/context-graph-enrich/src/config.js:60,hypaware-core/plugins-workspace/context-graph-enrich/src/config.js:150,hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:41,hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:44,hypaware-core/plugins-workspace/context-graph-enrich/src/curate.js:225 - Why it matters: Configured dataset and column names are accepted as any non-empty string and interpolated as SQL identifiers, so typos become runtime SQL failures and crafted config can alter the generated query.
- Suggested fix: Validate identifier fields with a strict identifier/schema rule, or quote identifiers with a real SQL identifier helper instead of using raw strings.
8) Release Safety
- Severity: minor
- Confidence: high
- Evidence:
hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:88 - Why it matters: The literal NUL separator makes
propose.jsappear as a binary file in git/rg output, hiding the changed source from normal diff and review tooling. - Suggested fix: Replace the literal NUL byte with an escaped separator like
`${c.type}\\0${c.label}`.
No Finding
- Change Impact / Blast Radius; 4) Concurrency, Ordering & State Safety; 5) Error Handling & Resilience; 7) Resource Lifecycle & Cleanup; 9) Test Evidence Quality beyond gaps tied to the findings above; 10) Architectural Consistency; 11) Debuggability & Operability.
Evidence Bundle
- Changed hot paths: completion capability types; Anthropic/OpenAI completion clients; enrichment activation; T1 propose loop; T2 curate loop; committed-only graph contract; bundled plugin discovery defaults.
- Impacted callers:
hypaware-core/plugins-workspace/context-graph-enrich/src/commands.js:37,hypaware-core/plugins-workspace/context-graph-enrich/src/commands.js:54,hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:143,hypaware-core/plugins-workspace/context-graph-enrich/src/curate.js:71,hypaware-core/plugins-workspace/context-graph-enrich/src/curate.js:278 - Impacted tests:
test/plugins/context-graph-enrich-prompts.test.js:13,test/plugins/context-graph-enrich-config.test.js:21,test/plugins/completion-openai-client.test.js:6,test/plugins/completion-anthropic-client.test.js:6; no direct tests forrunProposeTickcursor/deadline behavior or OpenAI-backed enrichment requests. - Unresolved uncertainty: I did not run the suite; review was diff-first. The prompt omitted
propose.jsas binary, so I opened that file from the worktree for line evidence.
Claude review
Claude review
Default text_column: 'content' does not exist in the schema-v5 ai_gateway_messages dataset
- Severity: blocker
- Confidence: 95
- Evidence: hypaware-core/plugins-workspace/context-graph-enrich/src/config.js:15
- Why it matters:
SOURCE_DEFAULTSdefaultssource_dataset: 'ai_gateway_messages'+text_column: 'content', but the projector schema (message_projector.js:60, schema v5 / commit 62f1a32) exposescontent_textwith no barecontentcolumn, so on the documented default config every proposeSELECT ${text_column}and curate deref throwsColumnNotFoundErrorand the enrichment pipeline yields zero prospects (verified: noname: 'content'column exists; test config.test.js:13 even locks in the broken default). - Suggested fix: Change
SOURCE_DEFAULTS.text_columnto'content_text', update the assertion in test/plugins/context-graph-enrich-config.test.js:13, and fix the stale "may be a string or an array of content blocks" comment in propose.js (post-v5 it is a part-level STRING).
Literal NUL byte in propose.js makes the file binary and corrupts the dedup key
- Severity: major
- Confidence: 92
- Evidence: hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:88
- Why it matters: Verified exactly one
0x00byte at offset 0xf06 sitting between${c.type}and${c.label}incandidateKeywhere a space was intended —filereports the file asdata, git renders it as a binary diff (the core T1 propose tick is invisible inpr.diff), ripgrep skips it by default, and the NUL poisons everyprospectIdhash; Node tolerates it so tests pass, making it a silent latent defect CI will not catch. - Suggested fix: Replace the NUL byte with a normal space so the literal reads
`${c.type} ${c.label}`, then confirmfile propose.jsreports text andgit diffshows it as text.
Propose watermark can silently skip source rows (two distinct mechanisms)
- Severity: major
- Confidence: 85
- Evidence: hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:51-69,75,113
- Why it matters:
maxCursoris the max timestamp over all fetched rows and is written unconditionally (:113) even when the proposal loop breaks early on the per-tick deadline (:75, default 60s), so un-proposed groups' rows are permanently excluded by the next tick'sWHERE ts > cursor; additionally, becauseai_gateway_messagesis part-level (many parts share onemessage_created_at), a strict->cursor combined withLIMIT max_rows_per_tickthat truncates mid-message permanently drops the remaining same-timestamp parts — both are silent source-row loss. (Codex independently flagged the same defect, category 1.) - Suggested fix: Advance the cursor only over groups actually processed before the break (the already-collected
g.maxTsis unused), and use a strictly monotonic compound cursor(message_created_at, part_id)with>=+ in-tick dedup so boundary parts are not skipped.
Core enrichment transforms runProposeTick / runCurateTick have zero test coverage
- Severity: major
- Confidence: 88
- Evidence: hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:31
- Why it matters: CLAUDE.md requires traditional tests for deterministic logic, yet the two most material new transforms — anchor grouping, prospect-id dedup, cursor advance (propose) and decision routing reject/merge/commit/deepen into committed-vs-resolution rows (curate) — have no test and no smoke flow; this is not academic, the watermark bug above lives in exactly this untested code, and the completion capability is injected via the
runtimeobject so a stub runtime makes both tick functions unit-testable. - Suggested fix: Add
context-graph-enrich-propose.test.jsand-curate.test.jsdriving the tick functions with a fake runtime (stubgetCompletionreturning cannedtool_use, in-memoryrunSql/appendRows); assert dedup + cursor advance in propose and reject-vs-commit row shapes in curate.
Watermark persistence in state.js is untested
- Severity: major
- Confidence: 85
- Evidence: hypaware-core/plugins-workspace/context-graph-enrich/src/state.js:23
- Why it matters:
readState/writeStateis exactly the "path helpers / local contracts" deterministic logic CLAUDE.md calls out, with real branches (schema_version === 1gating, malformed-JSON fallback, type coercion, atomic temp-then-rename) where a wrong watermark either reprocesses or silently skips rows, and it is pure temp-dir filesystem I/O that is trivially testable. - Suggested fix: Add
context-graph-enrich-state.test.jsround-tripping state through a tmpdir and asserting fallback for missing, malformed, and wrong-schema_versionfiles.
Forbidden @typedef in JSDoc violates CLAUDE.md code style
- Severity: minor
- Confidence: 88
- Evidence: hypaware-core/plugins-workspace/context-graph-enrich/src/state.js:17
- Why it matters: CLAUDE.md states verbatim "Do not use
@typedefin JSDoc. Define shared types asinterfaces in.d.tsfiles" — this new line declaresEnrichStateFileas a@typedefeven though the directory already has asrc/types.d.tsthat is the correct home. - Suggested fix: Move
EnrichStateFileintocontext-graph-enrich/src/types.d.tsas anexport interface, delete the@typedef, and reference it via an@importblock.
Forbidden inline import('...') type annotation violates CLAUDE.md code style
- Severity: minor
- Confidence: 85
- Evidence: hypaware-core/plugins-workspace/context-graph-enrich/src/commands.js:93
- Why it matters: CLAUDE.md states "Never use inline
import('...')types. Declare type imports at the top of the file with@import" — this@param {import('./types.d.ts').EnrichRuntime}is inconsistent even within its own file, which already declaresCommandRunContextcorrectly via an@importblock. - Suggested fix: Add
EnrichRuntimeto the existing top-of-file@importblock and change the annotation to the bare@param {EnrichRuntime} runtime.
sqlQuote SQL-literal escaping is untested
- Severity: minor
- Confidence: 82
- Evidence: hypaware-core/plugins-workspace/context-graph-enrich/src/sql.js:54
- Why it matters:
sqlQuoteis the only escaping guard for values interpolated into the hand-built SQL in propose/curate (anchor ids, node ids, source keys, cursor); it is the pure "SQL/TOML transform" category CLAUDE.md requires tests for, and an off-by-one in the'→''replacement would be a correctness/injection bug. - Suggested fix: Add a test asserting
sqlQuote("a'b") === "a''b", idempotency, and thatisMissingDatasetErrormatches ENOENT/unknown datasetbut not arbitrary errors.
Reports: .git/dual-review/pr-110
…ead once) T2 previously curated one prospect per Opus call, each re-reading the same session source via the prospect's provenance — so N prospects from one session sent that session's excerpt N times. Group the selected prospects by anchor (session) and make ONE curate call per group: the graph neighborhood and the union of the group's source rows are read once and shared; the model returns one decision per prospect keyed by 1-based index. curate_decision -> curate_decisions (array), buildCurateRequest -> buildCurateBatchRequest, parseDecision -> parseDecisions; max_tokens scales with group size; an empty/refused response leaves the group pending to retry. Verified: 4 prospects across 2 sessions now curate in 2 calls instead of 4 (a 6-prospect session collapses 6 calls -> 1), cutting Opus input proportionally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tability, config, tests)
Fixes the dual-review block findings on the T1/T2 enrichment + completion work.
Correctness:
- Default text_column 'content' -> 'content_text'; schema v5 exposes the
per-part text as content_text (there is no bare 'content' column).
- Propose watermark no longer skips source rows. The old `WHERE ts > 'cursor'`
never advanced: the engine surfaces a TIMESTAMP column as a Date and only
compares it against a numeric epoch literal (string/`=` match nothing). The
cursor is now a keyset tuple {ts: epochMillis, id: part_id}: a coarse
`ts >= cursorMs` filter that includes the boundary millisecond, an exact
(ts, part_id) keyset drop in JS, and advance only over the processed prefix
on a deadline break (no boundary-part loss, no skips).
- Removed the literal NUL byte in propose.js (file was binary in diffs/rg).
Provider portability:
- New provider-neutral CompletionRequest.toolChoice ('auto' | 'required' |
{name}); each provider translates to its native tool_choice. T1 forces its
tool portably; T2 is provider-aware (Anthropic keeps thinking/effort, others
force the tool). The completion capability is now genuinely interchangeable.
Hardening + style:
- Validate configured dataset/column fields as strict SQL identifiers.
- Move EnrichStateFile / CurateDecision to types.d.ts interfaces; drop the
@typedef and the inline import() type (CLAUDE.md style).
Tests:
- Extract pure helpers (buildProposeQuery, groupSourceRows, nextProposeCursor,
collectProspectRows, routeDecision) and cover them; new state/sql tests;
toolChoice translation tests for both completion providers.
Full suite green (1135 pass, 1 pre-existing skip), typecheck + lint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>philcunliffe
commented
Jun 15, 2026
ContributorAuthor
Dual-agent review — |
| Source | Finding (severity, evidence) | Intersects |
|---|---|---|
| Codex | major — cross-tick prospect_id non-idempotency (propose.js:63,65,188; curate.js:41,123) | Concurrency surface; Risk #1 (duplicate commits/resolutions/spend) |
| Codex | minor — runSql empty-result fallback masks bad source_dataset (sql.js:19,29; propose.js:41) | Config field chain; Risk #2 (silent no-op) |
| Claude | minor — inline import('...') type in test (propose.test.js:14) | none of the high-risk surfaces (style nit) |
| Claude (sub-80) | major — runProposeTick/runCurateTick orchestration untested | Concurrency surface; Risk #3 (orchestration test gap) |
| Claude (sub-80) | minor — salience_threshold>0 strands pending prospects (curate.js:173) | Concurrency surface; Risk #4 (pending-queue drainage) |
Codex review
Fix Validations
No explicit bug-fix failure mode was claimed; this is a feature PR, so there are no fix validations.
Findings
4) Concurrency, Ordering & State Safety
- Severity: major
- Confidence: high
- Evidence:
hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:63,hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:65,hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:188,hypaware-core/plugins-workspace/context-graph-enrich/src/curate.js:41,hypaware-core/plugins-workspace/context-graph-enrich/src/curate.js:123 - Why it matters: Deterministic
prospect_ids only dedupe within the current tick, but retries/reprocessing append the same IDs again, and T2 later processes every duplicate row, producing duplicate resolutions, duplicate committed rows, and extra model calls. - Suggested fix: Make prospect append idempotent across persisted rows, or at minimum dedupe
pendingbyprospect_idbefore curation; ideally filternewRowsagainst existingenrichment_prospects.prospect_idbeforeappendRows.
11) Debuggability & Operability
- Severity: minor
- Confidence: high
- Evidence:
hypaware-core/plugins-workspace/context-graph-enrich/src/sql.js:19,hypaware-core/plugins-workspace/context-graph-enrich/src/sql.js:29,hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js:41 - Why it matters:
runSql()turns every “unknown dataset” into[], but it is also used for the configured source dataset, so a missing or misspelled source silently makesenrich proposeno-op forever instead of surfacing an actionable config/runtime error. - Suggested fix: Restrict the missing-dataset fallback to plugin-owned enrichment tables, or add an explicit
allowMissingoption and leave source-dataset reads fail-fast.
No Finding
- Behavioral Correctness
- Contract & Interface Fidelity
- Change Impact / Blast Radius
- Error Handling & Resilience
- Security Surface
- Resource Lifecycle & Cleanup
- Release Safety
- Test Evidence Quality
- Architectural Consistency
Evidence Bundle
- Changed hot paths:
hypaware.completionprovider clients, context-graph enrichment activation, T1runProposeTick, T2runCurateTick, enrichment datasets, committed-only graph contract. - Impacted callers:
hypaware-core/plugins-workspace/context-graph-enrich/src/commands.js:38,hypaware-core/plugins-workspace/context-graph-enrich/src/commands.js:55,hypaware-core/plugins-workspace/context-graph-enrich/src/index.js:72,hypaware-core/plugins-workspace/context-graph-enrich/src/index.js:79. - Impacted tests:
test/plugins/context-graph-enrich-propose.test.js:156,test/plugins/context-graph-enrich-curate.test.js:10,test/plugins/context-graph-enrich-sql.test.js:17. - Unresolved uncertainty: I did not run the test suite; review was limited to the provided diff plus targeted caller/contract reads.
Claude review
Claude review
Inline import('...') type annotation violates CLAUDE.md code style
- Severity: minor
- Confidence: 92
- Evidence: test/plugins/context-graph-enrich-propose.test.js:14
- Why it matters: CLAUDE.md > Code Style states "Never use inline
import('...')types. Declare type imports at the top of the file with@importJSDoc comments"; the PR's own sibling tests (completion-anthropic-client.test.js, completion-openai-client.test.js) already use the correct top-of-file@importform, so this is an inconsistent, citable rule break rather than a pre-existing convention. - Suggested fix: Replace the inline
@returns {import('.../types.d.ts').EnrichConfig}with a top-of-file/** @import { EnrichConfig } from '../../hypaware-core/plugins-workspace/context-graph-enrich/src/types.d.ts' */block, then annotate@returns {EnrichConfig}.
Sub-threshold findings (below the ≥80 keep bar, recorded for the cross-reference only):
- Idempotency of persisted
prospect_ids across ticks (major, ~75 — corroborates Codex finding 4): deterministicprospect_ids dedupe only within a tick; a tick that appends prospects then fails before advancing the watermark would re-append the same rows, and T2 curation processes every pending row. Real, but contingent on a mid-tick failure and not independently verified to ≥80. runProposeTick/runCurateTickorchestration untested (major, 75): the pure helpers (watermark, routing, grouping) are well covered, but the glue that wires them — including theconfidence_floor/salience_thresholdfilters and the batch-per-session curation grouping — has no tick-level test.unionSources(enrich datasets.js) exported but untested (major, 70): the sibling context-graph plugin tests the equivalent limit/offset-stripping helper; the copy here does not.salience_threshold > 0can strand pending prospects (minor, 55): with arecall_indexconfigured, sub-threshold prospects are dropped from scoring and never get a terminal resolution row, so they re-score every tick. Default0.0never triggers it.- No LLP doc for the new
hypaware.completioncapability / T1-T2 design (minor–major, 55-60): +4837 lines of new capability ship with nollp/decision doc, where prior subsystems (vector-search 0024) each got one. CLAUDE.md "land the doc edit in the same commit" — possibly owned by a stacked PR.
Reports: .git/dual-review/pr-110
…l-fast, salience drain, tick tests, LLP 0028) Latest dual-review (request_changes) findings: - Cross-tick prospect_id idempotency (Codex major): T1 now filters candidates against already-persisted prospect_ids before appending (mirrors the graph projector's pre-write dedup); T2 dedups its pending selection by id as defense-in-depth. Without this a crash-before-watermark re-appended duplicate prospects that T2 re-curated (dup rows + model spend). - runSql missing-dataset fallback (Codex minor): tolerance is now opt-in (allowMissing, default fail-fast). The configured source read fail-fasts so a misspelled source_dataset surfaces instead of a silent no-op forever; plugin-owned tables and the not-yet-projected node/edge surface opt in. - salience_threshold stranding (Claude): below-threshold prospects get a terminal `skip` resolution (no curator call) so they drain instead of re-scoring every tick. Adds a skipped counter to telemetry + curate output. - Inline import() type in propose test (Claude): replaced with a top-of-file @import block. - Orchestration tests (Claude): tick-level tests for runProposeTick / runCurateTick via a fake runtime (new injected execSql seam), covering dedup, idempotency-across-ticks, confidence floor, decision routing, salience auto-skip, and no-decisions-stays-pending; plus unionSources and runSql allowMissing tests. - LLP doc gap (Claude): adds LLP 0028 documenting the hypaware.completion capability + T1/T2 enrichment design (committed-only projection, idempotency, operability, salience drain), wired into the explainer map, with six @ref annotations validated by ref-check. Full suite green (1146 pass, 1 pre-existing skip); typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds the T1/T2 enrichment layer on top of the existing T0 context graph, plus a new
hypaware.completioninference capability the tiers depend on.This is the prototype of cgproto's LLP 0006 projection pipeline above T0: a recall-tuned proposer (T1) over-proposes prospect knowledge from source text, and a graph-and-source-aware curator (T2) prunes / merges / deepens / commits.
New capability:
hypaware.completionText generation, mirroring the embedder pattern — its own capability, the provider is an explicit
plugins[]choice, and a localhostbase_urlkeeps inference on-machine. Two providers, both shipping:@hypaware/completion-anthropic— native Claude Messages API (/v1/messages). One provider serves both tiers by per-call model (Haiku for T1, Opus for T2). Adaptive thinking +efforton the T2 path; refusals returned asstopReason, never thrown.@hypaware/completion-openai— OpenAI-compatible/v1/chat/completions(OpenAI, proxies, Ollama/LM Studio).Both resolve the API key from an env var at call time (never logged), support streaming, and use an injected
fetchseam for tests.New plugin:
@hypaware/context-graph-enrichOne plugin, two daemon sources + commands. Requires
hypaware.context-graph,hypaware.vector-search, andhypaware.completion.enrich-propose(T1) — reads new source rows since a watermark, over-proposes prospects via a forced-tool extraction, writesenrichment_prospects.enrich-curate(T2) — selects pending prospects ordered by salience (vector-distance novelty), then per prospect assembles the serve path — recall (vector-search) + expand (SQL over the publishednode/edgesurface) + source deref (provenance slice) — and asks the curator to prune/merge/deepen/commit.enrich propose/enrich curate/enrich statuscommands.Key design decision: committed-only projection
The graph projector is append + dedup-by-id with no retract path (the incremental-vs-full-regeneration question is still open upstream). So the prospect lifecycle lives in this plugin's own datasets (
enrichment_prospects+ append-onlyenrichment_resolutions), and a contract projects onlyenrichment_committedinto the graph. Rejected prospects never reach the graph — this is derive-don't-store applied to the enrichment layer.Other notes
vector-search+completionare resolved lazily on first use: the dependency resolver orders byrequires.plugins, notrequires.capabilities, and the completion provider is swappable so it can't be named inrequires.plugins. (Graph stays eager — needed forregisterContract, and it's correctly ordered first.)Testing
toRow, prompt parsing, id determinism. Injectedfetchso no network.ai_gateway_messagescorpus with live Claude calls: Haiku T1 proposed 12 items → Opus T2 committed 3, rejected 1 → projected into the graph asDecision/Concept/Constraintnodes (carryingprops.confidence) withproducededges, and the rejected prospect was confirmed absent from the graph.Enabling
These are opt-in. Add to config, e.g.:
{ "name": "@hypaware/completion-anthropic", "config": { "api_key_env": "ANTHROPIC_API_KEY" } }, { "name": "@hypaware/context-graph-enrich", "config": { "text_column": "content_text" } }(also needs
@hypaware/context-graph+ an embedder/@hypaware/vector-search).🤖 Generated with Claude Code