diff --git a/.claude/skills/ref-check/SKILL.md b/.claude/skills/ref-check/SKILL.md index 34a7fd06..7241e586 100644 --- a/.claude/skills/ref-check/SKILL.md +++ b/.claude/skills/ref-check/SKILL.md @@ -9,19 +9,19 @@ Use this skill to validate the `@ref` annotations in a codebase. References are Invoke as: -- `/ref-check` — scan the current working directory for all reference annotations and report problems -- `/ref-check ` — scan a specific file or directory -- `/ref-check --fix` — attempt to auto-repair broken references where possible (e.g., LLP moved to a new number but title still matches) +- `/ref-check`: scan the current working directory for all reference annotations and report problems +- `/ref-check `: scan a specific file or directory +- `/ref-check --fix`: attempt to auto-repair broken references where possible (e.g., LLP moved to a new number but title still matches) ## What a `@ref` annotation looks like Per LLP 0000, the reference syntax is: ``` -@ref LLP NNNN#anchor — gloss -@ref LLP NNNN#anchor [relation] — gloss -@ref LLP NNNN — gloss -@ref path/to/doc.md#anchor — gloss +@ref LLP NNNN#anchor: gloss +@ref LLP NNNN#anchor [relation]: gloss +@ref LLP NNNN: gloss +@ref path/to/doc.md#anchor: gloss ``` Where: @@ -29,10 +29,10 @@ Where: - `NNNN` is a zero-padded four-digit LLP number (or just `NNNN` without padding, e.g. `LLP 42`) - `#anchor` is an optional section anchor within the document (maps to a heading in the target file) - `[relation]` is an optional relation type: `implements`, `constrained-by`, `tests`, `explains`, or a project-defined type -- `— gloss` is a short human-readable summary (required per LLP 0000 for readability) +- `: gloss` is a short human-readable summary (required per LLP 0000 for readability); the colon separates the structured prefix from the gloss - The whole thing appears in a language-appropriate comment (`//`, `#`, `/* */`, `--`, etc.) -References are commonly attached to the construct below them — a function, a struct, a block, a variable declaration. Per LLP 0000's attachment semantics, the reference binds to the next named construct. +References are commonly attached to the construct below them: a function, a struct, a block, a variable declaration. Per LLP 0000's attachment semantics, the reference binds to the next named construct. ## Ground rules @@ -109,17 +109,17 @@ Group the output by severity: ``` ref-check found 47 references in 23 files. -BROKEN (3) — these must be fixed: - src/auth/tokens.rs:42 @ref LLP 0099 — nonexistent LLP - src/ui/modal.ts:15 @ref LLP 0074#focus-trap — no such section (did you mean "focus-trapping"?) - src/net/client.go:88 @ref docs/vendor/spec.md#tokens — file not found +BROKEN (3), these must be fixed: + src/auth/tokens.rs:42 @ref LLP 0099 (nonexistent LLP) + src/ui/modal.ts:15 @ref LLP 0074#focus-trap (no such section; did you mean "focus-trapping"?) + src/net/client.go:88 @ref docs/vendor/spec.md#tokens (file not found) -WARNING (2) — references point at deprecated LLPs: - src/legacy/sync.rs:12 @ref LLP 0009 — tombstoned (no replacement indicated) - src/db/migrate.rs:55 @ref LLP 0021 — superseded by LLP 0044 +WARNING (2), references point at deprecated LLPs: + src/legacy/sync.rs:12 @ref LLP 0009 (tombstoned, no replacement indicated) + src/db/migrate.rs:55 @ref LLP 0021 (superseded by LLP 0044) -HINT (5) — consider updating: - src/ui/button.ts:33 @ref LLP 0007#layout — gloss "button click handler" does not obviously relate to section "layout" +HINT (5), consider updating: + src/ui/button.ts:33 @ref LLP 0007#layout: gloss "button click handler" does not obviously relate to section "layout" ... Summary: @@ -152,15 +152,15 @@ For scripting (this skill can be invoked from CI): ## Output formats - Plain text (default) -- JSON (`--format=json`) — an array of finding objects for programmatic consumption -- SARIF (`--format=sarif`) — for integration with CI systems that consume SARIF reports +- JSON (`--format=json`): an array of finding objects for programmatic consumption +- SARIF (`--format=sarif`): for integration with CI systems that consume SARIF reports ## Scope limits - Do not modify source files without explicit approval in `--fix` mode. - Do not modify LLP documents. - Do not follow references into unrelated repositories or external URLs. -- Do not attempt to resolve references that use unknown relation types or unknown reference syntax — report them as hints instead. +- Do not attempt to resolve references that use unknown relation types or unknown reference syntax; report them as hints instead. - Do not assume an LLP moved just because a title matches; always confirm with the user. ## Integration with other skills diff --git a/AGENTS.md b/AGENTS.md index 6d421017..4cbc2b5d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,13 +16,14 @@ actually shipped in V1. with its `Systems` value (e.g. `Sources`, `Sinks`, `Plugins`, `Config`). - **Annotate non-obvious decisions.** When you implement or change code that realizes a documented, non-obvious design decision, add an annotation: - `// @ref LLP NNNN#anchor — short gloss` (optional relation: - `[implements]`, `[constrained-by]`, `[tests]`). Attach it directly above the - construct — a blank line breaks attachment. Don't annotate mechanically; a + `// @ref LLP NNNN#anchor: short gloss` (with an optional relation before the + colon: `[implements]`, `[constrained-by]`, `[tests]`, e.g. + `// @ref LLP NNNN#anchor [implements]: short gloss`). Attach it directly above + the construct; a blank line breaks attachment. Don't annotate mechanically; a ref must tell you something the code and filename don't. - **Keep refs honest.** When you touch annotated code, check the referenced section still applies; update or remove the `@ref` if not. -- **Living docs.** Update the LLP when the design changes — land the doc edit in +- **Living docs.** Update the LLP when the design changes: land the doc edit in the same commit as the code. Mark retired docs `Superseded` or move them to `llp/tombstones/` with `Status: Tombstoned`; don't leave stale guidance. - **Tooling lives in-repo** under `.claude/skills/` (so every clone has it): @@ -34,6 +35,9 @@ actually shipped in V1. ## Code Style - JavaScript, no semicolons. +- No em dashes (the U+2014 character) anywhere: code, comments, JSDoc, strings, + or docs. In prose, use the punctuation the sentence wants (a comma, colon, + parentheses, or a sentence split); in runtime strings, prefer `-`. - Types are defined in JSDoc comments, not TypeScript. - Never use inline `import('...')` types. Declare type imports at the top of the file with `@import` JSDoc comments, then reference the bare names. diff --git a/bin/hypaware.js b/bin/hypaware.js index 7c85f075..9c58f522 100755 --- a/bin/hypaware.js +++ b/bin/hypaware.js @@ -12,7 +12,7 @@ const argv = process.argv.slice(2) // tmpdir). Routing it through the dispatcher would lock the tracer to // the parent process's HYP_HOME before the harness can change it. // -// Users never type `__smoke_internal` directly — they run +// Users never type `__smoke_internal` directly. They run // `hyp smoke `, which goes through the dispatcher and spawns us // here with a clean process state. if (argv[0] === '__smoke_internal') { diff --git a/hypaware-core/plugins-workspace/ai-gateway-graph/src/graph-keys.js b/hypaware-core/plugins-workspace/ai-gateway-graph/src/graph-keys.js index a559a41f..7dd580c6 100644 --- a/hypaware-core/plugins-workspace/ai-gateway-graph/src/graph-keys.js +++ b/hypaware-core/plugins-workspace/ai-gateway-graph/src/graph-keys.js @@ -3,13 +3,13 @@ import { posix } from 'node:path' /** - * Bridge-key vocabulary for cross-source convergence — owned by this connector. + * Bridge-key vocabulary for cross-source convergence: owned by this connector. * * Graph node ids are content-addressed over `(kind, type, natural key)` * (LLP 0023 §content-addressed-ids), so the natural key *is* the identity: * two contracts converge on one node iff they normalize the **same key * identically**. The `Repo` / `Commit` / `File` recipes live here, beside the - * contract that mints those node types (`graph_contract.js`) — not in the + * contract that mints those node types (`graph_contract.js`), not in the * generic `@hypaware/context-graph` engine, which stays node-type-agnostic. A * node type's identity recipe belongs to the plugin that emits it; the engine * only provides the type-blind `nodeId` / `edgeId` / `makeRowBuilders` @@ -21,10 +21,10 @@ import { posix } from 'node:path' * a recorded Claude/Codex session land on one node. This connector is the * host-side twin of that GitHub `keys.js`. The cross-repo contract is enforced * by digest pins on both sides (host: `test/plugins/ai-gateway-graph-bridge.test.js`; - * GitHub plugin: `test/graph-ids.test.js`) — if either side changes a recipe, + * GitHub plugin: `test/graph-ids.test.js`). If either side changes a recipe, * the pins mismatch and the change becomes a deliberate, visible decision * rather than a silent orphaning. The two are kept in sync by hand; the plugins - * are decoupled (separate repos), so a shared module isn't an option — and the + * are decoupled (separate repos), so a shared module isn't an option. And the * engine is not it either, since convergence is pin-enforced, not engine-hosted. * * The host adds two reconciliation steps the GitHub side does not need (it @@ -32,12 +32,12 @@ import { posix } from 'node:path' * a captured git **remote URL** into `owner/repo`, and a captured **absolute * local path** into a repo-relative path against the repo root. * - * @ref LLP 0032#shared-key-vocabulary [implements] — connector-owned bridge keys; Repo/Commit/File byte-identical to the GitHub side + * @ref LLP 0032#shared-key-vocabulary [implements]: connector-owned bridge keys; Repo/Commit/File byte-identical to the GitHub side */ // --------------------------------------------------------------------------- -// Verbatim from github-hyp-plugin/src/keys.js — KEEP IN SYNC. -// @ref LLP 0032#shared-key-vocabulary [constrained-by] — byte-identical to the GitHub side or the join silently stops converging +// Verbatim from github-hyp-plugin/src/keys.js. KEEP IN SYNC. +// @ref LLP 0032#shared-key-vocabulary [constrained-by]: byte-identical to the GitHub side or the join silently stops converging // --------------------------------------------------------------------------- /** @@ -72,8 +72,8 @@ export function normalizeRelpath(value) { } /** - * `Repo` key — `owner/repo`, lowercased. Accepts either `(owner, repo)` or a - * single `owner/repo` string. github.com is implied in V1 (no host segment — + * `Repo` key: `owner/repo`, lowercased. Accepts either `(owner, repo)` or a + * single `owner/repo` string. github.com is implied in V1 (no host segment: * see LLP 0032 §github-only-v1). * * @param {unknown} ownerOrFull @@ -95,7 +95,7 @@ export function repoKey(ownerOrFull, repo) { } /** - * `File` key — `owner/repo:relpath`. The repo half is normalized via + * `File` key: `owner/repo:relpath`. The repo half is normalized via * {@link repoKey}, the path via {@link normalizeRelpath}. A rename is a new * `File` (T0 keys path, not content). The `relpath` here is already * repo-relative; see {@link fileKeyFromParts} for the local-absolute form. @@ -143,7 +143,7 @@ const GITHUB_HOSTS = new Set(['github.com', 'www.github.com']) * - `https://github.com/owner/repo.git` (with optional `user:token@`) * - `git://github.com/owner/repo.git` * - * V1 is **github.com only** — a non-github remote returns null (no bridge-ready + * V1 is **github.com only**: a non-github remote returns null (no bridge-ready * `Repo`; the file/commit stay keyed on their fallbacks). Host-qualified keys * for other forges are a reserved migration (LLP 0032 §github-only-v1), not a * silent same-`owner/repo` collision across forges. @@ -177,11 +177,11 @@ export function repoKeyFromRemote(remote) { } /** - * `Commit` key — full 40-hex `sha`, lowercased. Unlike the GitHub side (which + * `Commit` key: full 40-hex `sha`, lowercased. Unlike the GitHub side (which * trusts the API to return full shas), the host validates the length: a * captured **abbreviated** sha (e.g. Codex's `latest_git_commit_hash`, which * may be short) must NOT mint a `Commit` node, because an abbreviated key would - * never converge with the GitHub side's full-sha node — it would mint a + * never converge with the GitHub side's full-sha node: it would mint a * distinct, dangling node instead. The guard only gates *whether* a key is * produced; for a full 40-hex sha the output is byte-identical to the GitHub * side. @ref LLP 0032#abbreviated-sha-guard @@ -199,7 +199,7 @@ export function commitKey(sha) { /** * Reconcile an absolute local path to a repo-relative POSIX path against the * repo root. Returns null when the path is outside the repo root (a touched - * file in `/tmp`, `~/.claude`, or another repo) — the caller then falls back to + * file in `/tmp`, `~/.claude`, or another repo). The caller then falls back to * keying that `File` on its absolute path, exactly as before the migration. * * Worktree convergence (LLP 0032 §worktree-convergence) rides on this: each @@ -209,7 +209,7 @@ export function commitKey(sha) { * Both sides are POSIX-normalized (`.`/`..` collapsed) **before** the * containment check, so a path that escapes the repo via `..` * (`/repo/../outside`) normalizes out from under the root and falls back to its - * absolute key — rather than slicing to a `../outside` relpath that would mint a + * absolute key, rather than slicing to a `../outside` relpath that would mint a * bogus bridge key (one that fails to converge, or worse, *collides* with an * unrelated file at that relpath). An in-repo `..` that stays inside * (`/repo/sub/../a` → `a`) still relativizes. @ref LLP 0032#file-migration @@ -234,8 +234,8 @@ export function relativizePath(repoRoot, absPath) { * absolute local path. Composes {@link repoKeyFromRemote} + * {@link relativizePath} + {@link fileKey} so the result is byte-identical to * the GitHub side's `fileKey(owner/repo, relpath)`. Null when the file can't be - * bridged (non-github remote, missing repo root, or path outside the repo) — - * the caller keeps the absolute-path fallback. + * bridged (non-github remote, missing repo root, or path outside the repo). + * The caller keeps the absolute-path fallback. * * @param {unknown} remote * @param {unknown} repoRoot diff --git a/hypaware-core/plugins-workspace/ai-gateway-graph/src/graph_contract.js b/hypaware-core/plugins-workspace/ai-gateway-graph/src/graph_contract.js index e8e64ae4..2a7ff956 100644 --- a/hypaware-core/plugins-workspace/ai-gateway-graph/src/graph_contract.js +++ b/hypaware-core/plugins-workspace/ai-gateway-graph/src/graph_contract.js @@ -15,7 +15,7 @@ export const PLUGIN_NAME = '@hypaware/ai-gateway-graph' export const SOURCE_DATASET = 'ai_gateway_messages' /** Projector id stamped into every row's provenance. */ export const PROJECTOR = 'ai-gateway.t0' -/** Projector version, stamped into provenance to mark which projector generation minted a row (not a re-projection trigger — ids are content-addressed; see LLP 0023 §inline-provenance). */ +/** Projector version, stamped into provenance to mark which projector generation minted a row (not a re-projection trigger: ids are content-addressed; see LLP 0023 §inline-provenance). */ export const PROJECTOR_VERSION = 1 /** Tools whose args name a concrete file. */ @@ -26,12 +26,12 @@ const FILE_TOOLS = new Set(['Read', 'Edit', 'Write', 'MultiEdit', 'NotebookEdit' * hand-authored node/edge mappings that used to live in `@hypaware/context-graph`; * they now live here, beside the source they read. Rows are built with the * graph plugin's `kit` so the id recipe and provenance columns stay owned by - * the graph plugin — this connector owns the SQL + `toRow` semantics and the + * the graph plugin. This connector owns the SQL + `toRow` semantics and the * bridge-key recipe (`keys`, imported from `./graph-keys.js`). * * @param {GraphKit} kit * @returns {{ name: string, plugin: string, sourceDataset: string, projector: string, projectorVersion: number, rules: ContractRule[] }} - * @ref LLP 0023#contract-contribution [implements] — a source's contract, contributed via the capability; engine + kit stay central + * @ref LLP 0023#contract-contribution [implements]: a source's contract, contributed via the capability; engine + kit stay central */ export function createAiGatewayGraphContract(kit) { const { buildNode, buildEdge } = kit.makeRowBuilders({ @@ -44,7 +44,7 @@ export function createAiGatewayGraphContract(kit) { const rules = [ // --- nodes --- - // Session per session_id. @ref LLP 0030#decision — session_id is the + // Session per session_id. @ref LLP 0030#decision: session_id is the // session container (always present); conversation_id is null for // Claude, so the Session node must key on session_id, not // conversation_id. @@ -111,7 +111,7 @@ export function createAiGatewayGraphContract(kit) { // captured repo (so it converges with @hypaware/github AND across worktrees // of one repo); it falls back to the absolute path otherwise (file outside // the repo, non-github remote, or a session with no captured repo). - // @ref LLP 0032#file-migration [implements] + // @ref LLP 0032#file-migration [implements]: { kind: 'node', type: 'File', @@ -153,7 +153,7 @@ export function createAiGatewayGraphContract(kit) { // --- edges --- - // Session -via-> App. @ref LLP 0030#decision — Session keyed on + // Session -via-> App. @ref LLP 0030#decision: Session keyed on // session_id (conversation_id is null for Claude). { kind: 'edge', @@ -250,7 +250,7 @@ export function createAiGatewayGraphContract(kit) { }, ] - // @ref LLP 0026#decision [implements] — tag-don't-drop: the gateway now + // @ref LLP 0026#decision [implements]: tag-don't-drop: the gateway now // RETAINS Claude harness aux exchanges (security monitor, etc.) tagged // `attributes.claude.aux_kind` instead of dropping them. That traffic is // real but not user conversation, so it must not mint graph @@ -306,8 +306,8 @@ function auxKindOf(attributes) { /** * Resolve a touched file's graph key + label + provenance. Prefers the bridge - * key `owner/repo:relpath` — which converges with @hypaware/github AND across - * worktrees of one repo (each worktree has its own root but the same relpath) — + * key `owner/repo:relpath` (which converges with @hypaware/github AND across + * worktrees of one repo, each worktree has its own root but the same relpath) * and falls back to the absolute path when the file can't be relativized * (outside the repo, a non-github remote, or a session with no captured repo). * The label is always the basename; `sourceKeys` records the absolute path for diff --git a/hypaware-core/plugins-workspace/ai-gateway-graph/src/index.js b/hypaware-core/plugins-workspace/ai-gateway-graph/src/index.js index 0195ec01..63f90420 100644 --- a/hypaware-core/plugins-workspace/ai-gateway-graph/src/index.js +++ b/hypaware-core/plugins-workspace/ai-gateway-graph/src/index.js @@ -14,12 +14,12 @@ import { createAiGatewayGraphContract } from './graph_contract.js' * registers the `ai_gateway_messages → graph` contract, building its rows with * the capability's shared kit. It declares plugin + capability dependencies on * both `@hypaware/ai-gateway` (the source it exists for) and the graph plugin, - * so both activate first — neither of them depends on the other or on this. + * so both activate first: neither of them depends on the other or on this. * Install this connector to project the gateway's data into the graph; omit it * and the gateway runs exactly as before. * * @param {PluginActivationContext} ctx - * @ref LLP 0023#contract-contribution [implements] — connector contributes the source's contract via the capability + * @ref LLP 0023#contract-contribution [implements]: connector contributes the source's contract via the capability */ export async function activate(ctx) { // ^1.0.0: this connector needs only the engine's generic kit diff --git a/hypaware-core/plugins-workspace/ai-gateway/src/config.js b/hypaware-core/plugins-workspace/ai-gateway/src/config.js index 73259f4b..912b4c27 100644 --- a/hypaware-core/plugins-workspace/ai-gateway/src/config.js +++ b/hypaware-core/plugins-workspace/ai-gateway/src/config.js @@ -57,7 +57,7 @@ export function compileUpstreams(raw) { /** * Parse `host:port`. IPv6 literals may be wrapped in `[]`. Throws on a - * malformed value — the gateway will surface that as an activation + * malformed value: the gateway will surface that as an activation * failure rather than silently bind to a wrong address. * * @param {string} listen diff --git a/hypaware-core/plugins-workspace/ai-gateway/src/dataset.js b/hypaware-core/plugins-workspace/ai-gateway/src/dataset.js index e1e8cdb6..144695a4 100644 --- a/hypaware-core/plugins-workspace/ai-gateway/src/dataset.js +++ b/hypaware-core/plugins-workspace/ai-gateway/src/dataset.js @@ -26,7 +26,7 @@ const PLUGIN_NAME = '@hypaware/ai-gateway' export const AI_GATEWAY_PROJECTED_EXCHANGE_KIND = 'ai_gateway.projected_exchange' export const DATASET_NAME = 'ai_gateway_messages' -// @ref LLP 0030#breaking — the partition key moved from conversation_id +// @ref LLP 0030#breaking: the partition key moved from conversation_id // to session_id (schema v6). The label bump gives the recreated cache a // fresh partition path; discoverParts still lists the legacy v4 path so // any pending v4 spool still flushes. @@ -151,8 +151,8 @@ const SCHEMA_COLUMN_NAMES = AI_GATEWAY_SCHEMA_COLUMNS.map((c) => c.name) /** * Expose the dataset's DECLARED schema columns on a data source even when the - * underlying parquet partitions physically lack some of them — the normal state - * after an additive schema bump, when older partitions predate a new column + * underlying parquet partitions physically lack some of them (the normal state + * after an additive schema bump), when older partitions predate a new column * (e.g. `git_remote`/`head_sha`/`repo_root` in v7, LLP 0032). Squirreling's * `validateScan` rejects a SELECT that names a column absent from the source's * `columns`, so without this a contract or query that reads a freshly-added @@ -160,7 +160,7 @@ const SCHEMA_COLUMN_NAMES = AI_GATEWAY_SCHEMA_COLUMNS.map((c) => c.name) * itself is unchanged: a row object that lacks the key simply reads as null, * which is the correct value for "this partition predates the column". * - * @ref LLP 0032#capture [implements] — additive columns stay queryable over old partitions; no partition-label bump / cache wipe needed + * @ref LLP 0032#capture [implements]: additive columns stay queryable over old partitions; no partition-label bump / cache wipe needed * @param {AsyncDataSource} source * @returns {AsyncDataSource} */ @@ -213,9 +213,9 @@ export function aiGatewayDatasetRegistration(state) { fallback: 'unknown', }, iceberg: { - // @ref LLP 0030#breaking — the required identity partition field + // @ref LLP 0030#breaking: the required identity partition field // is session_id (always present), not conversation_id (now - // nullable). @ref LLP 0022#within-partition-sort — these identity + // nullable). @ref LLP 0022#within-partition-sort: these identity // fields, in declared order, also seed the export sort order, so // session_id leads the clustering and conversation_id rides along // as a secondary thread-lookup sort key. @@ -239,7 +239,7 @@ export function aiGatewayDatasetRegistration(state) { * Build the flush-time settlement pass (LLP 0024). On each flush batch: * * 1. Short-circuit when the batch carries no fallback rows - * (`attributes.gateway.identity_source === 'gateway_fallback'`) — the + * (`attributes.gateway.identity_source === 'gateway_fallback'`) - the * common case, so the hot path does zero transcript or storage I/O. * 2. Group fallback rows by `client_name` and hand each group to the * enricher registered for that client; the enricher upgrades the @@ -270,7 +270,7 @@ function createSettleBatch(state) { * sweep the rows handed in are ALREADY committed, so a committed-scan * dedupe would match a non-upgraded fallback against its own committed * copy and wrongly drop it. The maintenance rewrite owns the de-twin - * instead — it has both committed twins of the partition in hand and + * instead, it has both committed twins of the partition in hand and * collapses an upgraded fallback against the native twin within the * rewrite set. So this pass upgrades fallback rows to native identity and * returns them; it never drops a row. @@ -326,7 +326,7 @@ async function upgradeFallbackRows(rows, state, ctx) { if (out[i] && out[i] !== group[i]) upgrades.set(group[i], out[i]) } } catch { - // An enricher failure must never drop rows — leave the group as + // An enricher failure must never drop rows: leave the group as // provisional fallback; a later flush or sweep can retry. continue } @@ -341,7 +341,7 @@ async function upgradeFallbackRows(rows, state, ctx) { * pre-write dedupe (committed scan + per-call fold-in), so an upgraded * fallback row collapses onto the canonical committed uuid row. * - * Scans ONLY committed partitions — deliberately NOT the spool. The rows + * Scans ONLY committed partitions (deliberately NOT the spool). The rows * passed here are the batch being flushed out of the spool, so seeding * the seen-set with spool `part_id`s would make every row match itself * and be dropped (see scanSpooledPartIds's hazard note). That spool scan @@ -398,7 +398,7 @@ function stringValue(value) { * Backfill providers yield a whole conversation as a single * `AiGatewayProjectedExchange` payload; this converts it into canonical * `ai_gateway_messages` rows through `aiGatewayRowsFromProjectedExchange` - * — the exact expansion the live gateway recorder uses — so backfilled + * (the exact expansion the live gateway recorder uses) so backfilled * and live-captured rows are byte-identical for the same projection. * Row expansion is pure with respect to `item.value`: it allocates a * fresh conversation state per call, so reruns and out-of-order items @@ -407,7 +407,7 @@ function stringValue(value) { * On top of that pure expansion the materializer applies a narrow * PRE-WRITE dedupe: before a batch is handed back to the runner for * `appendRows`, any row whose `part_id` already exists in the dataset is - * skipped. This is the PRIMARY rerun guarantee — rerunning a backfill + * skipped. This is the PRIMARY rerun guarantee: rerunning a backfill * re-materializes byte-identical rows, and without this guard each rerun * would re-append them and lean on cache-maintenance compaction to * collapse the duplicates later. Compaction's content-hash dedupe @@ -452,12 +452,12 @@ export function aiGatewayBackfillMaterializer() { * items in the same run that resolve to the same `part_id` (transitional * fixtures, fallback-id collisions, a re-yielded conversation) also * dedupe against each other. A later run carries a fresh run id, so it - * re-scans and observes the prior run's now-committed rows — which is + * re-scans and observes the prior run's now-committed rows, which is * what makes a clean rerun write zero new rows. * * The seen-set is seeded from two sources: the committed (flushed) - * Iceberg partitions AND the rows still pending in the spool — captured - * live but not yet flushed (issue #107). Without the spool scan, backfill + * Iceberg partitions AND the rows still pending in the spool (captured + * live but not yet flushed, issue #107). Without the spool scan, backfill * re-materializes its own copy of an unflushed live row and the spool * later flushes its copy, leaving two rows with the same `part_id`. The * spool scan is BACKFILL-ONLY (see scanSpooledPartIds); the flush-time @@ -482,7 +482,7 @@ function createBackfillDedupe() { const seen = await scanExistingPartIds(storage) // Fold in part_ids pending in the spool so backfill does not // re-materialize a row that was captured live and is still waiting - // to flush. Opt-in to the backfill path only — see scanSpooledPartIds. + // to flush. Opt-in to the backfill path only (see scanSpooledPartIds). await scanSpooledPartIds(storage, seen) memo = { runId, seen } } @@ -493,7 +493,7 @@ function createBackfillDedupe() { for (const row of rows) { const key = partIdKey(row) if (key === undefined) { - // No usable identity to dedupe on — never drop the row. + // No usable identity to dedupe on: never drop the row. fresh.push(row) continue } @@ -521,8 +521,8 @@ function canScanExistingRows(storage) { * set of `part_id`s already present. Reads are projected to the three * identity columns so the scan stays cheap, and every failure mode * (unreadable partition, missing table) degrades to "not seen" rather - * than aborting the backfill — a dedupe miss only risks a duplicate that - * compaction will later collapse, whereas throwing would drop real rows. + * than aborting the backfill (a dedupe miss only risks a duplicate that + * compaction will later collapse, whereas throwing would drop real rows). * * @param {QueryStorageService} storage * @returns {Promise>} @@ -558,21 +558,21 @@ async function scanExistingPartIds(storage) { * These are rows captured live but not yet flushed to a committed * partition, so `scanExistingPartIds` cannot see them. Folding them in * lets `hyp backfill` skip re-materializing a row whose live copy is - * about to flush — the fix for issue #107. + * about to flush (the fix for issue #107). * - * CRITICAL HAZARD — BACKFILL ONLY. This must never be wired into the + * CRITICAL HAZARD: BACKFILL ONLY. This must never be wired into the * flush-time settle path (`createSettleBatch` -> `dedupeByPartId`). At * flush, the rows being settled ARE the spool rows; if the settle * seen-set contained spool `part_id`s, every row would match itself and - * be dropped — the flush would delete the data it is committing. So the + * be dropped (the flush would delete the data it is committing). So the * spool scan stays opt-in and is invoked only from `createBackfillDedupe`. * * Best-effort like the committed scan: a storage stub without the spool - * read surface, or any read error, leaves `seen` untouched — a dedupe + * read surface, or any read error, leaves `seen` untouched (a dedupe * miss only risks a duplicate compaction can later collapse, whereas - * throwing would abort the backfill. + * throwing would abort the backfill). * - * @ref LLP 0027#open-questions [implements] — resolves the documented + * @ref LLP 0027#open-questions [implements]: resolves the documented * "backfill-vs-spool same-id duplicates" residue by scanning spooled * rows in the materializer (not the settle path). * diff --git a/hypaware-core/plugins-workspace/ai-gateway/src/index.js b/hypaware-core/plugins-workspace/ai-gateway/src/index.js index 78b6de83..da065ecb 100644 --- a/hypaware-core/plugins-workspace/ai-gateway/src/index.js +++ b/hypaware-core/plugins-workspace/ai-gateway/src/index.js @@ -33,10 +33,10 @@ const PLUGIN_NAME = '@hypaware/ai-gateway' * sets `state.listen`, which is what makes * `AiGatewayCapability.localEndpoint()` resolve. Until then the * capability is registered (adapters can record their contributions) - * but `localEndpoint()` throws — the contract documented in api.js. + * but `localEndpoint()` throws: the contract documented in api.js. * * @param {PluginActivationContext} ctx - * @ref LLP 0016#knows-nothing-about-claude-or-codex [implements] — owns the gateway capability + ai_gateway_messages; no client specifics + * @ref LLP 0016#knows-nothing-about-claude-or-codex [implements]: owns the gateway capability + ai_gateway_messages; no client specifics */ export async function activate(ctx) { const state = createGatewayState() diff --git a/hypaware-core/plugins-workspace/ai-gateway/src/message_projector.js b/hypaware-core/plugins-workspace/ai-gateway/src/message_projector.js index b9d175a8..7882dd5a 100644 --- a/hypaware-core/plugins-workspace/ai-gateway/src/message_projector.js +++ b/hypaware-core/plugins-workspace/ai-gateway/src/message_projector.js @@ -17,8 +17,8 @@ const DATASET_NAME = 'ai_gateway_messages' * * The row shape is the contract the dataset advertises and downstream * queries lock onto. The gateway always emits this column set, regardless - * of which adapter projector produced the messages — projector-defined - * fields map onto these named columns directly. `schema_version` 7 added + * of which adapter projector produced the messages (projector-defined + * fields map onto these named columns directly). `schema_version` 7 added * the `git_remote` / `head_sha` / `repo_root` capture columns (LLP 0032); * the additions are nullable, so old partitions read them as null and no * partition-label bump is needed. @@ -28,7 +28,7 @@ const DATASET_NAME = 'ai_gateway_messages' export const AI_GATEWAY_MESSAGE_COLUMNS = Object.freeze([ { name: 'gateway_id', type: 'STRING', nullable: false }, { name: 'schema_version', type: 'INT32', nullable: false }, - // @ref LLP 0030#decision — session_id is the partition key and the + // @ref LLP 0030#decision: session_id is the partition key and the // session container (Claude session / Codex metadata.session_id), // always present; conversation_id is the thread WITHIN it (Codex // thread; null for Claude) and is therefore nullable. @@ -44,7 +44,7 @@ export const AI_GATEWAY_MESSAGE_COLUMNS = Object.freeze([ { name: 'client_name', type: 'STRING', nullable: true }, { name: 'cwd', type: 'STRING', nullable: true }, { name: 'git_branch', type: 'STRING', nullable: true }, - // @ref LLP 0032#capture — captured repo identity for the GitHub↔LLM graph + // @ref LLP 0032#capture: captured repo identity for the GitHub↔LLM graph // bridge: the git remote URL, full HEAD sha, and repo root (the prefix that // relativizes a touched file's absolute path). Nullable: a session may run // outside a git repo, and older partitions predate these columns (read null). @@ -108,14 +108,14 @@ const SCHEMA_COLUMN_NAMES = new Set(AI_GATEWAY_MESSAGE_COLUMNS.map((column) => c * return `undefined`, or return an invalid shape are warned and * skipped. * 4. Applies fallback identity (hash `message_id`) ONLY when the - * chosen projection omitted identity — projector-supplied IDs are - * authoritative. `previous_message_id` is gateway-owned either + * chosen projection omitted identity (projector-supplied IDs are + * authoritative). `previous_message_id` is gateway-owned either * way: unless the projector supplied explicit history, every row * gets its IMMEDIATE predecessor in the THREAD (a 0/1-element - * array) — scoped to `(conversation_id ?? session_id, agent_id)` so + * array), scoped to `(conversation_id ?? session_id, agent_id)` so * a Claude subagent's chain (conversation_id null, scopes by * session) and a Codex thread (scopes by its own conversation_id) - * stay separate from the main loop's — so enriched and fallback + * stay separate from the main loop's, so enriched and fallback * rows stay query-compatible. The full * ancestry is the transitive closure of these links (storing it per * row was quadratic). @@ -124,9 +124,9 @@ const SCHEMA_COLUMN_NAMES = new Set(AI_GATEWAY_MESSAGE_COLUMNS.map((column) => c * `attributes.gateway.*` provenance, and strips to schema columns. * * If no projector matches or every match fails, the dispatcher - * returns an empty row array — the source still emits pass-through + * returns an empty row array (the source still emits pass-through * telemetry (`aigw.exchange` log + `aigw.exchange_bytes` meter), it - * just does not write any rows. + * just does not write any rows). * * @param {{ * gatewayId: string, @@ -174,7 +174,7 @@ export function createAiGatewayMessageProjector(opts) { return [] } - // @ref LLP 0030#decision — seed by session_id (the partition key, + // @ref LLP 0030#decision: seed by session_id (the partition key, // always present). Claude `conversation_id` is null, so seeding on it // would never dedup a replayed Claude session. await seedSeenMessagesForSession( @@ -195,7 +195,7 @@ export function createAiGatewayMessageProjector(opts) { } } -// @ref LLP 0026#consequences [implements] — closes the "durable live dedup +// @ref LLP 0026#consequences [implements]: closes the "durable live dedup // across daemon restarts (seed the seen-set from committed part_ids)" gap: // the in-memory seen-set is rebuilt empty on every restart/reload, so a // replay of already-committed history would re-emit same-part_id rows. @@ -203,10 +203,10 @@ export function createAiGatewayMessageProjector(opts) { * Lazily pre-populate `state.seenMessages` with the `message_id`s already * committed for one session, the FIRST time that session is projected in * this listener's lifetime. Seeds one session at a time (a large cache - * holds millions of part_ids — a global preload is a memory and scan + * holds millions of part_ids (a global preload is a memory and scan * problem), and only once per session per listener. * - * Scopes on `session_id` — the partition key (LLP 0030), always present. + * Scopes on `session_id`: the partition key (LLP 0030), always present. * Claude `conversation_id` is null, so a conversation-scoped seed would * never dedup a replayed Claude session; the session partition holds all * of a session's committed rows across its threads. @@ -243,9 +243,9 @@ function seedSeenMessagesForSession(sessionId, state, seedPromises, storage, log * * Best-effort throughout: a missing storage handle (unit-test stubs), a * missing table, or an unreadable partition degrades to "not seeded" and - * NEVER throws — a seeding miss only risks the duplicate this guards + * NEVER throws (a seeding miss only risks the duplicate this guards * against (which settlement/compaction can still collapse), whereas - * throwing would drop a real row. The promise still resolves on a + * throwing would drop a real row). The promise still resolves on a * partial/failed scan, so it is cached and not retried on every exchange. * * @param {string} sessionId @@ -273,8 +273,8 @@ async function scanCommittedMessageIds(sessionId, state, storage, log) { const tablePath = part?.path if (!tablePath || (typeof part.rowCount === 'number' && part.rowCount === 0)) continue // The dataset is Iceberg-partitioned by `session_id` (LLP 0030), so a - // partition naming a different session cannot hold this one's rows — - // skip it without a read. The row-level `session_id` filter below is + // partition naming a different session cannot hold this one's rows (so + // skip it without a read). The row-level `session_id` filter below is // the correctness backstop for source-partitioned or legacy partitions // that don't carry the key in their path. const partitionSession = part.partition?.session_id @@ -334,7 +334,7 @@ export function createAiGatewayConversationState() { * session_id`: a subagent (agent_id set) gets its own chain, separate * from the main loop, while agent_id null reuses the plain thread key. * Claude has conversation_id null, so it scopes by session; Codex keeps - * scoping by its thread (conversation_id) — both unchanged from before + * scoping by its thread (conversation_id), both unchanged from before * the split, since Claude's old conversation_id WAS the session id. * * @param {ReturnType} state @@ -577,12 +577,12 @@ function isValidProjection(value) { * conversationMessageIds: string[], * }} ctx */ -// @ref LLP 0026#consequences [implements] — store only the immediate +// @ref LLP 0026#consequences [implements]: store only the immediate // predecessor, not the full ancestry; full chain is reconstructable by // walking links, and the prior O(N) chain made the column quadratic. function resolveIdentity(ctx) { // `previous_message_id` carries the IMMEDIATE predecessor in this - // THREAD (a 0- or 1-element array) — scoped to (conversation_id, + // THREAD (a 0- or 1-element array), scoped to (conversation_id, // agent_id) by the caller's `conversationMessageIds`, whether // `message_id` was projector-supplied (transcript uuid) or // hash-synthesized here. The full ancestry is the transitive closure @@ -637,7 +637,7 @@ function expandMessageParts(ctx) { const finishReason = mapFinishReason(stringValue(ctx.message.stop_reason)) const messageCreatedAt = stringValue(ctx.message.message_created_at) ?? ctx.tsStart const messageAttributes = ctx.message.attributes - // @ref LLP 0035#one-carrier [implements] — response-level `usage` is + // @ref LLP 0035#one-carrier [implements]: response-level `usage` is // per-response, so a multi-block carrier message must not replicate it onto // every part. Strip `usage` from all but the last part; the last block (the // terminal output item, which also carries `stop_reason`/status) is the sole @@ -655,13 +655,13 @@ function expandMessageParts(ctx) { conversation_id: ctx.conversationId, user_id: ctx.projection.user_id, provider: ctx.projection.provider, - // @ref LLP 0026#consequences [implements] — the message envelope (incl. + // @ref LLP 0026#consequences [implements]: the message envelope (incl. // model) mirrors the transcript: backfill records the per-line model on // assistant messages only, so the per-message value wins where present and // mixed-model sessions stay accurate. When a message has no model the row - // falls back to the exchange model — which for live capture is the one + // falls back to the exchange model (which for live capture is the one // model per exchange (landing on user rows too), and for backfill is unset - // (backfilled user/tool_result rows carry no model, by design). + // (backfilled user/tool_result rows carry no model, by design)). model: stringValue(ctx.message.model) ?? ctx.projection.model, system_text: ctx.projection.system_text, tools: ctx.projection.tools, @@ -670,7 +670,7 @@ function expandMessageParts(ctx) { client_name: stringValue(ctx.projection.client_name), cwd: ctx.projection.cwd, git_branch: ctx.projection.git_branch, - // @ref LLP 0032#capture — repo identity for the graph bridge, exchange-level + // @ref LLP 0032#capture: repo identity for the graph bridge, exchange-level // like cwd/git_branch (captured by the Claude hook / Codex turn metadata). git_remote: ctx.projection.git_remote, head_sha: ctx.projection.head_sha, @@ -750,9 +750,9 @@ function expandMessageParts(ctx) { * before hashing: clients move the `cache_control` prompt-cache * breakpoint between exchanges, so hashing it would give the same * logical message a new id on every replay where the breakpoint - * shifted — each one a duplicate row the seen-set cannot catch. + * shifted (each one a duplicate row the seen-set cannot catch). * - * // @ref LLP 0030#decision — the thread scope is `conversation_id ?? + * // @ref LLP 0030#decision: the thread scope is `conversation_id ?? * // session_id`: for Claude (conversation_id null) that is the session * // id, the same value the pre-split conversation_id held, so Claude * // fallback ids are unchanged; for Codex it is the thread. agent_id @@ -779,7 +779,7 @@ export function computeMessageId(threadScope, role, content, agentId) { * `cache_control` is a wire-only prompt-cache breakpoint that moves * between exchanges. `caller` is a tool_use annotation that rides the * assistant *response* stream and the transcript but is dropped from - * the *request-input* echo of the same turn — so the one logical + * the *request-input* echo of the same turn, so the one logical * tool_use hashes with-or-without `caller` depending on which * representation reaches this fallback. This MUST strip the same set * the claude plugin's `contentKey` strips, so the fallback id and the @@ -789,8 +789,8 @@ export function computeMessageId(threadScope, role, content, agentId) { * canonicalizer would be the first static link across that boundary and * isn't worth it. * - * Reached only for an *unmatched* assistant tool_use — a matched one - * carries its native uuid and skips this fallback — so the split is + * Reached only for an *unmatched* assistant tool_use (a matched one + * carries its native uuid and skips this fallback), so the split is * rare, but stripping `caller` keeps the two representations from * landing as duplicate fallback rows when it does happen. * @@ -934,8 +934,8 @@ function buildStatus(block, isLastPart, role, finishReason) { * Stamp `attributes.client.{name,version}` on every emitted row when * the projector supplied client identity. The 1.x gateway carried a * special-case for Anthropic's `claude_version` field; that wart is - * gone — adapters now choose what `client.name` is and the gateway - * just propagates it. + * gone (adapters now choose what `client.name` is and the gateway + * just propagates it). * * @param {Record | undefined} attributes * @param {string | undefined} clientVersion @@ -980,7 +980,7 @@ function buildGatewayAttributes(exchange) { * unchanged when there's no `usage`, and `undefined` when `usage` was * the only key (so non-carrier parts fall back to client attributes). * - * @ref LLP 0035#one-carrier — usage is per-response; only the last part carries it. + * @ref LLP 0035#one-carrier: usage is per-response; only the last part carries it. * @param {Record | undefined} attributes * @returns {Record | undefined} */ diff --git a/hypaware-core/plugins-workspace/ai-gateway/src/proxy.js b/hypaware-core/plugins-workspace/ai-gateway/src/proxy.js index 200427e9..6da29e1b 100644 --- a/hypaware-core/plugins-workspace/ai-gateway/src/proxy.js +++ b/hypaware-core/plugins-workspace/ai-gateway/src/proxy.js @@ -40,7 +40,7 @@ const HOP_BY_HOP_HEADERS = new Set([ * * Routing is preset-driven: each compiled upstream is matched via * `match()` when supplied, otherwise via path-segment prefix. There - * is no hardcoded Anthropic / OpenAI / Codex routing — adapter + * is no hardcoded Anthropic / OpenAI / Codex routing: adapter * plugins own provider matching by registering presets with their * own `match()`. * diff --git a/hypaware-core/plugins-workspace/ai-gateway/src/recorder.js b/hypaware-core/plugins-workspace/ai-gateway/src/recorder.js index 538eaa99..ddc12680 100644 --- a/hypaware-core/plugins-workspace/ai-gateway/src/recorder.js +++ b/hypaware-core/plugins-workspace/ai-gateway/src/recorder.js @@ -32,7 +32,7 @@ const DEFAULT_REDACT_HEADERS = Object.freeze([ /** * Build a Recorder. The recorder owns the redact set so per-exchange - * code doesn't have to recompute it; it does not own a sink — the + * code doesn't have to recompute it; it does not own a sink: the * source layer hands each finished row to `appendRows` directly so it * can compose a span around the write. * @@ -158,7 +158,7 @@ export class Exchange { /** * Record dev_run_id (and any future request-scoped metadata that the * row will carry). Stored as a flat record because the schema's - * `metadata` column is a JSON variant — we stringify at finalize. + * `metadata` column is a JSON variant: we stringify at finalize. * * @returns {string | undefined} */ @@ -215,7 +215,7 @@ export class Exchange { * * The proxy is a pass-through, so when the upstream compressed the * stream (`content-encoding: gzip` et al.) these chunks are the - * compressed bytes — feeding them to the text parser yields zero + * compressed bytes: feeding them to the text parser yields zero * events and the whole response is silently lost. Compressed streams * are buffered raw here and decoded + parsed once, at `finalize()`. * @@ -243,7 +243,7 @@ export class Exchange { } /** - * Record a final error. Overwriting is allowed — the most recent + * Record a final error. Overwriting is allowed: the most recent * error usually carries the most useful diagnostic. * * @param {unknown} err @@ -265,7 +265,7 @@ export class Exchange { /** * Build the row to be handed to the exchange-projector dispatcher. - * Idempotent — once `finished` is true subsequent calls return the + * Idempotent: once `finished` is true subsequent calls return the * cached row. * * The JSON-shaped fields (`request_headers`, `response_headers`, @@ -288,9 +288,9 @@ export class Exchange { : '' // Header-blind SSE detection. Some upstreams stream Server-Sent - // Events WITHOUT a `content-type: text/event-stream` header — e.g. + // Events WITHOUT a `content-type: text/event-stream` header (e.g. // ChatGPT's `/backend-api/codex/responses` sends no content-type at - // all — so `setResponseStart` couldn't flag it and the body was + // all), so `setResponseStart` couldn't flag it and the body was // buffered like a normal response. Sniff the decoded body: if it // opens like an event stream, treat it as SSE so the response is // parsed into events instead of stored as an opaque, unprojectable @@ -299,7 +299,7 @@ export class Exchange { this.isSse = true } - // SSE whose bytes were buffered rather than parsed live — a + // SSE whose bytes were buffered rather than parsed live: a // compressed stream (see consumeStreamChunk) or a header-blind one // just detected above. Decode-and-parse the whole stream in one // pass. Event `t_ms` is stamped at finalize; per-chunk arrival times @@ -401,9 +401,9 @@ function decodeBody(buf, encodingHeader) { else if (enc === 'br') current = brotliDecompressSync(current) else if (enc === 'deflate') current = inflateOrRaw(current) else if (enc === 'zstd' && zstdDecompress) current = zstdDecompress(current) - else return current.toString('utf8') // unknown codec — stop, keep what we have + else return current.toString('utf8') // unknown codec: stop, keep what we have } catch { - return buf.toString('utf8') // undecodable — fall back to the raw bytes + return buf.toString('utf8') // undecodable: fall back to the raw bytes } } return current.toString('utf8') diff --git a/hypaware-core/plugins-workspace/ai-gateway/src/runtime.js b/hypaware-core/plugins-workspace/ai-gateway/src/runtime.js index 4d2cf4f4..25fe8e03 100644 --- a/hypaware-core/plugins-workspace/ai-gateway/src/runtime.js +++ b/hypaware-core/plugins-workspace/ai-gateway/src/runtime.js @@ -6,7 +6,7 @@ * source listener share, and a `started` flag the smoke / future * commands can consult before driving a source start vs. reload. * - * The shape mirrors `@hypaware/gascity`'s runtime — same lifecycle hook + * The shape mirrors `@hypaware/gascity`'s runtime: same lifecycle hook * pattern, same "saved-ctx-as-source-of-truth" convention so reloads * read the latest config from `runtime.ctx.config`. */ @@ -34,7 +34,7 @@ export function setAiGatewayRuntime(value) { */ export function requireAiGatewayRuntime() { if (!runtime) { - throw new Error('@hypaware/ai-gateway: not activated yet — runtime singleton is empty') + throw new Error('@hypaware/ai-gateway: not activated yet - runtime singleton is empty') } return runtime } diff --git a/hypaware-core/plugins-workspace/ai-gateway/src/source.js b/hypaware-core/plugins-workspace/ai-gateway/src/source.js index 6eb8f0f2..d49cf04c 100644 --- a/hypaware-core/plugins-workspace/ai-gateway/src/source.js +++ b/hypaware-core/plugins-workspace/ai-gateway/src/source.js @@ -94,8 +94,8 @@ async function launchListener(ctx, state, liveState) { gatewayId: config.gatewayId, projectors: state.projectors, // Thread storage so the projector can lazily seed its seen-set from - // committed part_ids per conversation — without it a restart/reload - // rebuilds an empty set and replays re-emit duplicate-part_id rows. + // committed part_ids per conversation (without it a restart/reload + // rebuilds an empty set and replays re-emit duplicate-part_id rows). storage: ctx.storage, log: ctx.log, }) @@ -176,8 +176,8 @@ async function launchListener(ctx, state, liveState) { * sorted by the proxy at compile time. * * Presets without a `match()` and without a `path_prefix` are filtered - * out — they can never route a request and would only inflate the - * compiled table. + * out (they can never route a request and would only inflate the + * compiled table). * * @param {UpstreamConfig[]} configUpstreams * @param {GatewayState} state @@ -206,7 +206,7 @@ function mergeUpstreams(configUpstreams, state) { /** * Read the names of configured upstreams from the activation config. - * Defensive — if config has been mutated to a degenerate shape, returns + * Defensive: if config has been mutated to a degenerate shape, returns * an empty list so status() never throws. * * @param {PluginActivationContext} ctx diff --git a/hypaware-core/plugins-workspace/central/index.js b/hypaware-core/plugins-workspace/central/index.js index ba0916fb..cbc04981 100644 --- a/hypaware-core/plugins-workspace/central/index.js +++ b/hypaware-core/plugins-workspace/central/index.js @@ -12,7 +12,7 @@ import { createForwardSink } from './src/sink.js' */ /** - * `@hypaware/central` — request sink that forwards ready cache + * `@hypaware/central`: request sink that forwards ready cache * partitions to a central HypAware server. The plugin replaces the * `role: gateway` config from collectivus: a host becomes "the * gateway" purely by configuring this sink under @@ -70,7 +70,7 @@ export async function activate(ctx) { if (!configControl) return sink - // @ref LLP 0025#config-pull-loop [implements] — pull immediately on bootstrap success, then on the steady timer + // @ref LLP 0025#config-pull-loop [implements]: pull immediately on bootstrap success, then on the steady timer const pullLoop = createConfigPullLoop({ centralUrl: config.url, identityClient, diff --git a/hypaware-core/plugins-workspace/central/src/backoff.js b/hypaware-core/plugins-workspace/central/src/backoff.js index e527d03e..faae8bb8 100644 --- a/hypaware-core/plugins-workspace/central/src/backoff.js +++ b/hypaware-core/plugins-workspace/central/src/backoff.js @@ -2,7 +2,7 @@ /** * Shared retry/backoff primitives for the central plugin's two HTTP - * loops — the config pull ({@link ./config_client.js}) and the forward + * loops: the config pull ({@link ./config_client.js}) and the forward * sink's ingest POSTs ({@link ./sink.js}). Both face the same server * contract: `429`/`503` carry a `Retry-After` the client honors, and a * linear ladder is the fallback when the header is absent or garbage @@ -17,7 +17,7 @@ export const RETRY_BACKOFF_SECONDS = [30, 60, 120, 300] * HTTP-date, anything unparseable → `undefined`. A literal `0` or a past * HTTP-date faithfully parses to `0`. Callers must treat any non-positive * (or `undefined`) result as "no useful pacing" and fall back to the - * backoff ladder — never honor it as a zero-delay retry, which would spin + * backoff ladder: never honor it as a zero-delay retry, which would spin * the retry loop. * * @param {string | null} value @@ -33,7 +33,7 @@ export function parseRetryAfter(value) { } /** - * Sleep `ms`, but reject as soon as `signal` aborts — so an in-flight + * Sleep `ms`, but reject as soon as `signal` aborts, so an in-flight * backpressure wait inside `exportBatch` cannot wedge sink `close()` or, * through it, daemon shutdown. With no signal it is a plain timed sleep. * The timer is not unref'd: an export deliberately pausing for the diff --git a/hypaware-core/plugins-workspace/central/src/config_client.js b/hypaware-core/plugins-workspace/central/src/config_client.js index 826d0726..e2fb36b1 100644 --- a/hypaware-core/plugins-workspace/central/src/config_client.js +++ b/hypaware-core/plugins-workspace/central/src/config_client.js @@ -16,7 +16,7 @@ export const DEFAULT_POLL_INTERVAL_SECONDS = 300 /** * Transport-level cap on a pulled config body. Mirrors the kernel's - * `MAX_CONFIG_DOCUMENT_BYTES` — the apply engine enforces it again, + * `MAX_CONFIG_DOCUMENT_BYTES`: the apply engine enforces it again, * but an oversized body is dropped before it is buffered whole: an * oversized `Content-Length` is rejected without reading, and a * chunked body is read through a byte counter that cancels the stream @@ -33,8 +33,8 @@ export const DEFAULT_REQUEST_TIMEOUT_SECONDS = 30 /** * How long `stop()` lets an in-flight poll drain before aborting it - * (seconds). A healthy poll finishes in this window — a mid-flight - * apply should commit rather than be cancelled — while a stalled + * (seconds). A healthy poll finishes in this window: a mid-flight + * apply should commit rather than be cancelled, while a stalled * request is cut off so shutdown stays prompt. */ export const DEFAULT_STOP_GRACE_SECONDS = 1 @@ -46,7 +46,7 @@ const LEGACY_404_BACKOFF_SECONDS = 300 * The config pull loop: poll `GET /v1/config` with `If-None-Match` set * to the *running* config's etag, confirm successful polls to the * kernel (clearing post-apply probation), and hand 200 bodies to the - * apply facade. Transport only — validation, persistence, restart, + * apply facade. Transport only: validation, persistence, restart, * probation, and rollback are all kernel-owned behind `configControl`. * * The loop is a self-rescheduling timeout rather than an interval so @@ -62,8 +62,8 @@ const LEGACY_404_BACKOFF_SECONDS = 300 * exit condition. * * Every poll runs under its own `AbortController` with a hard - * deadline: a stalled config GET must not be able to wedge `stop()` — - * and through it daemon shutdown or a staged restart — so a poll that + * deadline: a stalled config GET must not be able to wedge `stop()`, + * and through it daemon shutdown or a staged restart, so a poll that * outlives the deadline is aborted, and `stop()` aborts an in-flight * poll after a short drain grace. * @@ -77,7 +77,7 @@ const LEGACY_404_BACKOFF_SECONDS = 300 * log: PluginLogger, * fetchFn?: typeof fetch, * }} args - * @ref LLP 0025#config-pull-loop [implements] — immediate pull on bootstrap success, then a steady plugin-internal timer + * @ref LLP 0025#config-pull-loop [implements]: immediate pull on bootstrap success, then a steady plugin-internal timer */ export function createConfigPullLoop(args) { const { centralUrl, identityClient, configControl, log } = args @@ -261,7 +261,7 @@ export function createConfigPullLoop(args) { ...(retryAfter !== undefined ? { retry_after_seconds: retryAfter } : {}), }) // Honor only a *positive* Retry-After. A legal `0` or a past HTTP-date - // parses to 0 — rescheduling at 0s would re-poll immediately and spin; + // parses to 0: rescheduling at 0s would re-poll immediately and spin; // fall through to the ladder ('retry_backoff') instead. return retryAfter ? retryAfter : 'retry_backoff' } @@ -287,7 +287,7 @@ export function createConfigPullLoop(args) { signal, headers: { authorization: `Bearer ${jwt}`, - // If-None-Match always reflects the *running* config — the + // If-None-Match always reflects the *running* config: the // server reads it as the fleet-convergence signal, so a // gateway mid-apply keeps presenting its old etag. ...(runningEtag ? { 'if-none-match': runningEtag } : {}), @@ -306,7 +306,7 @@ export function createConfigPullLoop(args) { /** * Stop polling. Lets an in-flight poll drain for a short grace * (a mid-flight apply should commit, not be cancelled), then - * aborts it — so the wait is bounded even against a stalled + * aborts it, so the wait is bounded even against a stalled * server or a `fetchFn` that ignores abort signals. */ async stop() { @@ -373,7 +373,7 @@ async function readBodyCapped(response, maxBytes, signal) { } /** - * Await `promise`, but reject as soon as `signal` aborts — even when + * Await `promise`, but reject as soon as `signal` aborts, even when * the underlying promise never settles. A misbehaving `fetchFn` (or a * server that stalls mid-body) must not be able to wedge `stop()`, * and through it daemon shutdown. diff --git a/hypaware-core/plugins-workspace/central/src/identity_client.js b/hypaware-core/plugins-workspace/central/src/identity_client.js index c50f6b8c..8a8696ca 100644 --- a/hypaware-core/plugins-workspace/central/src/identity_client.js +++ b/hypaware-core/plugins-workspace/central/src/identity_client.js @@ -23,7 +23,7 @@ function fingerprintToken(token) { /** * Eagerly refresh when the remaining lifetime falls inside this window * (24h). Matches the donor `collectivus/src/gateway/identity.js` - * contract — see proto.md "Refresh window". + * contract: see proto.md "Refresh window". */ export const REFRESH_WINDOW_SECONDS = 24 * 60 * 60 @@ -87,7 +87,7 @@ export class IdentityClient { // gateway_id, so re-bootstrap with the new token instead. In steady // state no bootstrap token is configured (the seed is retired after // first apply), so this never fires and the persisted JWT is reused. - // @ref LLP 0031#physical-layout [implements] re-join re-bootstraps a fresh gateway identity; clearing config slots alone leaves identity.json shadowing the new token + // @ref LLP 0031#physical-layout [implements]: re-join re-bootstraps a fresh gateway identity; clearing config slots alone leaves identity.json shadowing the new token if (this.bootstrapToken && mintChanged(persisted, this.centralUrl, this.bootstrapToken)) { await this.bootstrap() return 'bootstrapped' @@ -98,7 +98,7 @@ export class IdentityClient { // file this server's data under the other server's gateway_id, a // cross-tenant leak. Refuse rather than silently mis-route; the // operator must re-run `hyp join` against the new server. - // @ref LLP 0031#physical-layout [implements] a re-point with no token cannot safely reuse the old identity, so loading is refused + // @ref LLP 0031#physical-layout [implements]: a re-point with no token cannot safely reuse the old identity, so loading is refused if (persisted.central_url !== undefined && persisted.central_url !== this.centralUrl) { throw new Error( `identity central URL mismatch: persisted identity was minted by ${persisted.central_url} but the configured central server is ${this.centralUrl}. Run \`hyp join ${this.centralUrl} \` to enroll this host with the new server` @@ -213,7 +213,7 @@ export class IdentityClient { */ async getCurrentJwt() { if (!this.identity) { - throw new Error('identity not acquired — call acquire() first') + throw new Error('identity not acquired - call acquire() first') } const remainingSec = this.identity.expires_at - Math.floor(this.now() / 1000) if (remainingSec <= REFRESH_WINDOW_SECONDS) { @@ -303,7 +303,7 @@ function writePersistedFile(filePath, identity) { try { fs.chmodSync(filePath, 0o600) } catch { - // best effort — rename already replaced the file + // best effort: rename already replaced the file } } @@ -332,7 +332,7 @@ function identityFromPayload(parsed, fallbackGatewayId) { /** * Decode the `sub` claim from a JWT without verifying the signature. - * The gateway trusts the TLS connection for authenticity — it has no + * The gateway trusts the TLS connection for authenticity: it has no * way to verify the JWT (it doesn't share the issuer secret). * * @param {string} jwt @@ -399,7 +399,7 @@ async function readErrorDetail(response) { if (error) return `${response.status} ${error}` } } catch { - // plain text body — fall through + // plain text body: fall through } return `${response.status} ${body.trim().slice(0, 200)}` } diff --git a/hypaware-core/plugins-workspace/central/src/sink.js b/hypaware-core/plugins-workspace/central/src/sink.js index e6fc5813..adb4e439 100644 --- a/hypaware-core/plugins-workspace/central/src/sink.js +++ b/hypaware-core/plugins-workspace/central/src/sink.js @@ -15,7 +15,7 @@ const KNOWN_SIGNALS = new Set(['logs', 'traces', 'metrics', 'proxy']) // Ceiling on how long one chunk POST will pace itself against the // server's 429/503 backpressure before giving up inline. We retry the // SAME chunk (honoring Retry-After) so delivery is correct at any volume -// — pausing whenever the server's byte-rate bucket empties — but bound +// (pausing whenever the server's byte-rate bucket empties), but bound // the inline wait so one throttled partition can't wedge a sink tick. // On exceeding it we throw: the driver respools the partition and the // next tick resumes, which is cheap because the server dedupes the @@ -161,7 +161,7 @@ function signalForPartition(query, partition) { * chunks, never materializing the whole table. Each chunk POSTs with an * `X-Hyp-Batch-Id` derived from the signal, the partition identity, the * chunk's position, and its bytes (see {@link batchIdForChunk}): stable - * across retries of that exact chunk, yet distinct for any other chunk — + * across retries of that exact chunk, yet distinct for any other chunk, * so two byte-identical chunks never collide. When the driver re-hands a * partition after a transport failure, re-streaming reproduces the same * chunk boundaries, so the unchanged prefix chunks hash to the same ids @@ -212,7 +212,7 @@ async function forwardPartition({ partition, signal, config, identityClient, sto }) } catch (err) { // Annotate so the partition-level failure log (exportBatch) can - // name the failing chunk and how many already landed — the new + // name the failing chunk and how many already landed: the new // chunk loop is otherwise invisible against the server ledger. if (err && typeof err === 'object') { const e = /** @type {{ hyp_batch_id?: string, hyp_chunks_sent?: number }} */ (err) @@ -254,8 +254,8 @@ async function forwardPartition({ partition, signal, config, identityClient, sto * partition identity (`tablePath`), the chunk's ordinal position, and * its exact bytes. Re-streaming a partition reproduces the same chunk * boundaries and order, so a re-sent chunk hashes to the same id (the - * server dedupes it); two byte-identical chunks at different positions — - * or in different partitions — get distinct ids and are both stored. + * server dedupes it); two byte-identical chunks at different positions or + * in different partitions get distinct ids and are both stored. * * @param {string} signal * @param {string} tablePath @@ -326,8 +326,8 @@ function serializeValue(value) { * idempotency key as `X-Hyp-Batch-Id`. Re-sends the *same* body + key on * two transient conditions, so every retry stays idempotent: * - * - `401` — refresh the JWT once and retry (a second `401` escalates). - * - `429`/`503` — server backpressure. Honor `Retry-After` (falling back + * - `401`: refresh the JWT once and retry (a second `401` escalates). + * - `429`/`503`: server backpressure. Honor `Retry-After` (falling back * to the linear ladder when it is absent or garbage), sleep, and retry * the same chunk. This is what makes delivery correct at any volume: * the POST pauses whenever the server's byte-rate bucket empties rather @@ -336,7 +336,7 @@ function serializeValue(value) { * throw and let the driver respool (the server dedupes the delivered * prefix, so the next tick resumes cheaply). * - * Any other non-2xx throws — `4xx` poison and other `5xx` are the + * Any other non-2xx throws: `4xx` poison and other `5xx` are the * driver's to classify (outbox respool); narrowing poison-drop is a * separate follow-up (hypaware #118). * @@ -388,11 +388,11 @@ async function postNdjson(args) { continue } - // @ref LLP 0014#forward-sink-backpressure [implements] — 429/503 is backpressure, not failure: pace the same chunk in place, bounded inline, respool past budget. + // @ref LLP 0014#forward-sink-backpressure [implements]: 429/503 is backpressure, not failure: pace the same chunk in place, bounded inline, respool past budget. if (response.status === 429 || response.status === 503) { // Honor only a *positive* Retry-After. A legal `Retry-After: 0` or a // past HTTP-date parses to 0 (not undefined) and carries no useful - // pacing — taking it verbatim would retry with zero delay, never + // pacing: taking it verbatim would retry with zero delay, never // advance `waitedMs`, and spin this loop forever. `||` (not `??`) // falls a zero through to the ladder, so every wait progresses and // the inline budget can bound the retries. @@ -416,7 +416,7 @@ async function postNdjson(args) { }) // Release the throttle response before parking: undici keeps the // socket out of the pool until the body is read or cancelled, so a - // multi-minute pause — and every retry that piles up — would + // multi-minute pause (and every retry that piles up) would // otherwise pin it. await discardBody(response) await sleepFn(delayMs, abortSignal) @@ -453,7 +453,7 @@ async function readErrorDetail(response) { if (error) return `${response.status} ${error}` } } catch { - // plain text — fall through + // plain text: fall through } return `${response.status} ${body.trim().slice(0, 200)}` } @@ -461,8 +461,8 @@ async function readErrorDetail(response) { } /** - * Discard a response body we will not read — a 429/503 we are about to - * retry past — so undici returns the socket to the pool. Cancelling is + * Discard a response body we will not read (a 429/503 we are about to + * retry past), so undici returns the socket to the pool. Cancelling is * best-effort: a missing or already-settled body is a no-op. * * @param {Response} response diff --git a/hypaware-core/plugins-workspace/claude/src/anthropic.js b/hypaware-core/plugins-workspace/claude/src/anthropic.js index eb1d8d8b..bed5b551 100644 --- a/hypaware-core/plugins-workspace/claude/src/anthropic.js +++ b/hypaware-core/plugins-workspace/claude/src/anthropic.js @@ -8,7 +8,7 @@ import { createHash } from 'node:crypto' /** * Anthropic Messages HTTP + SSE parsing. Ported from the gateway - * core's pre-2.0 `message_projector.js` — the same logic, scoped to + * core's pre-2.0 `message_projector.js`: the same logic, scoped to * the Anthropic shape (no OpenAI/Codex branches). The projector in * `projector.js` calls these to turn a captured `/v1/messages` * exchange into the `(messages, model, system_text, tools, …)` shape @@ -19,7 +19,7 @@ import { createHash } from 'node:crypto' * Build the list of canonical Anthropic messages for one captured * exchange. The request body's `messages` array is the chat history * the client already had; the response (either the JSON assistant - * body, or — for streamed responses — the reconstructed assistant + * body, or: for streamed responses: the reconstructed assistant * message from the SSE event stream) is appended as the final entry. * * @param {Record} reqBody @@ -177,7 +177,7 @@ export function hasAnthropicHeaderSignature(headers) { /** * Pull the Claude Code session id off either the request body's * `metadata.user_id` (Anthropic stuffs it there as a JSON-encoded - * blob) or the `x-claude-code-session-id` header — same priority as + * blob) or the `x-claude-code-session-id` header: same priority as * the donor `resolveSessionId`. * * @param {Record | undefined} reqBody @@ -191,8 +191,8 @@ export function resolveClaudeSessionId(reqBody, headers) { /** * Resolve the non-null `session_id` (partition key) for an Anthropic - * exchange. Claude has no per-thread conversation id — a session is a - * container of many threads — so this value is `session_id`, not + * exchange. Claude has no per-thread conversation id: a session is a + * container of many threads: so this value is `session_id`, not * `conversation_id` (which is null for Claude). @ref LLP 0030#decision * * Resolution: the session id wins; otherwise hash the first message's @@ -412,14 +412,14 @@ function isAnthropicAssistant(value) { /** * System-prompt fingerprints for Claude Code's harness-internal "aux" - * API calls — requests the CLI makes on its own behalf (not the user's + * API calls: requests the CLI makes on its own behalf (not the user's * conversation) that nonetheless flow through the gateway under the * session's headers. Each entry maps a stable system-prompt substring to * the aux kind it identifies. * * The autonomous-mode security monitor fires on every action and, with * its embedded session digest, dwarfs the real conversation in row - * volume — and it has a dedicated system prompt, so it is the one aux + * volume: and it has a dedicated system prompt, so it is the one aux * kind reliably fingerprintable today. Other aux calls (recap, title * generation) reuse the full Claude Code system prompt and only differ in * injected user text, so they have no stable fingerprint and are diff --git a/hypaware-core/plugins-workspace/claude/src/backfill.js b/hypaware-core/plugins-workspace/claude/src/backfill.js index d59c5b9f..d2467da2 100644 --- a/hypaware-core/plugins-workspace/claude/src/backfill.js +++ b/hypaware-core/plugins-workspace/claude/src/backfill.js @@ -32,7 +32,7 @@ import { anthropicMessageAttributes } from './anthropic.js' * recomputes ids when the projector supplies them): * - `uuid` -> `message_id` / `provider_uuid` * - `parentUuid` -> `parent_uuid` - * `previous_message_id` is NOT supplied here — the gateway expansion + * `previous_message_id` is NOT supplied here; the gateway expansion * always fills it with the full prior-message-id chain, the same * shape live capture rows get. * Reruns are deterministic: ids, parents, and timestamps come straight @@ -74,7 +74,7 @@ export function createClaudeBackfillProvider(opts) { const pluginName = opts.pluginName ?? DEFAULT_PLUGIN_NAME const projectsDir = opts.projectsDir ?? defaultClaudeProjectsDir(opts.homeDir) const stateFile = opts.stateFile - // @ref LLP 0032#capture — pre-0032 Claude sessions carry no captured remote; + // @ref LLP 0032#capture: pre-0032 Claude sessions carry no captured remote; // recover it by running git in the session's cwd at backfill time. Injectable // so tests stub the git lookup and stay hermetic. const deriveRepo = opts.deriveRepo ?? deriveRepoFromCwd @@ -300,7 +300,7 @@ async function projectedExchangeFromEntries(args) { let transcriptCwd // Usage is a response-level (per API message) figure that Claude Code // duplicates onto every block line of an assistant turn. Record the last - // block line per API message id so usage is stamped on only that one block — + // block line per API message id so usage is stamped on only that one block: // matching the live projector, so each response contributes usage to exactly // one row and live/backfill dedupe onto the same row. @ref LLP 0035#one-carrier /** @type {Map} */ @@ -330,7 +330,7 @@ async function projectedExchangeFromEntries(args) { /** @type {AiGatewayProjectedExchange} */ const exchange = { provider: 'anthropic', - // @ref LLP 0030#decision — the Claude session id is the session_id + // @ref LLP 0030#decision: the Claude session id is the session_id // partition key; conversation_id is null (no per-thread id). Matches // live capture so backfilled and live rows still converge. session_id: sessionId, @@ -351,7 +351,7 @@ async function projectedExchangeFromEntries(args) { const cwd = record?.cwd ?? transcriptCwd if (cwd) exchange.cwd = cwd if (record?.git_branch) exchange.git_branch = record.git_branch - // @ref LLP 0032#capture — repo identity rides the same hook-written + // @ref LLP 0032#capture: repo identity rides the same hook-written // session-context record as cwd/git_branch; the live projector stamps these // too (projector.js), so backfilled and live Claude rows converge identically. // Unlike Codex, the Claude hook captures `git rev-parse --show-toplevel`, so @@ -359,10 +359,10 @@ async function projectedExchangeFromEntries(args) { if (record?.git_remote) exchange.git_remote = record.git_remote if (record?.head_sha) exchange.head_sha = record.head_sha if (record?.repo_root) exchange.repo_root = record.repo_root - // @ref LLP 0032#capture — sessions recorded before the hook captured git + // @ref LLP 0032#capture: sessions recorded before the hook captured git // identity have a record with no remote; recover it by running git in the // recovered cwd. Only when the record didn't already supply a remote, and - // never head_sha — current HEAD ≠ the session's HEAD (git_repo.js). + // never head_sha: current HEAD ≠ the session's HEAD (git_repo.js). if (cwd && !exchange.git_remote) { const derived = await deriveRepo(cwd) if (derived.git_remote) exchange.git_remote = derived.git_remote @@ -376,7 +376,7 @@ async function projectedExchangeFromEntries(args) { * mirroring the live Claude projector's native-DAG identity mapping. * Differs from live capture in two ways: `role` / `content` come * straight from the transcript frame, and `raw_frame` carries only a - * minimized native-identity stub — never the full transcript line. + * minimized native-identity stub: never the full transcript line. * * @param {TranscriptEntry} entry * @param {Map} agentMeta @@ -394,7 +394,7 @@ function projectedMessageFromEntry(entry, agentMeta, stampUsage) { content: /** @type {any} */ (entry.content), } if (entry.provider_uuid) { - // Native id only — like the live projector, `previous_message_id` + // Native id only: like the live projector, `previous_message_id` // is left to the gateway expansion, which fills the full // prior-message chain; the native DAG parent rides `parent_uuid`. message.message_id = entry.provider_uuid @@ -446,7 +446,7 @@ function projectedMessageFromEntry(entry, agentMeta, stampUsage) { * Minimized native frame: enough to trace a row back to its Claude * transcript line (native uuids, type/subtype, timestamp) without * copying the full transcript or any prompt / response content. Per the - * bead contract: store a minimized, redacted native frame — never the + * bead contract: store a minimized, redacted native frame, never the * raw line. * * @param {TranscriptEntry} entry diff --git a/hypaware-core/plugins-workspace/claude/src/config.js b/hypaware-core/plugins-workspace/claude/src/config.js index a76c2770..41a27e87 100644 --- a/hypaware-core/plugins-workspace/claude/src/config.js +++ b/hypaware-core/plugins-workspace/claude/src/config.js @@ -3,7 +3,7 @@ /** * Config validation for the `@hypaware/claude` plugin's own `config` * block. v1 validates only the optional `backfill` sub-object that drives - * backfill-on-join — `{ on_join, window_days }`. Every other key (e.g. + * backfill-on-join: `{ on_join, window_days }`. Every other key (e.g. * `proxy`) passes through untouched so existing configs keep working; * there is no top-level `backfill` section and nothing new for core to * validate. @@ -23,7 +23,7 @@ export const CLAUDE_CONFIG_SECTION = 'claude' * `backfill` policy block is checked; unknown sibling keys are ignored so * the validator stays additive over the existing config surface. * - * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements] — + * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements]: * backfill policy ({ on_join, window_days }) lives in and is validated * by the source plugin's own config section; the kernel reconciler adds * no top-level schema. @@ -47,7 +47,7 @@ export function validateClaudeConfig(value) { * backfill-capable source plugin: `on_join` (whether to import on join, * boolean) and `window_days` (how far back, positive integer). Both are * optional; unknown keys are rejected so a typo (`window_day`) surfaces - * instead of being silently ignored. Pure — the caller chooses where the + * instead of being silently ignored. Pure: the caller chooses where the * returned pointers mount. * * @param {unknown} value diff --git a/hypaware-core/plugins-workspace/claude/src/git_repo.js b/hypaware-core/plugins-workspace/claude/src/git_repo.js index 14ab3e6a..a1acfea3 100644 --- a/hypaware-core/plugins-workspace/claude/src/git_repo.js +++ b/hypaware-core/plugins-workspace/claude/src/git_repo.js @@ -12,22 +12,22 @@ const execFileAsync = promisify(execFile) * * The live hook (`hook_command.js` `gitRepoFacts`) writes `git_remote` / * `head_sha` / `repo_root` into the session-context sidecar, but sessions - * recorded by an older hook have a sidecar that predates those fields — and + * recorded by an older hook have a sidecar that predates those fields, and * the transcript never carried them either. The one repo signal those * sessions DO carry is the `cwd` that rides each transcript line, so backfill * recovers identity by running git in that cwd NOW. * - * Returns `git_remote` (credential-redacted) and `repo_root` only — never + * Returns `git_remote` (credential-redacted) and `repo_root` only, never * `head_sha`. `rev-parse HEAD` today reports the repo's *current* HEAD, not * the commit the historical session sat on, so a derived `head_sha` would be * anachronistic and mint a wrong `Commit` node (LLP 0032 §repo-commit-nodes). * The headline `Session -in-> Repo` join needs only the remote, and a repo's * toplevel is stable across commits, so both are safe to derive after the * fact while `head_sha` is not. A cwd that no longer resolves to a git repo - * (a deleted worktree, a moved checkout) degrades to `{}` — fall back rather + * (a deleted worktree, a moved checkout) degrades to `{}`. Fall back rather * than mis-key (LLP 0032 §file-migration). * - * @ref LLP 0032#capture [implements] — repo identity is captured by running git, not inferred from cwd, so deriving it at backfill time stays within the contract + * @ref LLP 0032#capture [implements]: repo identity is captured by running git, not inferred from cwd, so deriving it at backfill time stays within the contract * @param {string | undefined} cwd * @param {(file: string, args: string[], opts: { timeout: number }) => Promise<{ stdout: string }>} [exec] * git runner seam; defaults to `execFile`. Injected in tests so the @@ -54,7 +54,7 @@ export async function deriveRepoFromCwd(cwd, exec = execFileAsync) { * Run one `git -C ` and return its first trimmed line, or * `undefined` on any failure (not a repo, no remote, git missing, timeout). * Mirrors `hook_command.js` `gitLine`, but with a backfill-appropriate - * timeout — backfill is offline, so it need not stay under the hook's + * timeout: backfill is offline, so it need not stay under the hook's * never-block-Claude budget. * * @param {(file: string, args: string[], opts: { timeout: number }) => Promise<{ stdout: string }>} exec diff --git a/hypaware-core/plugins-workspace/claude/src/hook_command.js b/hypaware-core/plugins-workspace/claude/src/hook_command.js index 6af5851e..79247de9 100644 --- a/hypaware-core/plugins-workspace/claude/src/hook_command.js +++ b/hypaware-core/plugins-workspace/claude/src/hook_command.js @@ -55,7 +55,7 @@ export async function runClaudeSessionContextHook(argv, ctx) { if (!sessionId || !cwd) return 0 const transcriptPath = str(event.transcript_path) const gitBranch = await currentGitBranch(cwd) - // @ref LLP 0032#capture — the hook already runs git in the live cwd for the + // @ref LLP 0032#capture: the hook already runs git in the live cwd for the // branch; the remote/HEAD/root for the graph bridge come from the same place. const repo = await gitRepoFacts(cwd) @@ -74,7 +74,7 @@ export async function runClaudeSessionContextHook(argv, ctx) { try { await appendSessionContext(stateFile, /** @type {any} */ (record)) } catch { - /* hook MUST never throw back into Claude — exit 0 even on write failure */ + /* hook MUST never throw back into Claude: exit 0 even on write failure */ } return 0 } @@ -164,7 +164,7 @@ async function currentGitBranch(cwd) { /** * Best-effort repo identity for the GitHub↔LLM graph bridge (LLP 0032): - * the `origin` remote URL, the FULL HEAD sha (never the short form — an + * the `origin` remote URL, the FULL HEAD sha (never the short form: an * abbreviated sha can't converge with the GitHub side's full-sha node), and * the repo root that relativizes touched-file paths. Each lookup is * independent and degrades to `undefined`; like `currentGitBranch`, the hook @@ -190,17 +190,17 @@ async function gitRepoFacts(cwd) { /** * Strip credential userinfo (`user[:token]@`) from a git remote URL so a token - * embedded in an HTTPS remote — e.g. `https://x-access-token:@github.com/owner/repo.git`, - * which `gh` and CI checkouts write into `remote.origin.url` — never lands in + * embedded in an HTTPS remote: e.g. `https://x-access-token:@github.com/owner/repo.git`, + * which `gh` and CI checkouts write into `remote.origin.url`: never lands in * the session-context sidecar or the `git_remote` row column. Convergence only * needs the normalized `owner/repo`, so the raw secret has no downstream use. * * Only the `scheme://[user[:token]@]host/…` URL form carries a secret; the * scp-like SSH form (`git@github.com:owner/repo.git`) authenticates by key, so * its `git@` user is left intact. Duplicated (deliberately) in `@hypaware/codex` - * `git-remote.js` — the plugins are decoupled; a test on each path pins it. + * `git-remote.js`: the plugins are decoupled; a test on each path pins it. * - * @ref LLP 0032#remote-redaction — owner/repo is all convergence needs; the raw remote can carry a secret + * @ref LLP 0032#remote-redaction: owner/repo is all convergence needs; the raw remote can carry a secret * @param {string | undefined} remote * @returns {string | undefined} */ diff --git a/hypaware-core/plugins-workspace/claude/src/index.js b/hypaware-core/plugins-workspace/claude/src/index.js index 33e00edb..592b5d20 100644 --- a/hypaware-core/plugins-workspace/claude/src/index.js +++ b/hypaware-core/plugins-workspace/claude/src/index.js @@ -27,12 +27,12 @@ const FALLBACK_BIN_PATH = fileURLToPath(new URL('../../../../bin/hypaware.js', i /** * The plugin's `config_sections` validator, surfaced as a side-effect-free * export so the kernel apply path can validate this plugin's `config` block - * (the `backfill` policy) *before* the plugin is ever activated — e.g. a - * central config that first introduces `@hypaware/claude`. It is the same + * (the `backfill` policy) *before* the plugin is ever activated (e.g. a + * central config that first introduces `@hypaware/claude`). It is the same * registration `activate()` hands `ctx.configRegistry.registerSection`; * importing this module never runs `activate()`, so discovery is safe. * - * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements] — the plugin owns + exposes its own `backfill` validator + * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements]: the plugin owns + exposes its own `backfill` validator * @type {{ section: string, validate: typeof validateClaudeConfig }} */ export const configSection = { section: CLAUDE_CONFIG_SECTION, validate: validateClaudeConfig } @@ -65,14 +65,14 @@ export function claudeSessionContextFile(ctx) { * `restored=true|false`. * * @param {PluginActivationContext} ctx - * @ref LLP 0016#knows-nothing-about-claude-or-codex [implements] — adapter requires the ai-gateway capability; registers client + upstream preset + * @ref LLP 0016#knows-nothing-about-claude-or-codex [implements]: adapter requires the ai-gateway capability; registers client + upstream preset */ export async function activate(ctx) { - // Validate the plugin's own `config` block — currently just the + // Validate the plugin's own `config` block: currently just the // optional `backfill` policy ({ on_join, window_days }) that drives // backfill-on-join. Registered so the kernel runs it via // `runPerPluginSectionValidators`; no top-level core schema change. - // @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements] — the source plugin owns and validates its `backfill` config + // @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements]: the source plugin owns and validates its `backfill` config ctx.configRegistry.registerSection({ plugin: PLUGIN_NAME, section: CLAUDE_CONFIG_SECTION, @@ -111,7 +111,7 @@ export async function activate(ctx) { }) ) - // @ref LLP 0027#decision — flush-time settlement: upgrade fallback rows + // @ref LLP 0027#decision: flush-time settlement: upgrade fallback rows // (transcript line not yet on disk when captured) to native identity // once the line has landed, so race duplicates collapse at flush. gateway.registerSettlementEnricher( @@ -271,14 +271,14 @@ export async function activate(ctx) { ctx.commands.register({ name: 'claude-hook session-context', - summary: 'Internal Claude Code hook — appends session context to the state file', + summary: 'Internal Claude Code hook: appends session context to the state file', usage: 'hyp claude-hook session-context --state-file ', hidden: true, run: runClaudeSessionContextHook, }) const skillsRoot = path.resolve(skillsRootDir(), 'skills') - // @ref LLP 0011#interactive-walkthrough [implements] — contributes client skills the first-run walkthrough installs + // @ref LLP 0011#interactive-walkthrough [implements]: contributes client skills the first-run walkthrough installs for (const skillName of [ 'hypaware-query', 'hypaware-ignore', @@ -344,8 +344,8 @@ function firstNonEmpty(...values) { * and `@hypaware/claude`. This is the Phase 9 V1 milestone preset and * exercises every first-party shipping plugin end-to-end. * - * The preset never overwrites an existing config file silently — - * passing `--force` opts into overwrite; otherwise the existing file + * The preset never overwrites an existing config file silently (pass + * `--force` to opt into overwrite); otherwise the existing file * stays and the command returns 1. * * @param {string[]} argv diff --git a/hypaware-core/plugins-workspace/claude/src/projector.js b/hypaware-core/plugins-workspace/claude/src/projector.js index 7221092f..c9eabfa0 100644 --- a/hypaware-core/plugins-workspace/claude/src/projector.js +++ b/hypaware-core/plugins-workspace/claude/src/projector.js @@ -46,10 +46,10 @@ import { * * Behavior: * - * 1. `match()` — true for any captured exchange whose path looks like + * 1. `match()`: true for any captured exchange whose path looks like * `/v1/messages*` OR whose headers carry an Anthropic signature * (`anthropic-version`, `x-api-key`, or `Authorization: Bearer sk-ant-*`). - * 2. `project()` — parses the request + response (HTTP body for + * 2. `project()`: parses the request + response (HTTP body for * non-streamed, SSE event stream for streamed) into the canonical * Anthropic message list, resolves a session id from * `metadata.user_id.session_id` / `x-claude-code-session-id`, reads @@ -63,7 +63,7 @@ import { * result (joined by `tool_use_id`), prompts stay whole. When a * transcript line matches: * - `message_id = provider_uuid = uuid`, native parent on - * `parent_uuid` — `previous_message_id` is never supplied; + * `parent_uuid`: `previous_message_id` is never supplied; * the gateway always fills it with the full prior-message * chain so enriched and fallback rows share one shape. * On a miss, messages are returned without `message_id` so the @@ -100,7 +100,7 @@ export function createClaudeExchangeProjector(opts) { const requestPath = stringValue(input.path) ?? '' const reqBody = parseMaybeJson(input.request_body) if (!isPlainObject(reqBody)) { - // Surface the skip — adapter projectors that decline must + // Surface the skip: adapter projectors that decline must // still leave a breadcrumb so a missing row is debuggable. ctx.log.warn('plugin.claude.projector_skip', { reason: 'unparseable_request_body', @@ -114,11 +114,11 @@ export function createClaudeExchangeProjector(opts) { // data, so we tag rather than drop: stamp `attributes.claude.aux_kind` // on every projected message below so conversation queries exclude // it (`aux_kind IS NULL`) without losing it. `claudeAuxKind` keys - // only on the dedicated security-monitor system prompt — the one aux - // kind reliably fingerprintable today — so a normal turn is never + // only on the dedicated security-monitor system prompt: the one aux + // kind reliably fingerprintable today: so a normal turn is never // mislabeled. The drop this replaced silently lost ~88% of rows in // an autonomous session. - // @ref LLP 0026#decision — tag-don't-drop; aux_kind rides the + // @ref LLP 0026#decision: tag-don't-drop; aux_kind rides the // attributes JSON (no schema change, per LLP 0027#decision pt 5). const auxKind = claudeAuxKind(reqBody) @@ -161,7 +161,7 @@ export function createClaudeExchangeProjector(opts) { : [] const transcriptIndex = indexTranscriptEntries(transcriptEntries) const identityFromTranscript = transcriptIndex.ordered.length > 0 - // @ref LLP 0030#decision — a Claude session is a container of many + // @ref LLP 0030#decision: a Claude session is a container of many // threads (main loop, subagents, side chats), so the session id is // the `session_id` partition key, NOT `conversation_id`. Claude has // no per-thread conversation id, so conversation_id is null. @@ -181,7 +181,7 @@ export function createClaudeExchangeProjector(opts) { responseBody, input.duration_ms ) - // @ref LLP 0026#decision — decompose each wire message into the + // @ref LLP 0026#decision: decompose each wire message into the // transcript's native units (one per assistant block, one per // tool_result) so message_id is the transcript-line uuid and // live rows converge with backfill. @@ -226,7 +226,7 @@ export function createClaudeExchangeProjector(opts) { // Tag every message of an aux exchange so queries can exclude it. // Keyed on THIS exchange's request body (see `auxKind` above), so - // only the aux exchange's rows carry `aux_kind` — real turns are + // only the aux exchange's rows carry `aux_kind`: real turns are // never mislabeled. if (auxKind) { for (const projected of projectedMessages) { @@ -236,8 +236,8 @@ export function createClaudeExchangeProjector(opts) { } } - // @ref LLP 0027#decision — a message that came out fallback (no - // transcript line on disk yet — the finalize race) carries the + // @ref LLP 0027#decision: a message that came out fallback (no + // transcript line on disk yet: the finalize race) carries the // content match-key so flush-time settlement can re-match it by // pure lookup once the line lands, without reconstructing the // content array that per-part expansion discards. @@ -254,7 +254,7 @@ export function createClaudeExchangeProjector(opts) { const projection = { provider: 'anthropic', session_id: sessionIdColumn, - // conversation_id is null for Claude — the session id is the + // conversation_id is null for Claude: the session id is the // session container, not a per-thread id. @ref LLP 0030#decision conversation_source: conversationSource, client_name: clientName, @@ -268,7 +268,7 @@ export function createClaudeExchangeProjector(opts) { if (userId) projection.user_id = userId if (sessionContextRecord?.cwd) projection.cwd = sessionContextRecord.cwd if (sessionContextRecord?.git_branch) projection.git_branch = sessionContextRecord.git_branch - // @ref LLP 0032#capture — repo identity for the graph bridge, recovered + // @ref LLP 0032#capture: repo identity for the graph bridge, recovered // from the same hook-written session-context record as cwd/git_branch. if (sessionContextRecord?.git_remote) projection.git_remote = sessionContextRecord.git_remote if (sessionContextRecord?.head_sha) projection.head_sha = sessionContextRecord.head_sha @@ -276,7 +276,7 @@ export function createClaudeExchangeProjector(opts) { if (exchangeAttrs) projection.attributes = exchangeAttrs if (input.ts_start) projection.conversation_started_at = input.ts_start - // @ref LLP 0026#decision — subagent exchanges identify themselves + // @ref LLP 0026#decision: subagent exchanges identify themselves // on the wire; sidechain provenance must not depend on winning the // transcript race, so it is stamped from the header (resolved // above, and used to scope transcript matching to this thread). @@ -299,7 +299,7 @@ export function createClaudeExchangeProjector(opts) { } // Claude-side identity provenance. Per the phase 2 spec, only - // the missing-log case stamps an explicit marker — when the + // the missing-log case stamps an explicit marker: when the // transcript supplied uuids the projection's `message_id` / // `parent_uuid` already encode the native DAG and no // extra marker is needed. The gateway still stamps its own @@ -314,7 +314,7 @@ export function createClaudeExchangeProjector(opts) { } // The projector preset is anchored on `/v1/messages` but we - // accept arbitrary paths via header signature — record both so + // accept arbitrary paths via header signature: record both so // the path heuristic is debuggable without re-running the smoke. if (requestPath && !isAnthropicPath(requestPath) && !hasAnthropicHeaderSignature(headers)) { // Defensive: should be unreachable because match() would have @@ -334,7 +334,7 @@ export function createClaudeExchangeProjector(opts) { * Split one wire assistant message into per-block projected messages, * mirroring Claude Code's one-transcript-line-per-block representation. * - * // @ref LLP 0026#decision — alignment: the lines of one API message + * // @ref LLP 0026#decision: alignment: the lines of one API message * // share `message.id` in block order, so when the counts agree each * // block takes its positional line (type-checked); otherwise each * // block falls back to its own content key. Cardinality is recorded @@ -395,12 +395,12 @@ function projectAssistantMessage(args) { /** * Project one wire user message into the transcript's units. * - * // @ref LLP 0026#decision — tool_results split one message per + * // @ref LLP 0026#decision: tool_results split one message per * // block (the transcript writes one line per result; `tool_use_id` * // is the join key). Prompt-style messages stay whole, matched with * // a wire-injected-reminder-stripped retry; on that match the - * // projected content is the TRANSCRIPT's (else live `uuid#0` — a - * // reminder — would collide with backfill `uuid#0` — the prompt), + * // projected content is the TRANSCRIPT's (else live `uuid#0`: a + * // reminder: would collide with backfill `uuid#0`: the prompt), * // and the injected blocks become a separate `wire_only` message. * * @param {{ @@ -442,7 +442,7 @@ function projectUserMessage(args) { const injectedRest = rest.filter(isInjectedReminderBlock) // Only harness-injected reminders are wire_only. Real content riding // alongside tool_results (queued user text, `[Request interrupted…]` - // markers, skill banners) is a genuine user message — project it + // markers, skill banners) is a genuine user message: project it // normally with transcript matching, not as fallback-only noise. if (realRest.length > 0) { /** @type {AiGatewayProjectedMessage} */ @@ -504,7 +504,7 @@ function wholeMessageProjection(role, message) { /** * Copy a transcript line's native identity and provenance onto a - * projected message. No-op when there is no match — the gateway then + * projected message. No-op when there is no match: the gateway then * computes fallback hash identity for the message. * * @param {AiGatewayProjectedMessage} projected @@ -512,7 +512,7 @@ function wholeMessageProjection(role, message) { */ function applyTranscriptMatch(projected, match) { if (!match) return - // Native id only — `previous_message_id` is deliberately NOT supplied. + // Native id only: `previous_message_id` is deliberately NOT supplied. // The gateway fills the full prior-message chain for every row; a // [parentUuid] singleton here would make enriched rows shaped // differently from fallback rows. The native DAG parent lands in @@ -550,7 +550,7 @@ function isInjectedReminderBlock(block) { /** * Register the Anthropic upstream preset on the gateway. Same routing - * surface as `match()` on the projector — keeping them paired here + * surface as `match()` on the projector: keeping them paired here * avoids drift between routing and projection. * * @returns {AiGatewayUpstreamPreset} diff --git a/hypaware-core/plugins-workspace/claude/src/session_context.js b/hypaware-core/plugins-workspace/claude/src/session_context.js index a543566e..2e3d9c59 100644 --- a/hypaware-core/plugins-workspace/claude/src/session_context.js +++ b/hypaware-core/plugins-workspace/claude/src/session_context.js @@ -35,8 +35,8 @@ export function defaultSessionContextFile(stateDir) { * Append one record to the state file, creating parent directories as * needed. Atomic line write (single `appendFile`); concurrent hook * invocations interleave at line granularity, which is fine because - * the reader picks newest-by-line — interleaving across lines just - * means another writer will land its record on the next line. + * the reader picks newest-by-line (interleaving across lines just + * means another writer will land its record on the next line). * * @param {string} filePath * @param {SessionContextRecord} record @@ -78,7 +78,7 @@ export async function readSessionContext(filePath, opts = {}) { if (record) out.push(record) } } catch { - /* truncated / rotated — return what we have */ + /* truncated / rotated: return what we have */ } return out } @@ -122,7 +122,7 @@ function recordFrom(value) { transcript_path: stringValue(value.transcript_path), cwd: stringValue(value.cwd), git_branch: stringValue(value.git_branch), - // @ref LLP 0032#capture — repo identity for the graph bridge. + // @ref LLP 0032#capture: repo identity for the graph bridge. git_remote: stringValue(value.git_remote), head_sha: stringValue(value.head_sha), repo_root: stringValue(value.repo_root), diff --git a/hypaware-core/plugins-workspace/claude/src/settle.js b/hypaware-core/plugins-workspace/claude/src/settle.js index 3a57b9d1..6273768e 100644 --- a/hypaware-core/plugins-workspace/claude/src/settle.js +++ b/hypaware-core/plugins-workspace/claude/src/settle.js @@ -19,14 +19,14 @@ import { pickLatestMatching, readSessionContext } from './session_context.js' * * The live projector writes a Claude message under a fallback hash id * when its transcript line hasn't landed on disk yet (the - * finalize-vs-transcript race). At flush — minutes later, the line now - * present — this upgrades those rows to native uuid identity so they + * finalize-vs-transcript race). At flush (minutes later, the line now + * present), this upgrades those rows to native uuid identity so they * collapse onto the uuid copy a later replay already wrote. * * Each fallback row carries `attributes.claude.match_key` (stamped at * projection from the wire content), so settlement is a pure transcript - * lookup — no need to reconstruct the content array that per-part - * expansion discards. Rows that still don't match (no transcript line — + * lookup (no need to reconstruct the content array that per-part + * expansion discards). Rows that still don't match (no transcript line: * harness aux traffic, wire-only reminders) are returned unchanged. * * @param {{ homeDir: string, stateFile: string, projectsDir?: string, clientName?: string }} opts @@ -49,7 +49,7 @@ export function createClaudeSettlementEnricher(opts) { if (!Array.isArray(rows) || rows.length === 0) return rows // Group fallback rows by session so each session's transcript is - // loaded and indexed once. @ref LLP 0030#decision — the session id + // loaded and indexed once. @ref LLP 0030#decision: the session id // lives in `session_id` now; Claude `conversation_id` is null, so // grouping on it would load nothing and never enrich. /** @type {Map} */ diff --git a/hypaware-core/plugins-workspace/claude/src/transcripts.js b/hypaware-core/plugins-workspace/claude/src/transcripts.js index 8bca8292..9a8bb1f7 100644 --- a/hypaware-core/plugins-workspace/claude/src/transcripts.js +++ b/hypaware-core/plugins-workspace/claude/src/transcripts.js @@ -23,7 +23,7 @@ import readline from 'node:readline' * * The projector calls `loadTranscript()` per exchange. The reader is * best-effort: a missing directory, a missing file, or a truncated - * line never throws — projection falls back to gateway-computed + * line never throws: projection falls back to gateway-computed * identity in that case. */ @@ -63,7 +63,7 @@ export async function loadTranscript(opts) { if (opts.transcriptPath) { await readTranscriptFile(opts.transcriptPath, entries) // Subagent transcripts live next to the session file in a directory - // named for the session, not inside it — without this walk every + // named for the session, not inside it: without this walk every // sidechain message misses transcript identity and lands as a // gateway-fallback row that later duplicates against the backfill. const sessionDir = path.join( @@ -99,14 +99,14 @@ export function* walkTranscriptFiles(projectsDir) { * Read the subagent metadata sidecars Claude Code writes beside each * subagent transcript: `/subagents/agent-.meta.json`. * The sidecar's `toolUseId` is the parent-thread `Agent`/`Task` tool call - * that spawned the subagent — provenance that lives in neither the + * that spawned the subagent: provenance that lives in neither the * subagent's own `.jsonl` (its first line has null parent/source uuids) * nor the wire exchange. Returns a map keyed by the agent id parsed from * each filename. * * Resolution mirrors `loadTranscript`: a `transcriptPath` scans just that - * session's directory (cheap — the live path); otherwise `projectsDir` - * is scanned recursively (the backfill path). Best-effort — a missing + * session's directory (cheap: the live path); otherwise `projectsDir` + * is scanned recursively (the backfill path). Best-effort: a missing * directory or an unparseable sidecar is skipped, never thrown. * * @param {{ transcriptPath?: string, projectsDir?: string }} opts @@ -184,18 +184,18 @@ function byTimestampAsc(a, b) { /** * Index transcript entries for the projector's wire→line matching. * - * // @ref LLP 0026#decision — one line per native DAG node: an API + * // @ref LLP 0026#decision: one line per native DAG node: an API * // message spans SEVERAL lines (one per assistant block), so the * // message-id index must keep the ordered list, not last-wins. * - * - `byUuid` — native uuid → entry. - * - `byMessageId` — API `message.id` → ordered entry list (block + * - `byUuid` : native uuid → entry. + * - `byMessageId` : API `message.id` → ordered entry list (block * order; assistant turns split one line per block * all sharing the API id). - * - `byToolUseId` — `tool_use_id` of a user tool_result line → + * - `byToolUseId` : `tool_use_id` of a user tool_result line → * entry. Each tool_result is its own line, so * this is a unique join key. - * - `byContentKey` — canonicalized role+content key → entry. + * - `byContentKey` : canonicalized role+content key → entry. * * @param {TranscriptEntry[]} entries */ @@ -228,11 +228,11 @@ export function indexTranscriptEntries(entries) { * loop AND every subagent, and content can repeat across them; without * this a subagent block could match a main-session (or other-agent) * entry and inherit the wrong uuid / `is_sidechain`. `byMessageId` and - * `byToolUseId` need no scoping — those ids are globally unique. + * `byToolUseId` need no scoping: those ids are globally unique. * `agent_id` empty/undefined is the main loop; ids and content keys are * hex, so `:` is an unambiguous separator. * - * // @ref LLP 0026#decision — match within a thread, not across the session. + * // @ref LLP 0026#decision: match within a thread, not across the session. * * @param {string | undefined} agentId * @param {string} contentKey @@ -267,7 +267,7 @@ function entryToolUseId(entry) { * canonical role+content key, with an optional `message.id` shortcut * that only applies when the id maps to exactly one line (an API * message split across several lines is ambiguous at message - * granularity — the splitter aligns those per block instead). + * granularity: the splitter aligns those per block instead). * * The content-key lookup is scoped to `candidate.agentId` (the * exchange's `x-claude-code-agent-id`, empty for the main loop) so a @@ -290,9 +290,9 @@ export function findTranscriptMatch(index, candidate) { * its transcript line. Exported so the projector can stamp it on a * fallback row at projection time (when wire content is in hand) and * flush-time settlement can re-match by pure lookup once the transcript - * line lands — without reconstructing the lost content array. + * line lands: without reconstructing the lost content array. * - * // @ref LLP 0027#decision — match-key at projection enables flush-time settlement. + * // @ref LLP 0027#decision: match-key at projection enables flush-time settlement. * * @param {string} role * @param {unknown} content @@ -311,7 +311,7 @@ export function matchKey(role, content) { * to the gateway (full prior-message chain) so enriched and fallback * rows stay one shape. * - * // @ref LLP 0027#decision — one identity-copy core for projection and settlement. + * // @ref LLP 0027#decision: one identity-copy core for projection and settlement. * * @param {Record} target * @param {TranscriptEntry} match @@ -341,7 +341,7 @@ export function assignTranscriptIdentity(target, match) { } /** - * The block type a single transcript line holds — used by the + * The block type a single transcript line holds: used by the * splitter's order-alignment sanity check. String content is a text * line; array content reports the first block's type (lines are * single-block in current transcripts). @@ -528,12 +528,12 @@ function readKey(obj, key) { /** * The model id from an assistant transcript line's `message.model`. * - * @ref LLP 0026#decision [implements] — native per-line granularity: each + * @ref LLP 0026#decision [implements]: native per-line granularity: each * assistant line carries its own model, so backfill surfaces it per message * rather than collapsing a session to one model. Only assistant lines record * `message.model`; user-prompt and tool_result lines have none. Claude Code * stamps `` on assistant lines it generates locally (interrupt - * notices, injected errors) that never hit a model — that is a sentinel, not a + * notices, injected errors) that never hit a model: that is a sentinel, not a * model id, so it is dropped to undefined. * @param {unknown} message */ diff --git a/hypaware-core/plugins-workspace/codex/src/backfill.js b/hypaware-core/plugins-workspace/codex/src/backfill.js index 13447a0f..3dd0111e 100644 --- a/hypaware-core/plugins-workspace/codex/src/backfill.js +++ b/hypaware-core/plugins-workspace/codex/src/backfill.js @@ -25,10 +25,10 @@ import { redactRemoteUserinfo } from './git-remote.js' * Discovery stack (V1): * - `/sessions/**` PRIMARY. Session/event JSONL (modern) * and legacy single-doc `{session,items}`. - * - `/history.*` diagnostic only — command/input history, + * - `/history.*` diagnostic only: command/input history, * not enough for canonical rows. Detected, * never parsed here. - * - `/log/**` diagnostic only — version/breadcrumbs. + * - `/log/**` diagnostic only: version/breadcrumbs. * - ChatGPT/Codex app + browser detected, NEVER parsed in V1; flagged * storage via an `unsupported_location` event. * - HypAware's gateway cache excluded (lives under HYP_HOME, not @@ -36,7 +36,7 @@ import { redactRemoteUserinfo } from './git-remote.js' * * Parsing is best-effort and version-defensive: a malformed line, a * truncated trailing record, or an unreadable file degrades to whatever - * parsed cleanly rather than aborting the run. Reruns are deterministic — + * parsed cleanly rather than aborting the run. Reruns are deterministic: * ids, parents, and timestamps come straight from the immutable rollout * and the materializer is pure. */ @@ -45,7 +45,7 @@ const DEFAULT_CLIENT_NAME = 'codex' const DEFAULT_PLUGIN_NAME = '@hypaware/codex' // The Codex client speaks the OpenAI wire format, so backfilled rows carry -// provider 'openai' and conversation_source 'codex' — matching the live +// provider 'openai' and conversation_source 'codex': matching the live // @hypaware/codex exchange projector's chatgpt/api output split. const PROVIDER = 'openai' const CONVERSATION_SOURCE = 'codex' @@ -217,7 +217,7 @@ async function* runCodexBackfill(args) { } /** - * Flag — but never parse — Codex/ChatGPT app and browser storage. Each + * Flag, but never parse, Codex/ChatGPT app and browser storage. Each * existing location emits both a structured log and an `unsupported_location` * `BackfillEvent` (the kernel's named lifecycle signal) so the runner and a * human can see what history was left on the table. @@ -327,7 +327,7 @@ function isRolloutFileName(name) { * Parse one rollout file into zero or more sessions. The legacy format is a * single pretty-printed JSON object; the modern format is line-delimited * records. Each file holds one session, so the array carries at most one - * entry — the array shape just keeps the run loop uniform. + * entry: the array shape just keeps the run loop uniform. * * @param {string} filePath * @returns {Promise} @@ -369,7 +369,7 @@ function parseLegacyDoc(text, filePath) { } /** - * Modern rollout: line-delimited `{ timestamp, type, payload }` records — + * Modern rollout: line-delimited `{ timestamp, type, payload }` records: * one `session_meta`, zero+ `turn_context`, and the conversation's * `response_item`s. A `token_count` `event_msg` carries the turn's token * usage and is captured as a synthetic turn-boundary marker (no message); @@ -410,7 +410,7 @@ function parseJsonlRollout(text, filePath) { } else if (type === 'response_item' && payload) { items.push({ payload, timestampMs: timestampToMs(row.timestamp) }) } else if (type === 'event_msg' && payload) { - // The one event_msg we keep: token_count. It is NOT a message — it is a + // The one event_msg we keep: token_count. It is NOT a message: it is a // turn-boundary marker carrying that turn's normalized usage. Its slot in // the items stream is preserved (so the projector can attribute it to the // preceding assistant message), but it never projects a row of its own. @@ -513,7 +513,7 @@ function projectedExchangeFromSession(args) { /** @type {AiGatewayProjectedExchange} */ const exchange = { provider: PROVIDER, - // @ref LLP 0030#decision — the rollout id is the thread; the rollout + // @ref LLP 0030#decision: the rollout id is the thread; the rollout // carries no distinct session id, so session_id (the non-null // partition key) and conversation_id (the thread) are both the // rollout id here. @@ -527,7 +527,7 @@ function projectedExchangeFromSession(args) { if (session.startedAtMs !== undefined) exchange.conversation_started_at = new Date(session.startedAtMs).toISOString() if (session.cwd) exchange.cwd = session.cwd if (session.gitBranch) exchange.git_branch = session.gitBranch - // @ref LLP 0032#capture — repo identity for the graph bridge (Repo/Commit), + // @ref LLP 0032#capture: repo identity for the graph bridge (Repo/Commit), // from the rollout's session_meta `git` block (commitKey rejects an // abbreviated sha). repo_root is intentionally NOT set from `cwd`: the rollout // records no verified git toplevel, and the cwd may be a repo subdir, which @@ -711,7 +711,7 @@ function reasoningItemToProjected(payload) { /** * Pull a turn's normalized token usage from a `token_count` event_msg * payload. Reads the per-turn delta (`info.last_token_usage`), NOT the - * cumulative session running total (`info.total_token_usage`) — stamping the + * cumulative session running total (`info.total_token_usage`): stamping the * cumulative would multiply-count when usage is summed across a conversation. * Returns `undefined` for any other event_msg or a usage-less payload. * @@ -737,7 +737,7 @@ function codexUsageAttributes(rawUsage) { /** @type {JsonObject} */ const usage = {} - // @ref LLP 0035#net-input — Codex `input_tokens` is gross (it includes + // @ref LLP 0035#net-input: Codex `input_tokens` is gross (it includes // `cached_input_tokens`); HypAware stores input_tokens NET of cache so it // never double-counts against cache_read_tokens and matches the Claude / // live-Codex convention. total_tokens stays raw, so net + cache_read + @@ -758,7 +758,7 @@ function codexUsageAttributes(rawUsage) { /** * Stamp a turn's usage onto the LAST assistant message at or after - * `startIndex` that carries text or a tool_use — the same target the live + * `startIndex` that carries text or a tool_use: the same target the live * projector picks (its terminal output item), so live and backfilled rows fold * usage onto the one logical message and dedupe cleanly, and the row carrying * usage is the response's last assistant row for both Codex and Claude. diff --git a/hypaware-core/plugins-workspace/codex/src/config.js b/hypaware-core/plugins-workspace/codex/src/config.js index 215ce52e..94b5a227 100644 --- a/hypaware-core/plugins-workspace/codex/src/config.js +++ b/hypaware-core/plugins-workspace/codex/src/config.js @@ -3,7 +3,7 @@ /** * Config validation for the `@hypaware/codex` plugin's own `config` * block. v1 validates only the optional `backfill` sub-object that drives - * backfill-on-join — `{ on_join, window_days }`. Every other key passes + * backfill-on-join: `{ on_join, window_days }`. Every other key passes * through untouched so existing configs keep working; there is no * top-level `backfill` section and nothing new for core to validate. * @@ -22,7 +22,7 @@ export const CODEX_CONFIG_SECTION = 'codex' * `backfill` policy block is checked; unknown sibling keys are ignored so * the validator stays additive over the existing config surface. * - * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements] — + * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements]: * backfill policy ({ on_join, window_days }) lives in and is validated * by the source plugin's own config section; the kernel reconciler adds * no top-level schema. @@ -46,7 +46,7 @@ export function validateCodexConfig(value) { * backfill-capable source plugin: `on_join` (whether to import on join, * boolean) and `window_days` (how far back, positive integer). Both are * optional; unknown keys are rejected so a typo (`window_day`) surfaces - * instead of being silently ignored. Pure — the caller chooses where the + * instead of being silently ignored. Pure: the caller chooses where the * returned pointers mount. * * @param {unknown} value diff --git a/hypaware-core/plugins-workspace/codex/src/exchange-projector.js b/hypaware-core/plugins-workspace/codex/src/exchange-projector.js index 39dced45..2acc9ad8 100644 --- a/hypaware-core/plugins-workspace/codex/src/exchange-projector.js +++ b/hypaware-core/plugins-workspace/codex/src/exchange-projector.js @@ -14,9 +14,9 @@ import { redactRemoteUserinfo } from './git-remote.js' * single projector subsumes three transport flavors that all flow * through the Codex client: * - * - OpenAI Chat (`/v1/chat/completions`) — non-streaming JSON. - * - OpenAI Responses (`/v1/responses`) — JSON or SSE. - * - ChatGPT Codex (`/backend-api/codex/*`) — SSE, with Codex-specific + * - OpenAI Chat (`/v1/chat/completions`): non-streaming JSON. + * - OpenAI Responses (`/v1/responses`): JSON or SSE. + * - ChatGPT Codex (`/backend-api/codex/*`): SSE, with Codex-specific * turn metadata, workspace, and identity headers. * * The match function is intentionally permissive across these paths @@ -70,7 +70,7 @@ export function createCodexExchangeProjector(opts = {}) { const augmented = augmentFromLogReaders(logReaders, input) const conversationId = resolveConversationId(reqBody, input, provider, path, codexContext) - // @ref LLP 0030#decision — session_id is the partition key (always + // @ref LLP 0030#decision: session_id is the partition key (always // non-null): Codex's `metadata.session_id`, falling back to the // thread (conversation_id) when no session id was captured. Keep // conversation_id = the thread; both can be set for Codex. @@ -96,8 +96,8 @@ export function createCodexExchangeProjector(opts = {}) { conversation_source: resolveConversationSource(provider), cwd: recordedContext.cwd, git_branch: recordedContext.git_branch, - // @ref LLP 0032#capture — repo identity for the graph bridge (Repo/Commit). - // repo_root is intentionally omitted for Codex (left null) — see + // @ref LLP 0032#capture: repo identity for the graph bridge (Repo/Commit). + // repo_root is intentionally omitted for Codex (left null). See // resolveCodexContext. @ref LLP 0032#codex-repo-root git_remote: codexContext?.git_remote, head_sha: codexContext?.head_sha, @@ -344,7 +344,7 @@ function openAiContentBlocks(content) { /** * Fan out response `output[]` items so each becomes its own assistant - * message — same per-item shape `responsesInputMessages` produces for + * message: same per-item shape `responsesInputMessages` produces for * replayed input items, so turn-1 response rows hash equal to turn-2 * input rows in the kernel's content-hash dedupe. * @@ -502,7 +502,7 @@ function readOpenAiUsageFromResponsesStream(streamEvents) { * Normalize OpenAI Chat Completions and Responses usage into the * `attributes.usage` shape already used by Claude rows. The provider's * usage object is response-scoped, so callers stamp it onto exactly one - * response assistant message — the LAST one — rather than every fanned-out + * response assistant message (the LAST one) rather than every fanned-out * output item. @ref LLP 0035#one-carrier * * @param {Record | undefined} rawUsage @@ -513,7 +513,7 @@ function openAiUsageAttributes(rawUsage) { /** @type {JsonObject} */ const usage = {} - // @ref LLP 0035#net-input — OpenAI/Codex report input_tokens INCLUSIVE of + // @ref LLP 0035#net-input: OpenAI/Codex report input_tokens INCLUSIVE of // cached prompt reads; HypAware stores input_tokens NET of cache so it never // double-counts against cache_read_tokens and matches the Claude convention // (input + cache_read [+ cache_write] = total prompt). total_tokens stays the @@ -553,14 +553,14 @@ function openAiUsageAttributes(rawUsage) { /** * Stamp response-level usage onto the LAST assistant message of the response - * that carries text or a tool_use (the terminal output item — a tool_use on + * that carries text or a tool_use (the terminal output item, a tool_use on * tool-calling turns, else the final text). One carrier per response keeps a * SUM over rows honest, and "last text/tool_use assistant" is the SAME * predicate the backfill path applies (`backfill.js#stampUsageOnTurn` → * `hasTextOrToolUse`), so live and backfilled rows fold usage onto the same * logical row and dedupe to one. Sharing the predicate means the rule no longer * rests on the implicit invariant that a live Responses reply never ends in a - * reasoning-only assistant message — today it never does (reasoning isn't + * reasoning-only assistant message. Today it never does (reasoning isn't * projected as an assistant message live), and if that ever changed the usage * would be dropped rather than mis-attributed, exactly as backfill does. * @ref LLP 0035#one-carrier @@ -693,11 +693,11 @@ function resolveCodexContext(input, provider, path, reqBody) { client_version: client.version, entrypoint: originator, sandbox, - // @ref LLP 0032#capture — repo identity for the graph bridge, already in the + // @ref LLP 0032#capture: repo identity for the graph bridge, already in the // turn metadata (also kept in attributes.codex.* for provenance). Only // git_remote/head_sha are first-class: they feed Repo/Commit convergence and // need no repo root. repo_root is deliberately NOT derived from the workspace - // path — Codex exposes no verified git toplevel, and the workspace may be a + // path. Codex exposes no verified git toplevel, and the workspace may be a // repo *subdir*, which would mis-relativize (or collide) File keys. Codex // File nodes therefore keep absolute keys in V1. @ref LLP 0032#codex-repo-root git_remote: git_origin_url, @@ -920,7 +920,7 @@ function extractSystemText(system) { /** * Apply registered log readers and merge any returned attributes. - * Today no readers are shipped — this is a no-op stub kept behind + * Today no readers are shipped. This is a no-op stub kept behind * the `HYPAWARE_CODEX_SQLITE_READS` env flag so a future bead can * register the Codex SQLite-turn reader without churning the * projector interface. @@ -999,7 +999,7 @@ function normalizeToolInput(value) { /** * Codex tool output can arrive as a string, a `{ output | content | text }` - * wrapper, or a structured payload — fall back to JSON.stringify so the + * wrapper, or a structured payload. Fall back to JSON.stringify so the * row keeps a faithful trace. * * @param {unknown} output diff --git a/hypaware-core/plugins-workspace/codex/src/git-remote.js b/hypaware-core/plugins-workspace/codex/src/git-remote.js index 9eafb5f0..ffe0d514 100644 --- a/hypaware-core/plugins-workspace/codex/src/git-remote.js +++ b/hypaware-core/plugins-workspace/codex/src/git-remote.js @@ -2,8 +2,8 @@ /** * Strip credential userinfo (`user[:token]@`) from a git remote URL so a token - * embedded in an HTTPS remote — e.g. `https://x-access-token:@github.com/owner/repo.git`, - * which `gh` and CI checkouts write into `remote.origin.url` — never lands in a + * embedded in an HTTPS remote: e.g. `https://x-access-token:@github.com/owner/repo.git`, + * which `gh` and CI checkouts write into `remote.origin.url`, never lands in a * stored `git_remote` column, the `attributes.codex.git_origin_url` mirror, or * the graph's `source_keys`. Convergence only needs the normalized `owner/repo` * (`keys.ownerRepoFromRemote` discards userinfo anyway), so the raw secret has @@ -15,9 +15,9 @@ * userinfo-free case pass through unchanged. * * Kept tiny and duplicated per capture plugin (see `@hypaware/claude`) rather - * than shared — the plugins are decoupled; a test on each path pins it. + * than shared; the plugins are decoupled; a test on each path pins it. * - * @ref LLP 0032#remote-redaction — owner/repo is all convergence needs; the raw remote can carry a secret + * @ref LLP 0032#remote-redaction: owner/repo is all convergence needs; the raw remote can carry a secret * @param {string | undefined} remote * @returns {string | undefined} */ diff --git a/hypaware-core/plugins-workspace/codex/src/index.js b/hypaware-core/plugins-workspace/codex/src/index.js index 773b3909..bd64ea78 100644 --- a/hypaware-core/plugins-workspace/codex/src/index.js +++ b/hypaware-core/plugins-workspace/codex/src/index.js @@ -23,12 +23,12 @@ const CHATGPT_UPSTREAM_NAME = 'chatgpt' /** * The plugin's `config_sections` validator, surfaced as a side-effect-free * export so the kernel apply path can validate this plugin's `config` block - * (the `backfill` policy) *before* the plugin is ever activated — e.g. a + * (the `backfill` policy) *before* the plugin is ever activated: e.g. a * central config that first introduces `@hypaware/codex`. It is the same * registration `activate()` hands `ctx.configRegistry.registerSection`; * importing this module never runs `activate()`, so discovery is safe. * - * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements] — the plugin owns + exposes its own `backfill` validator + * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements]: the plugin owns + exposes its own `backfill` validator * @type {{ section: string, validate: typeof validateCodexConfig }} */ export const configSection = { section: CODEX_CONFIG_SECTION, validate: validateCodexConfig } @@ -47,14 +47,14 @@ export const configSection = { section: CODEX_CONFIG_SECTION, validate: validate * `restored=true|false`. * * @param {PluginActivationContext} ctx - * @ref LLP 0016#knows-nothing-about-claude-or-codex [implements] — adapter requires the ai-gateway capability; registers client + upstream presets + * @ref LLP 0016#knows-nothing-about-claude-or-codex [implements]: adapter requires the ai-gateway capability; registers client + upstream presets */ export async function activate(ctx) { - // Validate the plugin's own `config` block — currently just the + // Validate the plugin's own `config` block: currently just the // optional `backfill` policy ({ on_join, window_days }) that drives // backfill-on-join. Registered so the kernel runs it via // `runPerPluginSectionValidators`; no top-level core schema change. - // @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements] — the source plugin owns and validates its `backfill` config + // @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements]: the source plugin owns and validates its `backfill` config ctx.configRegistry.registerSection({ plugin: PLUGIN_NAME, section: CODEX_CONFIG_SECTION, diff --git a/hypaware-core/plugins-workspace/completion-anthropic/src/client.js b/hypaware-core/plugins-workspace/completion-anthropic/src/client.js index ebd8eeea..8c59bf7f 100644 --- a/hypaware-core/plugins-workspace/completion-anthropic/src/client.js +++ b/hypaware-core/plugins-workspace/completion-anthropic/src/client.js @@ -14,12 +14,12 @@ const PLUGIN_NAME = '@hypaware/completion-anthropic' * Build the `hypaware.completion` capability value: an HTTP client for * the Anthropic Messages API (`POST /v1/messages`). * - * There is deliberately no retry layer — callers (an enrichment tick, an + * There is deliberately no retry layer: callers (an enrichment tick, an * interactive command) run on their own cadence, so a failed request * surfaces immediately instead of stalling a tick. A `refusal` is NOT an * error: the Messages API returns it as a successful HTTP 200, so * `complete()`/`stream()` return it as `stopReason: 'refusal'` and the - * caller decides — exactly as the capability contract requires. + * caller decides: exactly as the capability contract requires. * * @param {CreateAnthropicCompletionOptions} opts * @returns {CompletionCapability} @@ -79,7 +79,7 @@ export function createAnthropicCompletion(opts) { { component: 'completion' } ).catch((/** @type {unknown} */ err) => { const errorKind = /** @type {HypError} */ (err)?.hypErrorKind ?? 'completion_failed' - // Prompt content and key material never reach logs — counts only. + // Prompt content and key material never reach logs: counts only. log.error('completion.complete_failed', { [Attr.ERROR_KIND]: errorKind, completion_model: model, @@ -115,7 +115,7 @@ function assertMessages(req) { /** * Compose the request body + headers. The API key resolves from the * environment at call time, used only for `x-api-key`; it is never logged, - * thrown, or stored. `params` is the provider passthrough — `betas` lifts + * thrown, or stored. `params` is the provider passthrough: `betas` lifts * to the `anthropic-beta` header, everything else merges into the body * (e.g. `thinking`, `tool_choice`, `output_config.effort`). Explicit * fields (messages/system/tools) always win over `params`. @@ -250,7 +250,7 @@ function contentToText(content) { /** * Send the request, mapping transport/HTTP failures to `hypErrorKind` - * errors. The response body is deliberately never read into an error — + * errors. The response body is deliberately never read into an error: * a provider may echo prompt content or credential material in its error * detail; status + endpoint + kind are enough to diagnose. * @@ -262,9 +262,9 @@ async function sendRequest({ endpoint, headers, body, fetchImpl, config, signal } /** - * Generalized HTTP send (any method, optional body — the batch poll/results + * Generalized HTTP send (any method, optional body: the batch poll/results * GETs carry none), mapping transport/HTTP failures to `hypErrorKind` errors. - * The response body is deliberately never read into an error — a provider may + * The response body is deliberately never read into an error: a provider may * echo prompt content or credential material in its error detail; status + * endpoint + kind are enough to diagnose. * @@ -346,12 +346,12 @@ export function parseAnthropicMessageResponse(payload, ctx) { } /** - * Submit a Message Batch — one Anthropic batch job over N `{custom_id, params}` + * Submit a Message Batch: one Anthropic batch job over N `{custom_id, params}` * requests, each `params` a Messages body built by {@link composeMessageBody}. * Returns the initial job status; the caller polls and collects. Any `betas` * across the requests are unioned onto the submit's `anthropic-beta` header. * - * @ref LLP 0028#two-regimes [implements] + * @ref LLP 0028#two-regimes [implements]: * * @param {{ requests: CompletionBatchRequest[], config: AnthropicCompletionConfig, env: NodeJS.ProcessEnv, fetchImpl: FetchLike, batchEndpoint: string, log: PluginLogger, signal: AbortSignal | undefined }} args * @returns {Promise} @@ -479,7 +479,7 @@ function parseBatchStatusFull(payload) { * Parse the JSONL results body into per-`custom_id` outcomes. A `succeeded` * entry's `message` is normalized via {@link parseAnthropicMessageResponse} * (so a `refusal` arrives as a successful result with `stopReason: 'refusal'`); - * everything else surfaces only the safe error *category* — never the provider + * everything else surfaces only the safe error *category*: never the provider * error message, which can echo prompt content. * * @param {string} text diff --git a/hypaware-core/plugins-workspace/completion-anthropic/src/config.js b/hypaware-core/plugins-workspace/completion-anthropic/src/config.js index 68026b7f..990a66dc 100644 --- a/hypaware-core/plugins-workspace/completion-anthropic/src/config.js +++ b/hypaware-core/plugins-workspace/completion-anthropic/src/config.js @@ -16,12 +16,12 @@ export const DEFAULT_MODEL = 'claude-opus-4-8' export const DEFAULT_API_KEY_ENV = 'ANTHROPIC_API_KEY' export const DEFAULT_ANTHROPIC_VERSION = '2023-06-01' export const DEFAULT_MAX_TOKENS = 4096 -// Generation can run far longer than embedding — default well above the +// Generation can run far longer than embedding: default well above the // embedder's 30s so a slow frontier model isn't cut off mid-response. export const DEFAULT_TIMEOUT_MS = 120_000 /** - * Validate the plugin's config slice. Every field is optional — the + * Validate the plugin's config slice. Every field is optional: the * zero-config default targets Anthropic with `ANTHROPIC_API_KEY` and Opus. * * @param {unknown} value diff --git a/hypaware-core/plugins-workspace/completion-anthropic/src/index.js b/hypaware-core/plugins-workspace/completion-anthropic/src/index.js index 8fd20fd9..686ea46e 100644 --- a/hypaware-core/plugins-workspace/completion-anthropic/src/index.js +++ b/hypaware-core/plugins-workspace/completion-anthropic/src/index.js @@ -15,7 +15,7 @@ const CAPABILITY_VERSION = '1.0.0' * `hypaware.completion@1` capability backed by the Anthropic Messages API * (`POST /v1/messages`). * - * Activation performs no network IO and reads no credentials — the API key + * Activation performs no network IO and reads no credentials: the API key * resolves from the environment per request. Enabling this plugin is the * explicit opt-in that allows captured content (prompts built from the * graph and source) to leave the machine; a localhost `base_url` (a @@ -33,7 +33,7 @@ export async function activate(ctx) { const validated = validateAnthropicCompletionConfig(ctx.config) if (!validated.ok) { const detail = validated.errors.map((e) => `${e.pointer || '/'}: ${e.message}`).join('; ') - const err = /** @type {HypError} */ (new Error(`${PLUGIN_NAME}: invalid config — ${detail}`)) + const err = /** @type {HypError} */ (new Error(`${PLUGIN_NAME}: invalid config - ${detail}`)) err.hypErrorKind = 'completion_config_invalid' throw err } diff --git a/hypaware-core/plugins-workspace/completion-openai/src/client.js b/hypaware-core/plugins-workspace/completion-openai/src/client.js index 8df6a35e..22b52319 100644 --- a/hypaware-core/plugins-workspace/completion-openai/src/client.js +++ b/hypaware-core/plugins-workspace/completion-openai/src/client.js @@ -14,7 +14,7 @@ const PLUGIN_NAME = '@hypaware/completion-openai' * Build the `hypaware.completion` capability value: an HTTP client for * the OpenAI-compatible Chat Completions API (`POST /v1/chat/completions`). * - * There is deliberately no retry layer — callers run on their own + * There is deliberately no retry layer: callers run on their own * cadence, so a failed request surfaces immediately. The API key resolves * from the environment at call time and is sent only as a Bearer header; * an unset key sends no Authorization header so localhost servers (Ollama, diff --git a/hypaware-core/plugins-workspace/completion-openai/src/config.js b/hypaware-core/plugins-workspace/completion-openai/src/config.js index 7e7f9de7..ac57028b 100644 --- a/hypaware-core/plugins-workspace/completion-openai/src/config.js +++ b/hypaware-core/plugins-workspace/completion-openai/src/config.js @@ -18,12 +18,12 @@ export const DEFAULT_BASE_URL = 'https://api.openai.com' export const DEFAULT_MODEL = 'gpt-4o-mini' export const DEFAULT_API_KEY_ENV = 'OPENAI_API_KEY' export const DEFAULT_MAX_TOKENS = 4096 -// Generation can run far longer than embedding — default well above the +// Generation can run far longer than embedding. Default well above the // embedder's 30s so a slow model isn't cut off mid-response. export const DEFAULT_TIMEOUT_MS = 120_000 /** - * Validate the plugin's config slice. Every field is optional — the + * Validate the plugin's config slice. Every field is optional. The * zero-config default targets OpenAI with `OPENAI_API_KEY`. * * @param {unknown} value diff --git a/hypaware-core/plugins-workspace/completion-openai/src/index.js b/hypaware-core/plugins-workspace/completion-openai/src/index.js index 4fdf1be7..06d3cecf 100644 --- a/hypaware-core/plugins-workspace/completion-openai/src/index.js +++ b/hypaware-core/plugins-workspace/completion-openai/src/index.js @@ -15,7 +15,7 @@ const CAPABILITY_VERSION = '1.0.0' * `hypaware.completion@1` capability backed by an OpenAI-compatible * `POST /v1/chat/completions` client. * - * Activation performs no network IO and reads no credentials — the API key + * Activation performs no network IO and reads no credentials: the API key * resolves from the environment per request. Enabling this plugin is the * explicit opt-in that allows captured content to leave the machine when * `base_url` points at a remote provider; a localhost `base_url` (Ollama, @@ -33,7 +33,7 @@ export async function activate(ctx) { const validated = validateOpenAiCompletionConfig(ctx.config) if (!validated.ok) { const detail = validated.errors.map((e) => `${e.pointer || '/'}: ${e.message}`).join('; ') - const err = /** @type {HypError} */ (new Error(`${PLUGIN_NAME}: invalid config — ${detail}`)) + const err = /** @type {HypError} */ (new Error(`${PLUGIN_NAME}: invalid config - ${detail}`)) err.hypErrorKind = 'completion_config_invalid' throw err } diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/batch.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/batch.js index 697f6814..ea39a4d2 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/batch.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/batch.js @@ -22,16 +22,16 @@ import { readState, updateState } from './state.js' * calls go through the Anthropic **Batch API** (50% off, async). Two drivers * share one cluster-building + routing core (from curate.js): * - * - **backfill** (`hyp enrich backfill`) — {@link runCurateBatch} submits the + * - **backfill** (`hyp enrich backfill`) - {@link runCurateBatch} submits the * whole eligible pool and **polls to completion in one run** (out of daemon). - * - **ongoing** (daemon source) — {@link submitCurateJob} submits on one tick + * - **ongoing** (daemon source) - {@link submitCurateJob} submits on one tick * and {@link collectCurateJob} collects on a later tick, carrying the job in * the sidecar, so the frontier work never blocks a daemon tick. * * Either driver falls back to the synchronous {@link runCurateTick} when the * completion provider exposes no `batch` surface. * - * @ref LLP 0028#two-regimes [implements] + * @ref LLP 0028#two-regimes [implements]: * * @import { EnrichRuntime } from './types.d.ts' * @import { CompletionBatch, CompletionBatchStatus, CompletionRequest, SourceStatus, StartedSource, VectorSearchHit } from '../../../../collectivus-plugin-kernel-types.d.ts' @@ -40,7 +40,7 @@ import { readState, updateState } from './state.js' /** * Run a complete curate batch end-to-end: cluster the whole pending pool, * submit, poll to completion, collect, route, append. Blocks until the job - * finishes (≤24h) — the deliberate backfill-command path. Falls back to a + * finishes (≤24h): the deliberate backfill-command path. Falls back to a * synchronous tick when the provider has no batch API. * * @param {EnrichRuntime} runtime @@ -51,7 +51,7 @@ export async function runCurateBatch(runtime, opts = {}) { const completion = getCompletion(runtime) const batch = completion.batch // Dry run: build the scoped pool + clusters and report, submitting and writing - // nothing — so `--since` scoping and the resulting curator-call count can be + // nothing, so `--since` scoping and the resulting curator-call count can be // confirmed before the (paid) Batch submit. Independent of the batch API. if (opts.dryRun) { const pending = await selectPending(runtime, { anchorKeys: opts.anchorKeys }) @@ -64,16 +64,16 @@ export async function runCurateBatch(runtime, opts = {}) { return { ...sync, batched: false } } // Crash recovery: if *our own* (backfill) batch job is already persisted - // (submitted but not yet collected), resume it — poll to completion and collect - // from the persisted cluster→prospect map — rather than submitting a new + // (submitted but not yet collected), resume it: poll to completion and collect + // from the persisted cluster→prospect map, rather than submitting a new // (re-billed) batch. A daemon-owned job belongs to the ongoing regime's // submit-and-collect; this one shared slot can't hold two jobs, so overwriting // it would orphan the daemon's already-billed batch. Refuse instead of clobber. - // @ref LLP 0028#two-regimes [constrained-by] — shared curate_job slot ownership + // @ref LLP 0028#two-regimes [constrained-by]: shared curate_job slot ownership const inflight = readState(runtime.stateDir).curate_job if (inflight) { if (inflight.source !== 'backfill') { - throw new Error(`a daemon curate batch job is in flight (id ${inflight.id}); refusing to run backfill curate concurrently — disable the daemon curate source (or wait for it to collect) and retry`) + throw new Error(`a daemon curate batch job is in flight (id ${inflight.id}); refusing to run backfill curate concurrently - disable the daemon curate source (or wait for it to collect) and retry`) } // Recompute the scoped pending count so the caller's `N/M processed` line is // truthful on the resume path (the pool is still unresolved pre-collect), @@ -122,7 +122,7 @@ export async function runCurateBatch(runtime, opts = {}) { await appendCommitted(runtime, committedRows) await appendResolutions(runtime, resolutionRows) - // Results are committed — clear the persisted recovery job. + // Results are committed, clear the persisted recovery job. updateState(runtime.stateDir, (cur) => ({ ...cur, curate_job: null })) span.setAttribute('pending', pending.length) @@ -137,7 +137,7 @@ export async function runCurateBatch(runtime, opts = {}) { } /** - * Submit a curate batch for the ongoing regime and record it in the sidecar — + * Submit a curate batch for the ongoing regime and record it in the sidecar: * **does not block** waiting for results ({@link collectCurateJob} picks them up * on a later tick). Below-salience prospects get their terminal `skip` * resolutions now (batch-independent). No-op when a job is already in flight; @@ -193,7 +193,7 @@ export async function submitCurateJob(runtime, opts = {}) { * * Collects only the caller's own job: `owner` (default `daemon`) must match the * persisted job's `source`. A foreign job (e.g. a backfill job a daemon tick - * sees, or vice-versa) is left untouched — `phase: 'foreign'` — so the two + * sees, or vice-versa) is left untouched: `phase: 'foreign'`, so the two * drivers never collect/clear each other's batch. * * @param {EnrichRuntime} runtime @@ -273,7 +273,7 @@ async function buildClusterRequests(runtime, clusters, recallByProspect) { /** * Poll a batch job until `status === 'ended'`, sleeping `intervalMs` between - * polls (default 10s; `maxWaitMs` caps the total wait, default 24h — the Batch + * polls (default 10s; `maxWaitMs` caps the total wait, default 24h: the Batch * API's own ceiling). * * @param {CompletionBatch} batch @@ -299,8 +299,8 @@ export async function pollUntilEnded(batch, id, opts = {}) { /** * A cancellable inter-poll wait. The timer is deliberately **not** `unref`'d: * unlike the daemon source intervals (which unref so they never block shutdown), - * this delay is *awaited* inside {@link pollUntilEnded}'s run-to-completion loop — - * the only thing the `hyp enrich backfill` command is doing while it waits — so + * this delay is *awaited* inside {@link pollUntilEnded}'s run-to-completion loop, + * the only thing the `hyp enrich backfill` command is doing while it waits, so * it must keep the event loop alive. An unref'd timer here would let the process * exit mid-poll (abandoning the batch) and leaves an awaited promise pending when * the loop drains, which the node:test runner reports as a failure ("Promise @@ -322,7 +322,7 @@ function delay(ms, signal) { /** * Daemon source for the **ongoing** curate regime: a coarse-interval * submit-and-collect timer. Each tick either collects an in-flight job (and - * waits if it is still running) or submits a fresh one — so the frontier + * waits if it is still running) or submits a fresh one, so the frontier * curator work never blocks a tick. @ref LLP 0028#two-regimes * * @returns {Promise} diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/commands.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/commands.js index f8198557..90f10e53 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/commands.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/commands.js @@ -31,10 +31,10 @@ export async function runEnrich(argv, ctx) { } /** - * `hyp enrich backfill` — the deliberate, out-of-daemon backfill over **all** + * `hyp enrich backfill`: the deliberate, out-of-daemon backfill over **all** * sessions ([§two-regimes](LLP 0028)), mirroring `hyp graph project`. Proposes * every session (T1, synchronous) then curates the whole pending pool through - * the Anthropic **Batch API**, polling to completion. Expensive and one-shot — + * the Anthropic **Batch API**, polling to completion. Expensive and one-shot: * never automatic. * * @param {string[]} argv @@ -64,7 +64,7 @@ export async function runEnrichBackfill(argv, ctx) { const dr = await runCurateBatch(runtime, { anchorKeys, dryRun: true }) ctx.stdout.write( `enrich backfill curate (dry run): ${dr.pending} pending prospect(s) → ${dr.clusters} cluster(s), ` + - `${dr.skipped} below salience — nothing submitted\n` + `${dr.skipped} below salience - nothing submitted\n` ) return 0 } @@ -72,7 +72,7 @@ export async function runEnrichBackfill(argv, ctx) { const cr = await runCurateBatch(runtime, { anchorKeys, onProgress: (s) => ctx.stdout.write(` batch ${s.id}: ${s.status}\n`) }) const via = cr.batched ? 'batch' : 'synchronous (provider has no batch API)' ctx.stdout.write( - `enrich backfill curate (${via}): ${cr.processed}/${cr.pending} processed over ${cr.clusters} cluster(s) — ` + + `enrich backfill curate (${via}): ${cr.processed}/${cr.pending} processed over ${cr.clusters} cluster(s) - ` + `${cr.committed} committed, ${cr.merged} merged, ${cr.rejected} rejected, ${cr.skipped} skipped\n` ) } @@ -86,7 +86,7 @@ export async function runEnrichBackfill(argv, ctx) { /** * Resolve the set of session ids ("anchor keys") whose latest source part is on - * or after `since` (a `YYYY-MM-DD` day read as UTC midnight) — the in-window + * or after `since` (a `YYYY-MM-DD` day read as UTC midnight): the in-window * scope the `--since` curate restricts the pending pool to. Config-driven (the * `anchor_key_column` / `timestamp_column`), so it stays source-agnostic like * the rest of the plugin rather than hardcoding `ai_gateway_messages` columns. @@ -174,7 +174,7 @@ export async function runEnrichCurate(_argv, ctx) { const runtime = requireEnrichRuntime() const r = await runCurateTick(runtime) ctx.stdout.write( - `enrich curate: ${r.processed}/${r.pending} processed in ${r.calls} call(s) over ${r.clusters} cluster(s) — ` + + `enrich curate: ${r.processed}/${r.pending} processed in ${r.calls} call(s) over ${r.clusters} cluster(s) - ` + `${r.committed} committed, ${r.merged} merged, ${r.rejected} rejected, ${r.skipped} skipped\n` + `run 'hyp graph project' to project committed knowledge into the graph\n` ) diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/config.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/config.js index 4ce01e23..3287ef2c 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/config.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/config.js @@ -22,14 +22,14 @@ export const SOURCE_DEFAULTS = Object.freeze({ // the tuple (timestamp_column, tiebreak_column); `part_id` is the per-row id. tiebreak_column: 'part_id', anchor_type: 'Session', - // @ref LLP 0030#decision — the Session anchor keys on session_id (the + // @ref LLP 0030#decision: the Session anchor keys on session_id (the // session container, always present), matching the ai-gateway-graph // Session node; conversation_id is null for Claude. anchor_key_column: 'session_id', - // @ref LLP 0028#row-selection — the enrichment scans *signal*, not plumbing. + // @ref LLP 0028#row-selection: the enrichment scans *signal*, not plumbing. // `part_type` distinguishes content kinds (text / reasoning / tool_call / // tool_result …); `exclude_part_types` drops whole kinds before the model - // sees them. Default excludes `tool_result` — raw tool/file/command output is + // sees them. Default excludes `tool_result`, raw tool/file/command output is // ~60% of the corpus by volume but not durable knowledge worth extracting. part_type_column: 'part_type', exclude_part_types: ['tool_result'], @@ -43,7 +43,7 @@ export const PROPOSE_DEFAULTS = Object.freeze({ enabled: true, interval_minutes: 5, max_tick_ms: 60_000, - // @ref LLP 0028#two-regimes — the proposer is session-oriented, not + // @ref LLP 0028#two-regimes: the proposer is session-oriented, not // row-oriented: an ongoing tick extracts whole *settled* sessions (latest // part older than `settle_cutoff_minutes`), bounded by `max_sessions_per_tick` // so a synchronous tick can't run away. Backfill ignores both knobs. @@ -65,7 +65,7 @@ export const CURATE_DEFAULTS = Object.freeze({ t2_model: 'claude-opus-4-8', salience_threshold: 0.0, recall_top_k: 8, - // @ref LLP 0028#curate-clustering — the curate unit is a similarity/recall + // @ref LLP 0028#curate-clustering: the curate unit is a similarity/recall // cluster, not a session. A prospect whose top recall hit clears // `recall_cluster_floor` is grouped against that committed region; the // no-recall remainder is greedily clustered by embedding cosine @@ -91,7 +91,7 @@ export function validateEnrichConfig(value) { const raw = /** @type {Record} */ (value ?? {}) // Dataset/column fields are interpolated as SQL identifiers in the - // propose/curate queries, so they must be strict identifiers — a typo + // propose/curate queries, so they must be strict identifiers: a typo // becomes a runtime SQL failure, and an unvalidated string would let // crafted config alter the generated query. (`anchor_type` and // `recall_index` are used as values / index names, not SQL identifiers.) @@ -108,7 +108,7 @@ export function validateEnrichConfig(value) { // explicit `[]` disables the part-type filter (it is not undefined, so it // is honored rather than falling back to the default). // - // @ref LLP 0028#row-selection — the default `['tool_result']` only fits the + // @ref LLP 0028#row-selection: the default `['tool_result']` only fits the // default `ai_gateway_messages` schema, which has a `part_type` column. A // custom `source_dataset` may not, so it defaults to *no* part-type filter // (`[]`) rather than emitting `part_type NOT IN (…)` against a column the @@ -245,7 +245,7 @@ function readString(raw, key, errors, prefix = '') { /** * Read an array of non-empty strings. Returns `undefined` when the key is * absent (caller falls back to the default), but an explicit empty array is - * returned as-is — `[]` is a meaningful "filter nothing" value, distinct from + * returned as-is: `[]` is a meaningful "filter nothing" value, distinct from * "not configured". * * @param {Record} raw diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/contract.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/contract.js index a5b6fd3a..eb301b4b 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/contract.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/contract.js @@ -18,7 +18,7 @@ const SELECT_COMMITTED = /** * Build the enrichment projection contract. Projects ONLY committed * knowledge (the graph contract never sees prospects), so a rejected - * prospect — absent from `enrichment_committed` — never reaches the graph. + * prospect (absent from `enrichment_committed`) never reaches the graph. * * One node rule emits a node of `item_type` per committed item (the * projector keys node-vs-edge off `rule.kind`, not `rule.type`, so a single @@ -28,11 +28,11 @@ const SELECT_COMMITTED = * activity node rather than a duplicate. * * Multi-session provenance falls out of this for free: every contributing - * session writes its own committed row (commit/deepen, and `merge` too — see + * session writes its own committed row (commit/deepen, and `merge` too): see * curate.js `routeDecision`) under the same canonical `(item_type, item_id)`. * The content-addressed node id collapses the rows to **one node** (LLP 0023 * dedup-by-id), while each row's distinct anchor yields a separate `produced` - * edge — so the node carries a `produced` edge **per contributing session**, + * edge, so the node carries a `produced` edge **per contributing session**, * append-only. @ref LLP 0028#committed-only-projection * * @param {GraphKit} kit @@ -123,7 +123,7 @@ function asObject(v) { const parsed = JSON.parse(v) if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed } catch { - // not JSON — ignore + // not JSON: ignore } } return {} diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/curate.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/curate.js index d099b75b..4c9a95a5 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/curate.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/curate.js @@ -25,7 +25,7 @@ const MAX_SOURCE_CHARS = 8_000 * {@link curateRequestForCluster} / {@link routeClusterDecisions} pieces but * submit through the Batch API instead (see batch.js). * - * @ref LLP 0028#curate-clustering [implements] + * @ref LLP 0028#curate-clustering [implements]: * * @param {EnrichRuntime} runtime * @param {{ deadlineMs?: number, signal?: AbortSignal }} [opts] @@ -56,7 +56,7 @@ export async function runCurateTick(runtime, opts = {}) { calls++ const routed = routeClusterDecisions(cluster, result, at) if (routed.noDecisions) { - // Refusal / no tool call — leave the cluster pending for retry. + // Refusal / no tool call: leave the cluster pending for retry. runtime.log.warn('enrich.curate_no_decisions', { cluster_size: cluster.length }) continue } @@ -93,12 +93,12 @@ export async function runCurateTick(runtime, opts = {}) { * * An optional `anchorKeys` allowlist scopes the queue to prospects anchored to * those sessions. This is the lever the bounded `hyp enrich backfill --since` - * curate uses to keep the cold-backfill pool — and its per-prospect recall + - * greedy O(n²) clustering ({@link buildCurateClusters}) — tractable, *without* + * curate uses to keep the cold-backfill pool, and its per-prospect recall + + * greedy O(n²) clustering ({@link buildCurateClusters}): tractable, *without* * mutating the append-only prospect table: out-of-window prospects stay pending * for a later, separately-scoped run rather than being deleted or skip-drained. * - * @ref LLP 0028#curate-clustering [constrained-by] + * @ref LLP 0028#curate-clustering [constrained-by]: * * @param {EnrichRuntime} runtime * @param {{ anchorKeys?: Set }} [opts] @@ -240,7 +240,7 @@ export async function appendResolutions(runtime, rows) { * One recall pass over the pending prospects: returns the per-prospect hits * (reused for clustering and the prompt), the salience-ordered above-threshold * prospects (`ordered`, descending novelty), and the below-threshold ones - * (`skipped`). Novelty is `1 - top-1 similarity` to committed knowledge — a + * (`skipped`). Novelty is `1 - top-1 similarity` to committed knowledge: a * cheap, no-LLM triage so the curator spends on the least-covered first; the * caller writes a terminal resolution for the skipped so they drain instead of * re-scoring every tick (@ref LLP 0028#salience-drain). Salience-skipping only @@ -266,7 +266,7 @@ async function scoreAndRecall(runtime, pending) { try { hits = await getVector(runtime).search({ query: clusterText(p), topK: c.recall_top_k, ...(recallIndex ? { index: recallIndex } : {}) }) } catch { - // recall unavailable — treat as fully novel / cold + // recall unavailable: treat as fully novel / cold } recallByProspect.set(pid, hits) const novelty = hits.length > 0 ? 1 - hits[0].score : 1 @@ -281,10 +281,10 @@ async function scoreAndRecall(runtime, pending) { * Group the selected prospects into curator-call clusters * ([§curate-clustering](LLP 0028)): * - * - **Recall-region** — prospects whose top recall hit clears + * - **Recall-region**: prospects whose top recall hit clears * `recall_cluster_floor` are bucketed by that committed node id (dominates the * warm ongoing regime). - * - **Embedding** — the no-recall remainder is greedily clustered by its own + * - **Embedding**: the no-recall remainder is greedily clustered by its own * embeddings so near-duplicate proposals from different sessions land in one * call (dominates the cold backfill regime). Best-effort: if no embedder is * resolvable the remainder falls back to session grouping. @@ -315,7 +315,7 @@ export async function clusterProspects(runtime, prospects, recallByProspect) { } /** - * Bucket warm prospects by their top recalled committed node id — prospects + * Bucket warm prospects by their top recalled committed node id: prospects * that recall the same region of the graph are curated together against it. * Pure. * @@ -462,7 +462,7 @@ function groupByAnchor(prospects) { */ function clusterText(p) { const summary = strField(asObject(p.props).summary) - return `${strField(p.prospect_type)}: ${strField(p.label)}${summary ? ` — ${summary}` : ''}`.trim() + return `${strField(p.prospect_type)}: ${strField(p.label)}${summary ? ` - ${summary}` : ''}`.trim() } /** @@ -490,7 +490,7 @@ function formatHits(hits) { /** * The cluster's **shared recalled knowledge**: the union of every cluster - * prospect's recall hits, deduped by committed node id and ordered by score — + * prospect's recall hits, deduped by committed node id and ordered by score: * the content-based context the curator reasons against (cold clusters yield an * empty block). * @@ -512,7 +512,7 @@ function formatSharedRecalled(cluster, recallByProspect) { /** * Targeted source excerpt behind a set of provenance row ids (the union across a - * cluster's prospects — possibly spanning sessions). Bounded by + * cluster's prospects (possibly spanning sessions). Bounded by * {@link MAX_SOURCE_CHARS}. * * @param {EnrichRuntime} runtime @@ -556,18 +556,18 @@ async function safeDeref(runtime, ids) { * source_keys under the canonical `(item_type, item_key)`, so the * content-addressed graph id collapses the node while the projector emits this * session's `produced` edge ([§committed-only-projection](LLP 0028)). A `reject` - * — or a prospect the curator omitted (implicit reject) — commits nothing, so a + * or a prospect the curator omitted (implicit reject) commits nothing, so a * rejected prospect never reaches `enrichment_committed`, hence never the graph. * * A merge converges only under the *target's* canonical `(item_type, item_id)`, * so it needs BOTH `merge_into` (the key) and `item_type` (the type). If either * is missing, falling back to the prospect's own type/key would derive a * *different* content-addressed id and attach the `produced` edge to the wrong - * node — silent provenance corruption. An under-specified merge is therefore + * node. Silent provenance corruption. An under-specified merge is therefore * returned **pending** (`resolution: null`, no commit): it stays in the queue for * a later, better-specified pass rather than mis-routing. Pure: no I/O. * - * @ref LLP 0028#committed-only-projection [implements] + * @ref LLP 0028#committed-only-projection [implements]: * * @param {Record} prospect * @param {{ type: string, label: string, summary: string, confidence: number | undefined }} view @@ -587,7 +587,7 @@ export function routeDecision(prospect, view, decision, at) { const isMerge = decision.decision === 'merge' // A merge without its target key + type cannot be routed to the right - // content-addressed node — leave it pending rather than corrupt provenance. + // content-addressed node: leave it pending rather than corrupt provenance. if (isMerge && (!decision.merge_into || !decision.item_type)) { return { committed: null, rejected: false, merged: false, resolution: null } } diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/datasets.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/datasets.js index dd3c9c35..611aeb56 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/datasets.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/datasets.js @@ -115,7 +115,7 @@ export function columnsFor(dataset) { /** * Build the `DatasetRegistration` for an enrichment dataset so its rows are * queryable by name (the contract's SQL and the curate queue both depend on - * this). Generic over the three datasets — same discovery/data-source shape + * this). Generic over the three datasets: same discovery/data-source shape * the context-graph plugin uses for node/edge. * * @param {string} dataset @@ -197,7 +197,7 @@ async function createDataSource(partitions, ctx, dataset) { * lets propose filter against already-persisted ids for cross-tick idempotency * (see propose.js `filterNewProspects`). * - * @ref LLP 0028#idempotent-prospects [implements] + * @ref LLP 0028#idempotent-prospects [implements]: * * @param {{ extractor: string, extractorVersion: number, anchorKey: string, candidateKey: string }} parts * @returns {string} diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/index.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/index.js index 6d7d88a8..369a7821 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/index.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/index.js @@ -22,7 +22,7 @@ const PLUGIN_NAME = '@hypaware/context-graph-enrich' * committed-only projection contract with the graph; and starts the T1 * propose and T2 curate daemon sources. * - * The prospect lifecycle lives in this plugin's datasets — only committed + * The prospect lifecycle lives in this plugin's datasets. Only committed * knowledge is projected, so a rejected prospect never reaches the graph. * Projection itself runs via `hyp graph project` (the connector pattern): * `projectGraph` is internal to the graph plugin, so this plugin contributes @@ -40,13 +40,13 @@ export async function activate(ctx) { const validated = validateEnrichConfig(ctx.config) if (!validated.ok) { const detail = validated.errors.map((e) => `${e.pointer || '/'}: ${e.message}`).join('; ') - const err = /** @type {HypError} */ (new Error(`${PLUGIN_NAME}: invalid config — ${detail}`)) + const err = /** @type {HypError} */ (new Error(`${PLUGIN_NAME}: invalid config - ${detail}`)) err.hypErrorKind = 'enrich_config_invalid' throw err } const config = validated.config - // Resolve only the graph capability eagerly — registerContract + the kit + // Resolve only the graph capability eagerly: registerContract + the kit // are needed now, and context-graph is ordered before this plugin. The // vector-search + completion capabilities are resolved lazily on first // tick/command (see runtime.getVector/getCompletion): the resolver orders diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/prompts.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/prompts.js index 9a17cca8..8382b126 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/prompts.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/prompts.js @@ -2,8 +2,8 @@ /** * Model I/O for the two tiers. Both use a forced tool so the model returns - * schema-shaped structured output (a `tool_use` block) rather than prose — - * the structured-extraction channel of `hypaware.completion`. + * schema-shaped structured output (a `tool_use` block) rather than prose. + * This uses the structured-extraction channel of `hypaware.completion`. * * @import { CompletionRequest, CompletionResult } from '../../../../collectivus-plugin-kernel-types.d.ts' * @import { CurateDecision } from './types.d.ts' @@ -28,7 +28,7 @@ const EMIT_PROSPECTS_TOOL = { type: { type: 'string', enum: [...PROSPECT_TYPES] }, label: { type: 'string', description: 'A short canonical name for the item (used as its key).' }, summary: { type: 'string', description: 'One sentence stating the item.' }, - confidence: { type: 'number', description: '0..1 — your confidence this is a real, useful item.' }, + confidence: { type: 'number', description: '0..1 - your confidence this is a real, useful item.' }, evidence: { type: 'string', description: 'A short quote from the text supporting it.' }, }, required: ['type', 'label'], @@ -41,7 +41,7 @@ const EMIT_PROSPECTS_TOOL = { const T1_SYSTEM = 'You extract candidate knowledge (decisions, concepts, facts, constraints, open questions) from a work session transcript. ' + - 'OVER-PROPOSE: favor recall over precision — a later curation step prunes. Emit many small, specific candidates rather than few broad ones. ' + + 'OVER-PROPOSE: favor recall over precision - a later curation step prunes. Emit many small, specific candidates rather than few broad ones. ' + 'Each candidate gets a short canonical `label` (its key), a one-sentence `summary`, a `confidence` in 0..1, and a supporting `evidence` quote.' const CURATE_DECISIONS_TOOL = { @@ -64,7 +64,7 @@ const CURATE_DECISIONS_TOOL = { item_key: { type: 'string', description: 'Final canonical key (for commit/deepen). Reuse an existing key to converge.' }, label: { type: 'string', description: 'Final human-readable label.' }, summary: { type: 'string', description: 'Final one-sentence statement of the item.' }, - confidence: { type: 'number', description: '0..1 — final confidence after curation.' }, + confidence: { type: 'number', description: '0..1 - final confidence after curation.' }, merge_into: { type: 'string', description: 'Existing item key to merge into (for merge). Pair with item_type so the merged node matches.' }, note: { type: 'string', description: 'Short rationale.' }, }, @@ -80,9 +80,9 @@ const T2_SYSTEM = 'You are a graph librarian curating proposed knowledge for a context graph. ' + 'You are given a CLUSTER of related prospects (which may come from DIFFERENT work sessions), plus the shared recalled ' + 'committed knowledge, per-prospect similar existing items, and the shared source excerpt. For EACH prospect choose exactly one action: ' + - '`commit` (real and new — give a final type, canonical key, label, summary, confidence), ' + - '`merge` (duplicates another prospect in this cluster or an existing committed item — give `merge_into` AND `item_type` so the node converges), ' + - '`deepen` (real but should be corrected/enriched — give the improved fields), or ' + + '`commit` (real and new - give a final type, canonical key, label, summary, confidence), ' + + '`merge` (duplicates another prospect in this cluster or an existing committed item - give `merge_into` AND `item_type` so the node converges), ' + + '`deepen` (real but should be corrected/enriched - give the improved fields), or ' + '`reject` (noise, trivial, or wrong). When two prospects in the cluster are the same concept, commit one and `merge` the others into its key, ' + 'so cross-session duplicates collapse to one node. Prefer reusing existing keys so the same concept converges. ' + 'Respond by calling the `curate_decisions` tool exactly once, with one entry per prospect referenced by its `index`.' @@ -109,7 +109,7 @@ export function buildProposeRequest({ text, model, maxTokens, maxCandidates }) { * Build ONE curate request for a **similarity/recall cluster** of prospects * (possibly spanning sessions). The shared recalled committed knowledge and the * source excerpt are read once and shared across the cluster, so they aren't - * re-sent per prospect — the cost win — and the curator can merge cross-session + * re-sent per prospect: the cost win. The curator can merge cross-session * duplicates in one call. The model returns one decision per prospect, keyed by * 1-based index. (`neighborhood` carries the content-based shared recalled * knowledge; the field name is kept for call-site stability.) @@ -136,7 +136,7 @@ export function buildCurateBatchRequest({ prospects, neighborhood, source, model const user = `SHARED RECALLED KNOWLEDGE (committed items related to this cluster)\n${neighborhood || '(none)'}\n\n` + `SHARED SOURCE EXCERPT\n${source || '(unavailable)'}\n\n` + - `PROSPECTS — decide on each by index:\n${lines}\n` + `PROSPECTS - decide on each by index:\n${lines}\n` /** @type {CompletionRequest} */ const req = { model, diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js index 994c34fa..4920f6be 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/propose.js @@ -20,18 +20,18 @@ const EXTRACTOR_VERSION = 1 * Run one T1 propose tick over **whole sessions**. The two regimes differ only * in their session selector ([§two-regimes](LLP 0028)): * - * - `ongoing` (default) — settled, not-yet-enriched sessions: latest part older + * - `ongoing` (default): settled, not-yet-enriched sessions. Latest part older * than `settle_cutoff_minutes` AND past the session's watermark, capped at * `max_sessions_per_tick`. - * - `backfill` — every session, ignoring the settle cutoff and the watermark. + * - `backfill`: every session, ignoring the settle cutoff and the watermark. * * Each selected session's filtered parts are stitched in DAG order, `tool_result` - * excluded, and passed to a **single** frontier-model call — closing the old + * excluded, and passed to a **single** frontier-model call, closing the old * 12k-char truncation defect. Prospects are deduped by a deterministic * {@link prospectId}, pre-write-filtered against the persisted set (idempotent * across ticks/regimes), appended, and the session's watermark is advanced. * - * @ref LLP 0028#two-tiers-one-pipeline [implements] + * @ref LLP 0028#two-tiers-one-pipeline [implements]: * * @param {EnrichRuntime} runtime * @param {{ regime?: 'ongoing' | 'backfill', sessionIds?: string[], deadlineMs?: number, nowMs?: number, signal?: AbortSignal }} [opts] @@ -54,7 +54,7 @@ export async function runProposeTick(runtime, opts = {}) { // Per session: read its filtered parts, DAG-order them, extract in full. // Track which sessions advanced so the watermark only moves over the // sessions actually processed (an early deadline break must not skip the - // rest — they re-qualify next tick). + // rest: they re-qualify next tick). /** @type {Array<{ anchorKey: string, keys: string[], candidates: ReturnType }>} */ const perSession = [] /** @type {Record} */ @@ -68,8 +68,8 @@ export async function runProposeTick(runtime, opts = {}) { const mark = sessionMark(ordered, cfg) // Re-qualify against this read's own parts: the selector already compared // the exact tuple, but its aggregate and this parts query are separate - // reads — a part landing between them could make a just-selected session - // already-covered (TOCTOU). Backfill re-extracts unconditionally — its + // reads. A part landing between them could make a just-selected session + // already-covered (TOCTOU). Backfill re-extracts unconditionally: its // appends are idempotent ({@link filterNewProspects}). if (regime === 'ongoing') { const prev = marks[sid] @@ -99,7 +99,7 @@ export async function runProposeTick(runtime, opts = {}) { } // Persist marks only AFTER the prospects are appended: a crash in between - // re-reads the same sessions next tick (safe — idempotent), whereas + // re-reads the same sessions next tick (safe: idempotent), whereas // marking first then crashing would lose a session's prospects forever. const advanced = Object.keys(newMarks) if (advanced.length > 0) { @@ -128,24 +128,24 @@ export async function runProposeTick(runtime, opts = {}) { /** * Select the sessions a tick should extract. One aggregate query * ({@link buildSessionAggregateQuery}) returns the **precise latest part tuple** - * `(last_ts, last_id)` per session — one row per session — and the regime filters + * `(last_ts, last_id)` per session. One row per session, and the regime filters * it in JS: * - * - `backfill` — every session with extractable content. - * - `ongoing` — only **settled** sessions (latest part older than + * - `backfill`: every session with extractable content. + * - `ongoing`: only **settled** sessions (latest part older than * `settle_cutoff_minutes`) whose latest part is strictly past the stored * watermark, oldest-settled first and capped at `max_sessions_per_tick`. * * The exclusion compares the **full `(ts, tiebreak)` tuple** against the mark * ({@link cmpMark}), not the timestamp alone: parts of one message share a * wall-clock millisecond, so a same-`ts` part that advanced the session past its - * mark (higher tiebreak) must re-qualify — a timestamp-only check would silently + * mark (higher tiebreak) must re-qualify. A timestamp-only check would silently * drop its text. The tuple match is also why an already-enriched session * (`cmpMark == 0`) is excluded rather than re-selected every tick (no * cap-flooding). {@link runProposeTick} keeps a precise re-check as a TOCTOU * guard against rows landing between the two queries. * - * @ref LLP 0028#two-regimes [implements] + * @ref LLP 0028#two-regimes [implements]: * * @param {EnrichRuntime} runtime * @param {{ regime: 'ongoing' | 'backfill', nowMs: number, marks: Record }} args @@ -181,14 +181,14 @@ export async function selectSessions(runtime, { regime, nowMs, marks }) { /** * The per-session selector query: one row per session carrying the **precise - * latest part tuple** `(last_ts, last_id)` — the timestamp *and* tiebreak of the + * latest part tuple** `(last_ts, last_id)`. The timestamp *and* tiebreak of the * session's latest kept part, so the selector can compare the full mark, not the * timestamp alone ({@link selectSessions}). A plain `MAX(ts)` aggregate can't do * this: the tiebreak that wins at the max timestamp is not the global `MAX` * tiebreak, so we rank parts with `ROW_NUMBER() OVER (… ORDER BY ts DESC, * tiebreak DESC)` and keep `rn = 1`. The shared {@link contentFilterClauses} are * applied in the inner scan so a session that is *only* plumbing never appears - * and the tuple reflects the latest part the proposer would actually read — + * and the tuple reflects the latest part the proposer would actually read, * matching {@link sessionMark}, which is computed over the same filtered parts. * * @param {EnrichConfig} cfg @@ -210,7 +210,7 @@ export function buildSessionAggregateQuery(cfg) { } /** - * Read **all** filtered parts of one session — the full transcript, no row + * Read **all** filtered parts of one session. The full transcript, no row * budget and no truncation (that was the defect). The shared content filter * keeps the proposer on signal, and the anchor value is `sqlQuote`'d (the only * interpolated value; column names are validated identifiers). Ordering is done @@ -230,13 +230,13 @@ export function buildSessionPartsQuery(cfg, sessionId) { /** * Order a session's parts into one coherent transcript. The gateway assigns * `message_created_at` in logical message order, so sorting by (timestamp, - * row-unique tiebreak) reconstructs the conversation — the "DAG order" the - * design calls for — without coupling to `ai_gateway_messages`-specific columns + * row-unique tiebreak) reconstructs the conversation. The "DAG order" the + * design calls for. Without coupling to `ai_gateway_messages`-specific columns * (`message_index` / `agent_id`), which a custom source may lack. Deterministic, * so re-runs over the same session yield the same transcript (hence the same * prospect ids). * - * @ref LLP 0028#two-tiers-one-pipeline [implements] + * @ref LLP 0028#two-tiers-one-pipeline [implements]: * * @param {Record[]} rows * @param {EnrichConfig} cfg @@ -302,7 +302,7 @@ function t1MaxTokens(p) { /** * Dedup proposed candidates into prospect rows keyed by a deterministic - * {@link prospectId} — the same (extractor, version, anchor, type+label) + * {@link prospectId}. The same (extractor, version, anchor, type+label) * collapses to one row, so re-proposing the same content never duplicates. * * @param {Array<{ anchorKey: string, keys: string[], candidates: ReturnType }>} perSession @@ -350,7 +350,7 @@ export function collectProspectRows(perSession, cfg, createdAt) { * nothing new. Mirrors the graph projector's pre-write dedup (read the committed * id set, filter before append; only a missing dataset is benign there). * - * @ref LLP 0028#idempotent-prospects [implements] + * @ref LLP 0028#idempotent-prospects [implements]: * * @param {EnrichRuntime} runtime * @param {Record[]} candidates diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/runtime.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/runtime.js index bc17f8ff..a39c0323 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/runtime.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/runtime.js @@ -23,7 +23,7 @@ export function setEnrichRuntime(value) { /** @returns {EnrichRuntime} */ export function requireEnrichRuntime() { if (!runtime) { - throw new Error('@hypaware/context-graph-enrich: not activated yet — runtime singleton is empty') + throw new Error('@hypaware/context-graph-enrich: not activated yet - runtime singleton is empty') } return runtime } @@ -58,7 +58,7 @@ export function getVector(rt) { /** * Lazily resolve + cache the embedder capability. Used only by the T2 * cold-remainder clustering ([§curate-clustering](LLP 0028)): prospects that - * recall nothing are clustered by their own embeddings. Resolved best-effort — + * recall nothing are clustered by their own embeddings. Resolved best-effort: * an embedder provider is already present transitively (vector-search requires * one), but if it isn't, the caller falls back to session grouping rather than * failing the tick. Throws (caught by the caller) when no provider is installed. diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/sql.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/sql.js index 28f26a40..da67c059 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/sql.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/sql.js @@ -11,13 +11,13 @@ import { executeQuerySql } from '../../../../src/core/query/sql.js' * * Missing-dataset tolerance is **opt-in** via `allowMissing`, mirroring the * graph projector's pre-write dedup (LLP 0023#pre-write-dedup): a not-yet-written - * plugin-owned enrichment table — or the published `node`/`edge` surface before - * its first projection — is benign, so those callers pass `allowMissing: true` + * plugin-owned enrichment table (or the published `node`/`edge` surface before + * its first projection) is benign, so those callers pass `allowMissing: true` * and get `[]`. The configured *source* dataset is read **fail-fast** (the * default): a missing or misspelled `source_dataset` must surface as an * actionable error, never silently make `enrich propose` a no-op forever. * - * @ref LLP 0028#operability — tolerate own tables, fail-fast on the source. + * @ref LLP 0028#operability: tolerate own tables, fail-fast on the source. * * @param {EnrichRuntime} runtime * @param {string} query @@ -87,13 +87,13 @@ export function sqlQuote(v) { * `require_text` drops rows whose text column is null/empty: they contribute * nothing to the model yet consume the per-tick row budget, and they include * the signature-only thinking/reasoning parts a proxy does not persist. - * `exclude_part_types` drops whole part kinds — default `tool_result`, i.e. raw + * `exclude_part_types` drops whole part kinds: default `tool_result`, i.e. raw * tool/file/command output, the bulk of the corpus but not durable knowledge. * * The column names are validated SQL identifiers and the part-type values are * `sqlQuote`'d literals, so this introduces no injection surface. * - * @ref LLP 0028#row-selection — enrichment scans signal, not plumbing. + * @ref LLP 0028#row-selection: enrichment scans signal, not plumbing. * * @param {EnrichConfig} cfg * @returns {string[]} diff --git a/hypaware-core/plugins-workspace/context-graph-enrich/src/state.js b/hypaware-core/plugins-workspace/context-graph-enrich/src/state.js index 8ecba93c..ad7ba6bc 100644 --- a/hypaware-core/plugins-workspace/context-graph-enrich/src/state.js +++ b/hypaware-core/plugins-workspace/context-graph-enrich/src/state.js @@ -7,14 +7,14 @@ import { randomUUID } from 'node:crypto' /** * Per-host watermark state for the enrichment proposer, persisted as a single * sidecar JSON under the plugin's state dir (the same approach vector-search - * uses for shard metadata). The watermark is a **per-session high-water mark** — + * uses for shard metadata). The watermark is a **per-session high-water mark**: * one (timestamp, row-unique id) tuple per session, "this session has been - * enriched through here" — which replaces the original single global keyset + * enriched through here", which replaces the original single global keyset * cursor. Backfill seeds the marks, the ongoing batch advances them, and a * resumed session re-qualifies when its latest part moves past its mark. Curate - * has no mark — its queue is "prospects with no resolution", computed by query. + * has no mark: its queue is "prospects with no resolution", computed by query. * - * @ref LLP 0028#per-session-watermark [implements] + * @ref LLP 0028#per-session-watermark [implements]: * * @import { CurateJob, EnrichStateFile, SessionMark } from './types.d.ts' */ @@ -34,7 +34,7 @@ export function readState(stateDir) { return { schema_version: SCHEMA_VERSION, session_marks: readMarks(parsed.session_marks), curate_job: readCurateJob(parsed.curate_job) } } } catch { - // Missing, malformed, or an older schema — start from the beginning. An + // Missing, malformed, or an older schema: start from the beginning. An // older sidecar carries no per-session marks or job, so it is discarded // rather than migrated (a fresh ongoing run re-settles every session). } @@ -60,7 +60,7 @@ function readCurateJob(value) { } } // A job persisted before `source` existed (or with a junk value) is read as - // `daemon` — the original owner, so legacy in-flight jobs keep being collected + // `daemon`: the original owner, so legacy in-flight jobs keep being collected // by the daemon and a backfill correctly refuses to clobber them. const source = j.source === 'backfill' ? 'backfill' : 'daemon' return { id: j.id, submitted_at: typeof j.submitted_at === 'string' ? j.submitted_at : '', source, clusters } @@ -119,12 +119,12 @@ export function writeState(stateDir, state) { * owns a disjoint field (propose advances `session_marks`, curate owns * `curate_job`). Both have long `await` windows (frontier-model calls, batch * submit/poll); a writer that captured a snapshot *before* its await and wrote it - * *after* would clobber the field the other source advanced in between — a lost + * *after* would clobber the field the other source advanced in between: a lost * update that orphans a submitted batch (results never collected) and lets the * next tick double-submit. Every mutation of the sidecar therefore goes through * here, merging into the latest on-disk state. * - * @ref LLP 0028#two-regimes [constrained-by] + * @ref LLP 0028#two-regimes [constrained-by]: * * @param {string} stateDir * @param {(current: EnrichStateFile) => EnrichStateFile} mutate diff --git a/hypaware-core/plugins-workspace/context-graph/src/command.js b/hypaware-core/plugins-workspace/context-graph/src/command.js index a29ed3ae..3a8f2ad3 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/command.js +++ b/hypaware-core/plugins-workspace/context-graph/src/command.js @@ -10,7 +10,7 @@ import { requireGraphRuntime } from './runtime.js' */ /** - * `hyp graph project` — run the T0 projection over every registered source + * `hyp graph project` - run the T0 projection over every registered source * contract (optionally filtered to one source with `--source `). * * @param {string[]} argv @@ -34,7 +34,7 @@ export async function runGraphProject(argv, ctx) { ctx.stdout.write( source ? `graph project: no contract registered for source '${source}'\n` - : 'graph project: no contracts registered — install a source connector (e.g. @hypaware/ai-gateway-graph)\n' + : 'graph project: no contracts registered - install a source connector (e.g. @hypaware/ai-gateway-graph)\n' ) return 0 } @@ -50,7 +50,7 @@ export async function runGraphProject(argv, ctx) { ctx.stdout.write(`graph project (dry-run): ${r.nodes} node(s), ${r.edges} edge(s) would be projected\n`) } else { ctx.stdout.write( - `graph project: ${r.nodes} node(s), ${r.edges} edge(s) — wrote ${r.nodesWritten} new node(s), ${r.edgesWritten} new edge(s)\n` + `graph project: ${r.nodes} node(s), ${r.edges} edge(s) - wrote ${r.nodesWritten} new node(s), ${r.edgesWritten} new edge(s)\n` ) } return 0 @@ -64,7 +64,7 @@ export async function runGraphProject(argv, ctx) { * Parse `graph project` argv: flags only, no positional. `--source` takes a * value (the source dataset to filter to, `--source ` or `--source=`); * `--dry-run` is boolean. A missing, empty, or flag-shaped `--source` value is - * a usage error — so a malformed targeted + * a usage error, so a malformed targeted * command can't silently fall back to projecting *all* contracts, a broader * write than was asked for. Unknown flags and stray positionals are rejected * for the same reason. @@ -103,7 +103,7 @@ function parseProjectArgv(argv) { } /** - * `hyp graph compact` — merge duplicate node/edge rows and rewrite + * `hyp graph compact` - merge duplicate node/edge rows and rewrite * affected partitions into sorted replacement tables. * * @param {string[]} argv @@ -120,11 +120,11 @@ export async function runGraphCompact(argv, ctx) { for (const d of r.datasets) { if (dryRun) { ctx.stdout.write( - `graph compact (dry-run): ${d.dataset} — ${d.duplicateIds} duplicate id(s) across ${d.partitionsRewritten} partition(s) would be merged\n` + `graph compact (dry-run): ${d.dataset} - ${d.duplicateIds} duplicate id(s) across ${d.partitionsRewritten} partition(s) would be merged\n` ) } else { ctx.stdout.write( - `graph compact: ${d.dataset} — merged ${d.rowsMerged} duplicate row(s) (${d.duplicateIds} id(s)), rewrote ${d.partitionsRewritten} partition(s)\n` + `graph compact: ${d.dataset} - merged ${d.rowsMerged} duplicate row(s) (${d.duplicateIds} id(s)), rewrote ${d.partitionsRewritten} partition(s)\n` ) } for (const skip of d.partitionsSkipped) { @@ -132,8 +132,8 @@ export async function runGraphCompact(argv, ctx) { } } // A concurrent-write skip is a benign retry-later; an unreadable - // cursor needs operator attention — exit nonzero so it can't pass - // silently in scripts. + // cursor needs operator attention, so exit nonzero to prevent it from + // passing silently in scripts. const unreadable = r.datasets.some((d) => d.partitionsSkipped.some((s) => s.reason === 'unreadable-cursor')) return unreadable ? 1 : 0 } catch (err) { diff --git a/hypaware-core/plugins-workspace/context-graph/src/contract-kit.js b/hypaware-core/plugins-workspace/context-graph/src/contract-kit.js index 2c02bf26..a565438f 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/contract-kit.js +++ b/hypaware-core/plugins-workspace/context-graph/src/contract-kit.js @@ -13,7 +13,7 @@ export { nodeId, edgeId } * * The id recipe (`ids.js`) and the provenance column shape live here, in the * graph plugin. A contract author receives `buildNode`/`buildEdge` through the - * `hypaware.context-graph` capability and never reimplements either — so no + * `hypaware.context-graph` capability and never reimplements either, so no * source can fork the id recipe (which would orphan every committed row, see * `ids.js`) or drift the provenance columns. The source supplies only the * per-row semantics (`type`, natural `key`, `props`, `sourceKeys`); the kit @@ -21,7 +21,7 @@ export { nodeId, edgeId } * * @param {{ sourceDataset: string, projector: string, projectorVersion: number }} meta * @returns {{ buildNode: (spec: NodeSpec) => Record, buildEdge: (spec: EdgeSpec) => Record }} - * @ref LLP 0023#contract-contribution [implements] — central id+provenance kit; sources own only their rules + * @ref LLP 0023#contract-contribution [implements]: central id+provenance kit; sources own only their rules */ export function makeRowBuilders({ sourceDataset, projector, projectorVersion }) { /** diff --git a/hypaware-core/plugins-workspace/context-graph/src/contract-registry.js b/hypaware-core/plugins-workspace/context-graph/src/contract-registry.js index c887e4db..8ed74c7b 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/contract-registry.js +++ b/hypaware-core/plugins-workspace/context-graph/src/contract-registry.js @@ -8,14 +8,14 @@ /** * Registry of projection contracts contributed by source plugins through the * `hypaware.context-graph` capability. The `graph project` command reads - * `list()` and the engine runs every registered contract — so adding a source + * `list()` and the engine runs every registered contract, so adding a source * is contributing a contract here, never editing the engine. * * Lives in the plugin (not the kernel): graph projection is a plugin concern, * not a core capture concern (hypaware LLP 0003 core-vs-plugin minimalism). * * @param {{ log?: PluginLogger }} [opts] - * @ref LLP 0023#contract-contribution [implements] — one registry the engine iterates; sources contribute via the capability + * @ref LLP 0023#contract-contribution [implements]: one registry the engine iterates; sources contribute via the capability */ export function createContractRegistry(opts = {}) { const log = opts.log diff --git a/hypaware-core/plugins-workspace/context-graph/src/datasets.js b/hypaware-core/plugins-workspace/context-graph/src/datasets.js index 75441eb9..111d38e2 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/datasets.js +++ b/hypaware-core/plugins-workspace/context-graph/src/datasets.js @@ -21,11 +21,11 @@ export const EDGE_DATASET = 'edge' /** * `node` columns. Provenance is carried inline (source_dataset / source_keys / - * projector / projector_version) — a v1 simplification of the separate + * projector / projector_version), a v1 simplification of the separate * provenance-table design. * * @type {ReadonlyArray} - * @ref LLP 0023#inline-provenance — first sighting's keys as exemplar; a join table waits for a consumer that needs full lineage + * @ref LLP 0023#inline-provenance: first sighting's keys as exemplar; a join table waits for a consumer that needs full lineage */ export const NODE_COLUMNS = Object.freeze([ { name: 'node_id', type: 'STRING', nullable: false }, diff --git a/hypaware-core/plugins-workspace/context-graph/src/ids.js b/hypaware-core/plugins-workspace/context-graph/src/ids.js index 1506337d..9be99114 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/ids.js +++ b/hypaware-core/plugins-workspace/context-graph/src/ids.js @@ -11,7 +11,7 @@ import { createHash } from 'node:crypto' * * Hash-input segments are joined with NUL (`\0`, written as the * two-character escape so this file stays plain text): NUL cannot appear - * in the JS strings fed in here, so the join is collision-free — no + * in the JS strings fed in here, so the join is collision-free, no * (type, key) pair can be confused with another by crafting keys that * contain the delimiter. Changing the delimiter changes every id, which * would orphan all committed graph rows; test/plugins/context-graph-ids.test.js @@ -19,7 +19,7 @@ import { createHash } from 'node:crypto' * * @param {string} value * @returns {string} - * @ref LLP 0023#content-addressed-ids [implements] — changing the recipe orphans every committed graph row + * @ref LLP 0023#content-addressed-ids [implements]: changing the recipe orphans every committed graph row */ function sha(value) { return createHash('sha256').update(value).digest('hex').slice(0, 24) diff --git a/hypaware-core/plugins-workspace/context-graph/src/index.js b/hypaware-core/plugins-workspace/context-graph/src/index.js index fa47d6c3..e12ac4ac 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/index.js +++ b/hypaware-core/plugins-workspace/context-graph/src/index.js @@ -27,20 +27,20 @@ const CAPABILITY_VERSION = '1.0.0' * Activate `@hypaware/context-graph`. * * Registers: - * - capability `hypaware.context-graph@1.0.0` — source plugins (or a + * - capability `hypaware.context-graph@1.0.0` - source plugins (or a * connector like `@hypaware/ai-gateway-graph`) call `registerContract` to * contribute a projection contract, and build its rows with the shared * `kit` (id recipe + provenance). The engine runs every registered contract. - * - dataset `node` and dataset `edge` — derived graph tables, fronted by + * - dataset `node` and dataset `edge` - derived graph tables, fronted by * the kernel-managed Iceberg cache (populated by the projection command, * not by a live source) - * - command `graph project` — runs the T0 deterministic projection over + * - command `graph project` - runs the T0 deterministic projection over * every registered source contract - * - command `graph compact` — merges duplicate node/edge rows and + * - command `graph compact` - merges duplicate node/edge rows and * rewrites affected partitions into sorted tables - * - command `graph neighbors` — walks the activity graph from a seed node out + * - command `graph neighbors` - walks the activity graph from a seed node out * to N hops, reading the published node/edge datasets ([LLP 0026]) - * - skill `hypaware-graph` — teaches AI clients (Claude, Codex) how to project + * - skill `hypaware-graph` - teaches AI clients (Claude, Codex) how to project * and query the graph; installed by `hyp skills install` when this plugin is * active * @@ -48,7 +48,7 @@ const CAPABILITY_VERSION = '1.0.0' * snapshot/commit hook exists, and eventual freshness is acceptable). * * @param {PluginActivationContext} ctx - * @ref LLP 0023#on-demand-projection [implements] — command-only projection keeps the plugin out of the daemon loop + * @ref LLP 0023#on-demand-projection [implements]: command-only projection keeps the plugin out of the daemon loop */ export async function activate(ctx) { // The contract registry source plugins contribute into, exposed via the diff --git a/hypaware-core/plugins-workspace/context-graph/src/maintenance.js b/hypaware-core/plugins-workspace/context-graph/src/maintenance.js index 20f5a972..dac9beee 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/maintenance.js +++ b/hypaware-core/plugins-workspace/context-graph/src/maintenance.js @@ -53,7 +53,7 @@ const GRAPH_DATASETS = [NODE_DATASET, EDGE_DATASET] * (same content-addressed id) across all committed partitions, and * rewrite affected partitions into sorted replacement tables. * - * Duplicates are rare — projection dedups pre-write — but concurrent + * Duplicates are rare (projection dedups pre-write), but concurrent * projections or partial failures can land the same id twice, possibly * in different `source=` partitions. Each duplicate group folds into a * single row (earliest `first_seen`, unioned props, via the same @@ -65,7 +65,7 @@ const GRAPH_DATASETS = [NODE_DATASET, EDGE_DATASET] * * @param {{ storage: ExtendedQueryStorageService, dryRun?: boolean }} args * @returns {Promise<{ datasets: { dataset: string, duplicateIds: number, rowsMerged: number, partitionsRewritten: number, partitionsSkipped: SkippedPartition[] }[] }>} - * @ref LLP 0023#graph-compaction [implements] — graph semantics only; file-count/snapshot compaction stays with the kernel + * @ref LLP 0023#graph-compaction [implements]: graph semantics only; file-count/snapshot compaction stays with the kernel */ export async function compactGraphTables({ storage, dryRun = false }) { return withSpan( @@ -109,7 +109,7 @@ async function compactGraphDataset({ storage, dataset, dryRun }) { // read here is positive (tryReadCursorSync) and remembered per partition: // the eventual generation swap is conditional on the cursor still // matching it, and a partition whose cursor cannot be positively read is - // never rewritten — a corrupt cursor.json must not be mistaken for the + // never rewritten. A corrupt cursor.json must not be mistaken for the // epoch-0 default when the old generation is about to be retired. /** @type {Map }>} */ const occurrences = new Map() @@ -195,7 +195,7 @@ async function compactGraphDataset({ storage, dataset, dryRun }) { mergedByPart.set(home, rows) } - // Pass 2b: rewrite each affected partition — duplicate rows dropped, + // Pass 2b: rewrite each affected partition (duplicate rows dropped): // this partition's merged rows appended, sorted replacement table. // // Home partitions (the ones that receive merged rows) go first, and a @@ -264,14 +264,14 @@ async function compactGraphDataset({ storage, dataset, dryRun }) { * The swap is conditional: writers (`appendRowsToSourceTable`) keep * appending to the old generation while it is being scanned, so before * repointing, the cursor is re-read and compared against the one the - * compaction scan started from. Any change — a bumped rowCount from a + * compaction scan started from. Any change (a bumped rowCount from a * concurrent append, a different tableDir from another compactor, or a - * cursor that can no longer be positively read — aborts the swap: the + * cursor that can no longer be positively read) aborts the swap: the * staged replacement table is removed and the partition is reported * skipped, because retiring the old generation at that point would lose * the rows appended during the rewrite window. (A small write window * between the re-read and the cursor write remains; closing it needs a - * partition-level lock, which graph compaction doesn't take — reruns are + * partition-level lock, which graph compaction doesn't take. Reruns are * cheap and duplicates are benign.) * * @param {{ @@ -284,7 +284,7 @@ async function compactGraphDataset({ storage, dataset, dryRun }) { * sortOrder: readonly { column: string, direction?: 'asc' | 'desc' }[] * }} args * @returns {Promise<{ status: 'rewritten', dropped: number } | { status: 'skipped', reason: 'unreadable-cursor' | 'concurrent-write' }>} - * @ref LLP 0023#graph-compaction [constrained-by] — conditional swap: on any cursor change, skip and report; never retire + * @ref LLP 0023#graph-compaction [constrained-by]: conditional swap: on any cursor change, skip and report; never retire */ export async function rewritePartition({ partitionDir, idCol, dropIds, extraRows, expectedCursor, fallbackColumns, sortOrder }) { const oldTableDirName = expectedCursor.tableDir ?? 'table' @@ -395,7 +395,7 @@ async function removeStagedTable(tableDir) { /** * Deterministic duplicate ordering: earliest `first_seen` first (the - * canonical row), then partition path, then the kernel row id — so the + * canonical row), then partition path, then the kernel row id: so the * fold result does not depend on scan order. * * @param {{ row: GraphRow, part: string }} a diff --git a/hypaware-core/plugins-workspace/context-graph/src/project.js b/hypaware-core/plugins-workspace/context-graph/src/project.js index 2067d742..2907a0dc 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/project.js +++ b/hypaware-core/plugins-workspace/context-graph/src/project.js @@ -28,7 +28,7 @@ import { * * @param {{ query: QueryRegistry, storage: ExtendedQueryStorageService, contracts: Contract[], config?: HypAwareV2Config, dryRun?: boolean }} args * @returns {Promise<{ nodes: number, edges: number, nodesWritten: number, edgesWritten: number }>} - * @ref LLP 0023#contract-contribution [implements] — the engine runs every registered contract; adding a source is contributing one + * @ref LLP 0023#contract-contribution [implements]: the engine runs every registered contract; adding a source is contributing one */ export async function projectGraph({ query, storage, contracts, config, dryRun = false }) { return withSpan( @@ -104,7 +104,7 @@ export async function projectGraph({ query, storage, contracts, config, dryRun = } /** - * Filter out rows whose id is already committed in the dataset — the + * Filter out rows whose id is already committed in the dataset: the * pre-write dedup that keeps re-projection idempotent at query time. * Duplicates that slip past it (concurrent projections, partial * failures) are merged later by `compactGraphTables` (maintenance.js). @@ -116,7 +116,7 @@ export async function projectGraph({ query, storage, contracts, config, dryRun = * @param {ExtendedQueryStorageService} storage * @param {HypAwareV2Config | undefined} config * @returns {Promise} - * @ref LLP 0023#pre-write-dedup [implements] — only a missing dataset is benign; real failures abort instead of duplicating + * @ref LLP 0023#pre-write-dedup [implements]: only a missing dataset is benign; real failures abort instead of duplicating */ async function dedupExisting(rows, idCol, dataset, query, storage, config) { if (rows.length === 0) return rows @@ -171,14 +171,14 @@ const propsProvenance = new WeakMap() * Merge a duplicate row into the accumulated one: keep the earliest * `first_seen` and union props. On a props key conflict the value from * the earliest-seen row wins; equal (or unknown) times fall back to a - * value comparison — so the result is independent of merge order, which + * value comparison so the result is independent of merge order, which * matters because the projection's source SELECTs have no stable * ordering. Shared with the dedup compaction in maintenance.js so * projection-time and compaction-time merges agree. * * @param {GraphRow} existing * @param {GraphRow} incoming - * @ref LLP 0023#merge-policy [implements] — order-independent merge shared by projection and compaction + * @ref LLP 0023#merge-policy [implements]: order-independent merge shared by projection and compaction */ export function mergeRow(existing, incoming) { const existingTime = firstSeenTime(existing.first_seen) @@ -238,7 +238,7 @@ function stableJson(value) { /** * Epoch millis for a `first_seen` value. Projection-time rows carry ISO - * strings; rows scanned back from Iceberg carry `Date` objects — both + * strings; rows scanned back from Iceberg carry `Date` objects: both * must compare the same way. * * @param {unknown} value diff --git a/hypaware-core/plugins-workspace/context-graph/src/query.js b/hypaware-core/plugins-workspace/context-graph/src/query.js index 8c300f1e..65b3de1c 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/query.js +++ b/hypaware-core/plugins-workspace/context-graph/src/query.js @@ -12,7 +12,7 @@ import { EDGE_DATASET, NODE_DATASET } from './datasets.js' /** * Resolve a seed token to exactly one node, in tiers: exact `node_id`, then - * exact `natural_key`, then `label` — each optionally narrowed by `type`. The + * exact `natural_key`, then `label`, each optionally narrowed by `type`. The * tiers exist because content-addressed ids are unguessable, so a human seed is * almost always a natural key or label. Multiple matches is an ambiguity error * carrying the candidates, never a silent pick. @@ -21,7 +21,7 @@ import { EDGE_DATASET, NODE_DATASET } from './datasets.js' * @param {string} token * @param {string | undefined} type * @returns {{ ok: true, node: GraphNode } | TraversalErr} - * @ref LLP 0026#seed-resolution [implements] — node_id → natural_key → label, ambiguity lists candidates + * @ref LLP 0026#seed-resolution [implements]: node_id → natural_key → label, ambiguity lists candidates */ export function resolveSeed(nodes, token, type) { const ofType = (n) => !type || n.node_type === type @@ -35,7 +35,7 @@ export function resolveSeed(nodes, token, type) { if (matches.length > 1) { return { ok: false, - error: `ambiguous seed ${JSON.stringify(token)} — ${matches.length} nodes match by ${field}; narrow with --type or pass an exact node_id/natural_key`, + error: `ambiguous seed ${JSON.stringify(token)} - ${matches.length} nodes match by ${field}; narrow with --type or pass an exact node_id/natural_key`, candidates: matches, } } @@ -46,17 +46,17 @@ export function resolveSeed(nodes, token, type) { /** * Breadth-first walk from a seed to `depth` hops over in-memory node/edge - * arrays. Pure — no IO — so the traversal logic is unit-testable directly. + * arrays. Pure, no IO, so the traversal logic is unit-testable directly. * * `direction` 'out' follows src→dst, 'in' follows dst→src, 'both' follows * either (recording which way each neighbor was reached). A non-empty * `edgeTypes` restricts which edge types are traversable. The full reachable * set within `depth` is collected, then `limit` slices it in BFS order with - * `truncated`/`reachable` reporting the drop — never a silent cap. + * `truncated`/`reachable` reporting the drop: never a silent cap. * * @param {{ nodes: GraphNode[], edges: GraphEdge[], seed: string, depth?: number, edgeTypes?: string[], direction?: Direction, limit?: number, type?: string }} args * @returns {TraversalOk | TraversalErr} - * @ref LLP 0026#thin-in-memory-traversal [implements] — whole-graph-in-RAM BFS is the deliberate basic tier; persisted index is the deferred fast path + * @ref LLP 0026#thin-in-memory-traversal [implements]: whole-graph-in-RAM BFS is the deliberate basic tier; persisted index is the deferred fast path */ export function traverse({ nodes, edges, seed, depth = 1, edgeTypes = [], direction = 'both', limit = Infinity, type }) { const resolved = resolveSeed(nodes, seed, type) @@ -117,25 +117,25 @@ export function traverse({ nodes, edges, seed, depth = 1, edgeTypes = [], direct /** * Load the published `node`/`edge` datasets through the query surface and walk - * them. Reads only the registered datasets — never the projection's internals — + * them. Reads only the registered datasets, never the projection's internals, * so an alternate query path stays possible. * * @param {{ query: QueryRegistry, storage: ExtendedQueryStorageService, config?: HypAwareV2Config, seed: string, depth?: number, edgeTypes?: string[], direction?: Direction, limit?: number, type?: string }} args * @returns {Promise} - * @ref LLP 0026#query-reads-the-published-surface [implements] — reads node/edge via the registry, not project.js state + * @ref LLP 0026#query-reads-the-published-surface [implements]: reads node/edge via the registry, not project.js state */ export async function queryNeighbors({ query, storage, config, seed, depth, edgeTypes, direction, limit, type }) { const edgeRows = await loadRows(query, storage, config, `SELECT src_id, dst_id, edge_type FROM ${EDGE_DATASET}`) const nodeRows = await loadRows(query, storage, config, `SELECT node_id, node_type, natural_key, label FROM ${NODE_DATASET}`) // Fold by graph identity before handing clean arrays to the pure traversal. - // The published surface can carry pre-compaction duplicates — the same + // The published surface can carry pre-compaction duplicates: the same // content-addressed id committed twice by concurrent projections or a // partial failure. `hyp graph compact` merges them, but a read must not // depend on it having run: two physical copies of one node must resolve as // a single seed (not a false "ambiguous"), and a doubled edge must not be // walked twice. Node identity is `node_id`; edge identity is - // `(src_id, edge_type, dst_id)` — exactly the digest `edgeId()` hashes. + // `(src_id, edge_type, dst_id)`: exactly the digest `edgeId()` hashes. /** @type {Map} */ const nodeById = new Map() for (const r of nodeRows) { diff --git a/hypaware-core/plugins-workspace/context-graph/src/runtime.js b/hypaware-core/plugins-workspace/context-graph/src/runtime.js index af0a1e48..43557b44 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/runtime.js +++ b/hypaware-core/plugins-workspace/context-graph/src/runtime.js @@ -2,8 +2,8 @@ /** * Module-local runtime singleton for `@hypaware/context-graph`. Holds the - * contract registry created at `activate()` so the `graph project` command — - * which runs with a `CommandRunContext`, not the activation context — can read + * contract registry created at `activate()` so the `graph project` command, + * which runs with a `CommandRunContext`, not the activation context, can read * the contracts source plugins contributed through the capability without * rebuilding an activation context. Mirrors `@hypaware/ai-gateway`'s * `runtime.js` (saved-state-as-source-of-truth) convention. @@ -25,7 +25,7 @@ export function setGraphRuntime(value) { */ export function requireGraphRuntime() { if (!runtime) { - throw new Error('@hypaware/context-graph: not activated yet — runtime singleton is empty') + throw new Error('@hypaware/context-graph: not activated yet - runtime singleton is empty') } return runtime } diff --git a/hypaware-core/plugins-workspace/context-graph/src/verb.js b/hypaware-core/plugins-workspace/context-graph/src/verb.js index 257f7b5a..c08820c6 100644 --- a/hypaware-core/plugins-workspace/context-graph/src/verb.js +++ b/hypaware-core/plugins-workspace/context-graph/src/verb.js @@ -16,10 +16,10 @@ const LARGE_GRAPH = 500_000 * `graph neighbors` as a verb: one declaration projecting the CLI command * **and** the `graph_neighbors` MCP tool. The traversal core * (`queryNeighbors`, LLP 0026) is already pure; this is the thin glue the - * kernel owns once. Read-class — a query-scoped MCP client may call it. + * kernel owns once. Read-class: a query-scoped MCP client may call it. * * @type {VerbRegistration} - * @ref LLP 0034#verbs [implements] — context-graph registers a verb; `graph_neighbors` becomes a tool with zero core change + * @ref LLP 0034#verbs [implements]: context-graph registers a verb; `graph_neighbors` becomes a tool with zero core change */ export const graphNeighborsVerb = { name: 'graph neighbors', @@ -81,7 +81,7 @@ export const graphNeighborsVerb = { * * @param {TraversalOk & { depth: number, direction: Direction }} result * @returns {VerbRenderResult} - * @ref LLP 0026#honest-limits [implements] — flag truncation with the true total; nudge to the index path on a large in-memory load + * @ref LLP 0026#honest-limits [implements]: flag truncation with the true total; nudge to the index path on a large in-memory load */ function renderNeighbors(result) { const out = [`graph neighbors: ${display(result.seed)} (${result.seed.node_type}) [${shortId(result.seed.node_id)}]`] @@ -90,19 +90,19 @@ function renderNeighbors(result) { out.push(` (no ${dir}neighbors within ${result.depth} hop(s))`) return { stdout: out.join('\n') + '\n' } } - // Distinct nodes can share a display label — most often Files with the same + // Distinct nodes can share a display label, most often Files with the same // basename but different paths, a genuine distinction the graph preserves // rather than collapses. Rendered by label alone they look like duplicate // rows. Disambiguate only the colliders (≥2 neighbors with one display text) // so unique labels stay readable; `--json` already carries `natural_key`. - // @ref LLP 0026#query-reads-the-published-surface — same-basename Files are genuine distinct nodes, not duplicates to fold + // @ref LLP 0026#query-reads-the-published-surface: same-basename Files are genuine distinct nodes, not duplicates to fold const labelCounts = new Map() for (const n of result.neighbors) { const text = display(n.node) labelCounts.set(text, (labelCounts.get(text) ?? 0) + 1) } // A long `natural_key` is tail-truncated, so two colliders that share a long - // path suffix would still render identically — the same-row bug, surviving for + // path suffix would still render identically: the same-row bug, surviving for // deep paths. Count the disambiguated rows too; any that *still* collide fall // back to the full `node_id`: the graph's content-addressed primary key, so // distinct nodes have distinct ids. This is the one suffix that *guarantees* @@ -127,7 +127,7 @@ function renderNeighbors(result) { out.push(` ${n.hop} ${arrow.padEnd(18)} ${n.node.node_type.padEnd(8)} ${shown}`) } if (result.truncated) { - out.push(`${result.neighbors.length} of ${result.reachable} neighbor(s) within ${result.depth} hop(s) — truncated; raise --limit`) + out.push(`${result.neighbors.length} of ${result.reachable} neighbor(s) within ${result.depth} hop(s) - truncated; raise --limit`) } else { out.push(`${result.reachable} neighbor(s) within ${result.depth} hop(s)`) } diff --git a/hypaware-core/plugins-workspace/embedder-openai/src/client.js b/hypaware-core/plugins-workspace/embedder-openai/src/client.js index e4c9e5df..bb60a36d 100644 --- a/hypaware-core/plugins-workspace/embedder-openai/src/client.js +++ b/hypaware-core/plugins-workspace/embedder-openai/src/client.js @@ -16,7 +16,7 @@ const PLUGIN_NAME = '@hypaware/embedder-openai' * * Batches larger than `max_batch` are chunked into sequential requests; * the returned vectors are always aligned with the input order. There - * is deliberately no retry layer — callers (the vector-search refresh + * is deliberately no retry layer: callers (the vector-search refresh * tick, an interactive search) already re-run on their own cadence, so * a failed request surfaces immediately instead of stalling a tick. * @@ -96,7 +96,7 @@ export function createOpenAiEmbedder(opts) { { component: 'embedder' } ).catch((/** @type {unknown} */ err) => { const errorKind = /** @type {HypError} */ (err)?.hypErrorKind ?? 'embedder_failed' - // Text content and key material never reach logs — counts only. + // Text content and key material never reach logs: counts only. log.error('embedder.embed_failed', { [Attr.ERROR_KIND]: errorKind, embed_model: config.model, @@ -112,7 +112,7 @@ export function createOpenAiEmbedder(opts) { /** * One `POST /v1/embeddings` request. The API key resolves from the * environment at call time and is used only for the Authorization - * header — it is never logged, never thrown in a message, and never + * header: it is never logged, never thrown in a message, and never * stored on the embedder. When the configured env var is unset the * request goes out without an Authorization header so localhost * servers (Ollama, LM Studio) work with zero credential config; a 401 @@ -127,7 +127,7 @@ export function createOpenAiEmbedder(opts) { * signal: AbortSignal | undefined, * }} args * @returns {Promise<{ vectors: Float32Array[], usage?: { prompt_tokens?: number, total_tokens?: number } }>} - * @ref LLP 0024#embedder-speaks-openai-compatible-base_url-configurable [implements] — config names the env var; the key resolves at call time and never reaches logs + * @ref LLP 0024#embedder-speaks-openai-compatible-base_url-configurable [implements]: config names the env var; the key resolves at call time and never reaches logs */ async function requestEmbeddings({ batch, config, env, endpoint, fetchImpl, signal }) { const apiKey = env[config.api_key_env] diff --git a/hypaware-core/plugins-workspace/embedder-openai/src/config.js b/hypaware-core/plugins-workspace/embedder-openai/src/config.js index 1caa0982..71c78da8 100644 --- a/hypaware-core/plugins-workspace/embedder-openai/src/config.js +++ b/hypaware-core/plugins-workspace/embedder-openai/src/config.js @@ -11,7 +11,7 @@ * @import { EmbedderConfigError, EmbedderConfigResult } from './types.d.ts' */ -// @ref LLP 0024#embedder-speaks-openai-compatible-base_url-configurable [implements] — defaults cover OpenAI; base_url override covers proxies and localhost servers +// @ref LLP 0024#embedder-speaks-openai-compatible-base_url-configurable [implements]: defaults cover OpenAI; base_url override covers proxies and localhost servers export const DEFAULT_BASE_URL = 'https://api.openai.com' export const DEFAULT_MODEL = 'text-embedding-3-small' export const DEFAULT_API_KEY_ENV = 'OPENAI_API_KEY' @@ -19,7 +19,7 @@ export const DEFAULT_MAX_BATCH = 128 export const DEFAULT_TIMEOUT_MS = 30_000 /** - * Validate the plugin's config slice. Every field is optional — the + * Validate the plugin's config slice. Every field is optional: the * zero-config default targets OpenAI with `OPENAI_API_KEY`. * * @param {unknown} value diff --git a/hypaware-core/plugins-workspace/embedder-openai/src/index.js b/hypaware-core/plugins-workspace/embedder-openai/src/index.js index bee8766f..b7ce4664 100644 --- a/hypaware-core/plugins-workspace/embedder-openai/src/index.js +++ b/hypaware-core/plugins-workspace/embedder-openai/src/index.js @@ -15,7 +15,7 @@ const CAPABILITY_VERSION = '1.0.0' * `hypaware.embedder@1` capability backed by an OpenAI-compatible * `POST /v1/embeddings` client. * - * Activation itself performs no network IO and reads no credentials — + * Activation itself performs no network IO and reads no credentials: * the API key resolves from the environment per request. Enabling this * plugin is the explicit opt-in that allows captured content (indexed * text and query strings) to leave the machine when `base_url` points @@ -23,7 +23,7 @@ const CAPABILITY_VERSION = '1.0.0' * local. * * @param {PluginActivationContext} ctx - * @ref LLP 0024#embedding-is-a-separate-capability [implements] — embedding is its own capability; choosing this provider is an explicit `plugins[]` config decision + * @ref LLP 0024#embedding-is-a-separate-capability [implements]: embedding is its own capability; choosing this provider is an explicit `plugins[]` config decision */ export async function activate(ctx) { ctx.configRegistry.registerSection({ @@ -35,7 +35,7 @@ export async function activate(ctx) { const validated = validateEmbedderConfig(ctx.config) if (!validated.ok) { const detail = validated.errors.map((e) => `${e.pointer || '/'}: ${e.message}`).join('; ') - const err = /** @type {HypError} */ (new Error(`${PLUGIN_NAME}: invalid config — ${detail}`)) + const err = /** @type {HypError} */ (new Error(`${PLUGIN_NAME}: invalid config - ${detail}`)) err.hypErrorKind = 'embedder_config_invalid' throw err } diff --git a/hypaware-core/plugins-workspace/format-iceberg/src/commit.js b/hypaware-core/plugins-workspace/format-iceberg/src/commit.js index af45bf97..24e1a20a 100644 --- a/hypaware-core/plugins-workspace/format-iceberg/src/commit.js +++ b/hypaware-core/plugins-workspace/format-iceberg/src/commit.js @@ -82,7 +82,7 @@ export async function commitBatch(input, priorState) { if (!priorState.exists) { try { - // @ref LLP 0022#partition-derivation — create with the writer-owned + // @ref LLP 0022#partition-derivation: create with the writer-owned // day-grain partitionSpec + conversation sort order. Both default to // unpartitioned/unsorted in icebird when `partitioning` is absent. await icebergCreateTable({ @@ -97,7 +97,7 @@ export async function commitBatch(input, priorState) { throw wrapCommitError(err, 'iceberg_commit_failed', `create table failed at '${input.tableUrl}'`) } } else if (priorState.metadata) { - // @ref LLP 0022#drift-rejection — an existing table whose partition spec no + // @ref LLP 0022#drift-rejection: an existing table whose partition spec no // longer matches the dataset's derived day grain is rejected; the export // cannot retroactively repartition object-store data files. [constrained-by] const existingSpec = currentPartitionSpec(priorState.metadata) ?? { 'spec-id': 0, fields: [] } @@ -115,7 +115,7 @@ export async function commitBatch(input, priorState) { // Reverse drift: the dataset stopped deriving partitioning but the // table on disk is partitioned. The append itself would succeed (icebird // keeps routing rows through the existing spec) while the sink reports - // `unpartitioned` — reject rather than let layout and telemetry diverge. + // `unpartitioned`. Reject rather than let layout and telemetry diverge. const existingLabel = existingSpec.fields.map((f) => `${f.transform}(${f.name})`).join(',') throw newError( 'iceberg_partition_spec_drift', @@ -294,9 +294,9 @@ function toNumber(value) { * * The blob-io adapter raises `iceberg_metadata_read_failed` for two * structurally distinct conditions: - * - The object is genuinely missing — adapter sets `code = 'ENOENT'`. + * - The object is genuinely missing: adapter sets `code = 'ENOENT'`. * - The underlying read errored (timeout, throttle, 500, SDK failure) - * — adapter leaves `code` unset. + * Adapter leaves `code` unset. * * Treating the kind alone as a miss conflates the two and lets a * flaky read drive the sink into `create` mode. Match only the diff --git a/hypaware-core/plugins-workspace/format-iceberg/src/index.js b/hypaware-core/plugins-workspace/format-iceberg/src/index.js index 239a0783..1eac5492 100644 --- a/hypaware-core/plugins-workspace/format-iceberg/src/index.js +++ b/hypaware-core/plugins-workspace/format-iceberg/src/index.js @@ -15,7 +15,7 @@ const PLUGIN_VERSION = '1.0.0' * `createSink` builds an Iceberg writer over the destination's * `BlobStore` and the inner `SinkEncoder` resolved by the kernel. * - * The plugin contributes no `sinks[]` entry — table-format writers + * The plugin contributes no `sinks[]` entry: table-format writers * bypass the destination's sink contribution and become the sink * themselves (see `src/core/registry/sinks.js` * §`instantiateTableFormat`). The destination must still provide diff --git a/hypaware-core/plugins-workspace/format-iceberg/src/maintenance.js b/hypaware-core/plugins-workspace/format-iceberg/src/maintenance.js index f3ae5556..660d718b 100644 --- a/hypaware-core/plugins-workspace/format-iceberg/src/maintenance.js +++ b/hypaware-core/plugins-workspace/format-iceberg/src/maintenance.js @@ -138,15 +138,15 @@ export async function discoverExportDatasets(blobStore, prefix) { * 0.8.10 preserves v3 row lineage across the rewrite, which matters * because export tables are created with `formatVersion: 3`). * - * @ref LLP 0022#compaction — this is the *out-of-band* rewrite the spec + * @ref LLP 0022#compaction: this is the *out-of-band* rewrite the spec * reserves: it must only run from an explicit, manual invocation * (`hyp sink maintain --compact`), never from the daemon loop or the * sink tick, because a full read-rewrite in the daemon process is the * OOM/blocking failure mode already seen with the parquet encoder. * * The rewrite is skipped while the table's live data-file count is below - * `compactFileCount` — for a day-partitioned archive the files are - * already large, so most tables never reach the threshold — and when the + * `compactFileCount` - for a day-partitioned archive the files are + * already large, so most tables never reach the threshold - and when the * current snapshot's `total-files-size` exceeds `compactMaxBytes` * (icebird's rewrite holds every live row in memory; see DEFAULTS). * @@ -156,7 +156,7 @@ export async function discoverExportDatasets(blobStore, prefix) { * latest metadata is re-loaded BEFORE any cleanup: a timeout after the * conditional PUT durably landed, or an SDK-internal retry of its own * successful write surfacing 412, both leave the staged rewrite as the - * table's current snapshot — deleting its data files then would corrupt + * table's current snapshot. Deleting its data files then would corrupt * the export. Only when the reload confirms the staged snapshot did not * land is a conflict's staged output deleted best-effort (icebird's * `icebergRewrite` leaves it orphaned, which is why this stages and @@ -165,7 +165,7 @@ export async function discoverExportDatasets(blobStore, prefix) { * * Every non-compaction outcome is discriminated by `reason` so the CLI * can tell an idle table from a failed rewrite (a swallowed failure here - * would misreport as "below threshold" — the one manual compaction tool + * would misreport as "below threshold" - the one manual compaction tool * misdiagnosing itself). A metadata *load* failure is only reported as * `no-table` when the table verifiably does not exist; auth/IO failures * surface as `error` so the CLI exits nonzero instead of printing an @@ -271,14 +271,14 @@ async function compactExportTableInner({ tableUrl, resolver, lister, compactFile // // The stage phase itself writes data files, a manifest, and a // manifest list one by one, and icebird only reports `writtenFiles` - // on a StagedUpdate that completed — so a failure after the first + // on a StagedUpdate that completed. A failure after the first // write would leak everything written before it. Track every path // whose write finished through a wrapped resolver and reclaim them // when staging dies mid-flight. /** @type {string[]} */ const stagedPaths = [] // `Resolver.writer` is optional; a resolver without one can't stage - // anything, so there's nothing to track — let icebergStageRewrite + // anything, so there's nothing to track. Let icebergStageRewrite // surface its own failure through the catch below. const baseWriter = resolver.writer /** @type {Resolver} */ @@ -349,7 +349,7 @@ async function compactExportTableInner({ tableUrl, resolver, lister, compactFile // A 412 means the conditional write was rejected, so once the reload // confirms the staged snapshot is absent the staged files are safe // to reclaim. Any other shape (network error, reload failure) could - // still be an in-flight commit — leave the bounded orphans rather + // still be an in-flight commit: leave the bounded orphans rather // than risk deleting data files a landed commit references. if (outcome === 'lost' && isCommitConflict(err)) { span.setAttribute('staged_files_reclaimed', staged.writtenFiles.length) @@ -366,7 +366,7 @@ async function compactExportTableInner({ tableUrl, resolver, lister, compactFile compacted: false, reason: 'error', error: - `${describeError(err)}; commit outcome unverified — ` + + `${describeError(err)}; commit outcome unverified - ` + `${staged.writtenFiles.length} staged rewrite file(s) left under the table ` + '(deleting them could corrupt the table if the commit actually landed)', dataFilesBefore, @@ -464,10 +464,10 @@ function describeError(err) { /** * Run export maintenance on all datasets under a prefix: snapshot - * expiration per dataset, plus — only when `compact` is set — the + * expiration per dataset, plus (only when `compact` is set) the * out-of-band data-file rewrite ({@link compactExportTable}). * - * @ref LLP 0022#compaction — `compact` defaults to false; the daemon + * @ref LLP 0022#compaction: `compact` defaults to false; the daemon * and the sink tick never set it. Only the manual CLI path * (`hyp sink maintain --compact`) opts in. * diff --git a/hypaware-core/plugins-workspace/format-iceberg/src/partitioning.js b/hypaware-core/plugins-workspace/format-iceberg/src/partitioning.js index c42e2294..1d70006d 100644 --- a/hypaware-core/plugins-workspace/format-iceberg/src/partitioning.js +++ b/hypaware-core/plugins-workspace/format-iceberg/src/partitioning.js @@ -11,7 +11,7 @@ import { icebergSchemaForColumns } from './schema.js' * @import { DatasetPartitioning } from './types.d.ts' */ -// @ref LLP 0022#partition-derivation — the export partitions by a writer-owned +// @ref LLP 0022#partition-derivation: the export partitions by a writer-owned // day grain on the dataset's primaryTimestampColumn, derived independently of // the cache's `cachePartitioning` (which would impose an unbounded // per-conversation file count on an archive). [implements] @@ -19,7 +19,7 @@ import { icebergSchemaForColumns } from './schema.js' * Derive the export table's layout for a dataset: a `day(primaryTimestampColumn)` * partition plus a within-partition sort on the dataset's lookup columns. * Returns `null` when the dataset declares no `primaryTimestampColumn` present - * in its schema — that dataset exports unpartitioned (V1 behavior unchanged). + * in its schema: that dataset exports unpartitioned (V1 behavior unchanged). * * @param {DatasetRegistration | undefined} reg * @param {readonly ColumnSpec[]} columns @@ -50,15 +50,15 @@ export function derivePartitioning(reg, columns) { } } -// @ref LLP 0022#within-partition-sort — cluster each day partition by the +// @ref LLP 0022#within-partition-sort: cluster each day partition by the // dataset's declared identity (lookup) columns so a conversation lookup prunes // row groups by min/max, without the file-count cost of partitioning on it. -// This is the one place the export reads `cachePartitioning` — sort axis only. +// This is the one place the export reads `cachePartitioning`: sort axis only. // [implements] /** * Build a sort order from the dataset's declared identity columns * (`cachePartitioning.iceberg.fields`, transform `identity`), in declared order. - * Returns an empty (unsorted) order when none apply — icebird treats that as a + * Returns an empty (unsorted) order when none apply: icebird treats that as a * no-op, so an undeclared dataset is day-partitioned but unsorted. * * @param {DatasetRegistration} reg diff --git a/hypaware-core/plugins-workspace/format-iceberg/src/schema.js b/hypaware-core/plugins-workspace/format-iceberg/src/schema.js index 05766bcb..303ed450 100644 --- a/hypaware-core/plugins-workspace/format-iceberg/src/schema.js +++ b/hypaware-core/plugins-workspace/format-iceberg/src/schema.js @@ -44,7 +44,7 @@ export function icebergSchemaForColumns(columns) { /** * Reconcile a `ColumnSpec[]` schema with an existing Iceberg table * schema so subsequent appends keep field ids stable. The result is - * the schema to pass back into `icebergAppend` — same fields as + * the schema to pass back into `icebergAppend`, same fields as * `icebergSchemaForColumns` but with ids re-bound to whatever the * existing table already carries. * diff --git a/hypaware-core/plugins-workspace/format-iceberg/src/state.js b/hypaware-core/plugins-workspace/format-iceberg/src/state.js index dad16125..267a51b5 100644 --- a/hypaware-core/plugins-workspace/format-iceberg/src/state.js +++ b/hypaware-core/plugins-workspace/format-iceberg/src/state.js @@ -78,7 +78,7 @@ export async function loadMarker(blobStore, key) { /** * Write an export marker. Writes are non-conditional because the marker - * is the *result* of a successful commit — overwriting a stale marker + * is the *result* of a successful commit. Overwriting a stale marker * is the desired behavior when a later snapshot subsumes the one the * marker referred to. * @@ -117,7 +117,7 @@ export async function writeMarker(blobStore, key, marker) { * @param {ExportMarker | null} marker * @param {ProbeStateLike | string | undefined} state * The current probe state. A bare snapshot id string is accepted for - * backward compatibility — without metadata ancestry can only be + * backward compatibility, without metadata ancestry can only be * proven via equality. * @returns {boolean} */ @@ -146,7 +146,7 @@ function normalizeProbeState(state) { /** * Walk `parent-snapshot-id` from `currentSnapshotId` looking for * `markerSnapshotId`. Returns true iff the marker's snapshot is a - * strict ancestor of current — i.e. a commit landed on top of the + * strict ancestor of current, i.e. a commit landed on top of the * marker's snapshot via the usual single-writer linear history. * * Iceberg snapshot ids are random 64-bit longs, so a numeric compare diff --git a/hypaware-core/plugins-workspace/format-iceberg/src/table-format.js b/hypaware-core/plugins-workspace/format-iceberg/src/table-format.js index 9fdbb61b..1049ddab 100644 --- a/hypaware-core/plugins-workspace/format-iceberg/src/table-format.js +++ b/hypaware-core/plugins-workspace/format-iceberg/src/table-format.js @@ -196,7 +196,7 @@ async function exportDataset({ ctx, batch, dataset, partitions, prefix, log, mai return { partitionsExported: partitions.length, bytesWritten: 0, status: 'skipped' } } - // @ref LLP 0022#partition-derivation — derived per dataset at commit time + // @ref LLP 0022#partition-derivation: derived per dataset at commit time // because `createSink` runs once for a sink that exports many datasets, so // the spec cannot be resolved up front. [implements] const partitioning = derivePartitioning(ctx.query.getDataset(dataset), columns) @@ -289,7 +289,7 @@ async function exportDataset({ ctx, batch, dataset, partitions, prefix, log, mai hyp_dataset: dataset, hyp_batch_id: batch.batchId, encoder_format: ctx.encoder.format, - // @ref LLP 0022#observability — surface the resolved layout so a smoke + // @ref LLP 0022#observability: surface the resolved layout so a smoke // can assert what was written, not just that rows landed. hyp_partition_spec: partitioning?.partitionSpecLabel ?? 'unpartitioned', hyp_sort_order: partitioning?.sortOrderLabel ?? '', @@ -478,7 +478,7 @@ function collectPartitionKeys(partitions) { const out = {} for (const partition of partitions) { for (const [k, v] of Object.entries(partition.partition ?? {})) { - // Preserve the first non-empty value per key — different + // Preserve the first non-empty value per key, different // partitions may share the same key (e.g. `partition=all`). if (typeof v === 'string' && v.length > 0 && !(k in out)) out[k] = v } diff --git a/hypaware-core/plugins-workspace/format-jsonl/src/index.js b/hypaware-core/plugins-workspace/format-jsonl/src/index.js index a819a2a3..f41af7b4 100644 --- a/hypaware-core/plugins-workspace/format-jsonl/src/index.js +++ b/hypaware-core/plugins-workspace/format-jsonl/src/index.js @@ -18,7 +18,7 @@ const COMPRESSION = 'GZIP' /** * Activate `@hypaware/format-jsonl`. Registers the `hypaware.encoder@1` * capability with a gzipped JSONL encoder. The encoder's `supports` - * list is empty by design — pairing JSONL with `@hypaware/local-fs` + * list is empty by design: pairing JSONL with `@hypaware/local-fs` * resolves to a non-queryable sink (the design's "Parquet+local-fs is * queryable, JSONL+local-fs is not" rule). It is still useful as a * grep-friendly archive sink. diff --git a/hypaware-core/plugins-workspace/format-parquet/src/columns.js b/hypaware-core/plugins-workspace/format-parquet/src/columns.js index d821e500..2644812c 100644 --- a/hypaware-core/plugins-workspace/format-parquet/src/columns.js +++ b/hypaware-core/plugins-workspace/format-parquet/src/columns.js @@ -28,7 +28,7 @@ export function rowsToColumnSources(columns, rows) { // JSON columns arrive from the cache (Iceberg `variant`) as parsed // objects/arrays. hyparquet-writer keys its dictionary by reference, so // structurally-identical objects are otherwise distinct entries and the - // column collapses to PLAIN — re-storing the (denormalized) blob on every + // column collapses to PLAIN: re-storing the (denormalized) blob on every // row. Interning by canonical content makes identical values share one // reference so they dictionary-encode (stored once), while the JSON // logical type still round-trips the original object to readers. @@ -40,12 +40,12 @@ export function rowsToColumnSources(columns, rows) { /** * Replace structurally-identical object/array values in `data` with a single * shared reference, in place. Primitive values (already-stringified JSON, - * numbers, null) are left untouched — they dedupe by value already. + * numbers, null) are left untouched: they dedupe by value already. * * The interning key is a plain `JSON.stringify` of the value, which is a * faithful, injective rendering of what the JSON writer will emit (numbers * vs strings stay distinct, no sentinel that real data could forge). Values - * that cannot be serialized — a nested BigInt is the only realistic case — + * that cannot be serialized, a nested BigInt being the only realistic case, * throw and are simply left un-interned: keeping their own reference means a * distinct value is never merged into another, at the cost of not deduping * that one value. Such values do not occur in these columns in practice @@ -64,7 +64,7 @@ function internJsonValues(data) { try { key = JSON.stringify(v) } catch { - continue // non-serializable (e.g. nested BigInt) — do not intern + continue // non-serializable (e.g. nested BigInt): do not intern } const existing = seen.get(key) if (existing !== undefined) data[i] = existing diff --git a/hypaware-core/plugins-workspace/format-parquet/src/index.js b/hypaware-core/plugins-workspace/format-parquet/src/index.js index e68eb293..ea035544 100644 --- a/hypaware-core/plugins-workspace/format-parquet/src/index.js +++ b/hypaware-core/plugins-workspace/format-parquet/src/index.js @@ -21,8 +21,8 @@ const DEFAULT_ZSTD_LEVEL = 3 // Row-group clustering. hyparquet-writer keeps a column dictionary-encoded // only while a row group's DISTINCT values fit under its ~1 MiB dictionary- // page cap. Columns denormalized onto every row but constant per conversation -// (e.g. `tools`, `system_text`) explode to PLAIN — re-storing every copy in -// full — once a single row group spans the whole partition's distinct values. +// (e.g. `tools`, `system_text`) explode to PLAIN: re-storing every copy in +// full once a single row group spans the whole partition's distinct values. // Bounding each row group to a small number of distinct cluster keys (and a // max row count) keeps the dictionary alive. The dictionary decision depends // on the distinct-value COUNT, not row order, and the source rows already @@ -118,7 +118,7 @@ function makeZstdCompressor(level) { * for writing the encoded bytes once this encoder hands them back. * * @param {PluginActivationContext} ctx - * @ref LLP 0014#queryable-sinks [implements] — parquet encoder declares `queryable`; lights up only paired with a blob store + * @ref LLP 0014#queryable-sinks [implements]: parquet encoder declares `queryable`, lights up only paired with a blob store */ export async function activate(ctx) { const settings = resolveEncodeSettings(ctx.config, ctx.log) @@ -181,7 +181,7 @@ async function encodePartition(partition, ctx, settings) { const sourceRows = ctx.rows // Derive a stable schema from the declared column types (not from the - // data) so we can write row groups incrementally — never holding more + // data) so we can write row groups incrementally: never holding more // than one cluster group of rows (plus its columnar copy) in memory. // This is what stops `hyp sink force` on a large partition from OOMing // while materializing the whole partition at once. diff --git a/hypaware-core/plugins-workspace/gascity/src/dataset.js b/hypaware-core/plugins-workspace/gascity/src/dataset.js index c9471886..942b4f6c 100644 --- a/hypaware-core/plugins-workspace/gascity/src/dataset.js +++ b/hypaware-core/plugins-workspace/gascity/src/dataset.js @@ -13,7 +13,7 @@ export const PARTITION_LABEL = 'all' /** * Stable column order for the `gascity_messages` dataset. A trimmed * projection of the donor schema (Collectivus `gascity_messages.schema`) - * sufficient for V1 — `city` and `provider_session_id` carry session + * sufficient for V1: `city` and `provider_session_id` carry session * identity, `event_time` is the primary timestamp, and `metadata` * carries everything the normalizer doesn't hoist (including * `dev_run_id` from the harness so smoke flows can filter by run). @@ -68,7 +68,7 @@ export function discoverParts(ctx) { } /** - * Live-ingest refresh path — gascity writes rows through the kernel + * Live-ingest refresh path: gascity writes rows through the kernel * cache service from the supervisor subscriber, so there is no external * source file to refresh here. The contract still wants a result, so * report `skipped` with zero rows (a sentinel the query layer tolerates diff --git a/hypaware-core/plugins-workspace/gascity/src/index.js b/hypaware-core/plugins-workspace/gascity/src/index.js index c2ac8e27..c8eaa8f3 100644 --- a/hypaware-core/plugins-workspace/gascity/src/index.js +++ b/hypaware-core/plugins-workspace/gascity/src/index.js @@ -24,7 +24,7 @@ import { setGascityRuntime } from './runtime.js' * - init preset `gascity` * - skill `hypaware-gascity` * - * Activation does NOT start the source — `gascity attach` is the + * Activation does NOT start the source: `gascity attach` is the * lifecycle trigger, mirroring the donor's "configure first, attach * on demand" UX. The source starts on the first attach and reloads * on every subsequent attach/detach. diff --git a/hypaware-core/plugins-workspace/gascity/src/transport.js b/hypaware-core/plugins-workspace/gascity/src/transport.js index beacc1e9..599879ad 100644 --- a/hypaware-core/plugins-workspace/gascity/src/transport.js +++ b/hypaware-core/plugins-workspace/gascity/src/transport.js @@ -5,7 +5,7 @@ * * In production the source opens an HTTP/SSE connection per city * (`/v0/city//events/stream`) and consumes lifecycle - * frames. The HTTP path is not exercised by the V1 smoke suite — + * frames. The HTTP path is not exercised by the V1 smoke suite: * `gascity_attach_writes_partition` boots an in-process fixture * supervisor and replaces the transport at the well-known global * key below. @@ -23,7 +23,7 @@ export const TRANSPORT_SYMBOL = Symbol.for('hypaware-gascity:transport') /** * Look up the active transport from the `globalThis` registry. The - * default (HTTP/SSE) path is deliberately not implemented in V1 — + * default (HTTP/SSE) path is deliberately not implemented in V1: * activation surfaces a no-op subscription if nothing is registered * so attach/detach still produce the spans the bead asks for. * diff --git a/hypaware-core/plugins-workspace/local-fs/src/blob-store.js b/hypaware-core/plugins-workspace/local-fs/src/blob-store.js index 7039314e..810c4f7e 100644 --- a/hypaware-core/plugins-workspace/local-fs/src/blob-store.js +++ b/hypaware-core/plugins-workspace/local-fs/src/blob-store.js @@ -110,7 +110,7 @@ export function createLocalFsBlobStore({ baseDir }) { await fs.mkdir(dir, { recursive: true }) const bytes = await materializeBody(input.body) if (input.ifNoneMatch === '*') { - // Conditional create — use O_EXCL so a concurrent writer cannot + // Conditional create: use O_EXCL so a concurrent writer cannot // race past us. node:fs/promises maps O_EXCL via `flag: 'wx'`. try { const handle = await fs.open(dest, 'wx') diff --git a/hypaware-core/plugins-workspace/local-fs/src/index.js b/hypaware-core/plugins-workspace/local-fs/src/index.js index c253141d..e62cb6f1 100644 --- a/hypaware-core/plugins-workspace/local-fs/src/index.js +++ b/hypaware-core/plugins-workspace/local-fs/src/index.js @@ -20,7 +20,7 @@ const PLUGIN_VERSION = '1.0.0' * capability as a full `BlobStore` (put/get/list/delete) AND contributes * a `local-fs` sink that writes encoded partition bytes under * `///`. The sink contribution is - * untouched by the BlobStore migration — existing + * untouched by the BlobStore migration: existing * encoder-writer + local-fs sinks keep working unchanged. * * BlobStore base directory resolution: @@ -30,12 +30,12 @@ const PLUGIN_VERSION = '1.0.0' * * The sink closes over the activation context so its `exportBatch` can * (a) look up dataset schemas through `ctx.query.getDataset` and - * (b) stream cache rows through `ctx.storage.readRows` — both inputs + * (b) stream cache rows through `ctx.storage.readRows`: both inputs * are then handed to the paired encoder via the kernel's * `sink.encode_partition` helper. * * @param {PluginActivationContext} ctx - * @ref LLP 0014#bytes-flow-down-semantics-flow-up [implements] — provides hypaware.blob-store; never knows its bytes' format + * @ref LLP 0014#bytes-flow-down-semantics-flow-up [implements]: provides hypaware.blob-store, never knows its bytes' format */ export async function activate(ctx) { const baseDir = resolveExportsBaseDir({ pluginConfig: ctx.config, env: ctx.env }) @@ -176,7 +176,7 @@ function lookupColumns(query, datasetName) { /** * Open the partition's cache rows as an async iterable. When the * partition lacks a `tablePath` (or the table doesn't exist yet on - * disk), yield nothing instead of throwing — the encoder will land an + * disk), yield nothing instead of throwing: the encoder will land an * empty file at the expected path, which is the right behavior for a * registered-but-empty partition. * diff --git a/hypaware-core/plugins-workspace/otel/src/collector.js b/hypaware-core/plugins-workspace/otel/src/collector.js index 73383780..481be873 100644 --- a/hypaware-core/plugins-workspace/otel/src/collector.js +++ b/hypaware-core/plugins-workspace/otel/src/collector.js @@ -156,7 +156,7 @@ function asObject(value) { /** * Stamp `listen_host` and `listen_port` onto the currently-active * `source.start` span. Called by `source.js` once the HTTP listener has - * bound — the kernel opens the span for us, and we just enrich it. + * bound: the kernel opens the span for us, and we just enrich it. * * @param {string} host * @param {number} port diff --git a/hypaware-core/plugins-workspace/otel/src/index.js b/hypaware-core/plugins-workspace/otel/src/index.js index ce915b69..bf3a66af 100644 --- a/hypaware-core/plugins-workspace/otel/src/index.js +++ b/hypaware-core/plugins-workspace/otel/src/index.js @@ -12,8 +12,8 @@ import { startOtelSource } from './source.js' * Activate `@hypaware/otel`. * * Registers: - * - source `otlp` (configSection: `otel`) — owns the HTTP listener - * - dataset `logs`, dataset `traces`, dataset `metrics` — fronted + * - source `otlp` (configSection: `otel`), owns the HTTP listener + * - dataset `logs`, dataset `traces`, dataset `metrics`. Fronted * by the kernel-managed Iceberg cache * * Activation auto-starts the source so the listener is ready as soon @@ -22,7 +22,7 @@ import { startOtelSource } from './source.js' * `source.start` span (or via `kernel.sources.status('otlp')`). * * @param {PluginActivationContext} ctx - * @ref LLP 0012#source-kinds [implements] — OTLP HTTP receiver registered as a source; owns logs/traces/metrics tables + * @ref LLP 0012#source-kinds [implements]: OTLP HTTP receiver registered as a source; owns logs/traces/metrics tables */ export async function activate(ctx) { ctx.sources.register({ diff --git a/hypaware-core/plugins-workspace/otel/src/otlp/metrics.js b/hypaware-core/plugins-workspace/otel/src/otlp/metrics.js index 80973f6e..e017bcd9 100644 --- a/hypaware-core/plugins-workspace/otel/src/otlp/metrics.js +++ b/hypaware-core/plugins-workspace/otel/src/otlp/metrics.js @@ -15,7 +15,7 @@ import { * Flatten an `ExportMetricsServiceRequest` JSON envelope into one row per * data point. Row keys line up with `METRICS_COLUMNS` in `../datasets.js`. * Covers gauge, sum, histogram, exponential histogram, and summary metric - * shapes — matching the donor's collector.js logic but written fresh + * shapes: matching the donor's collector.js logic but written fresh * against the camelCase JSON wire form. * * @param {unknown} payload diff --git a/hypaware-core/plugins-workspace/otel/src/server.js b/hypaware-core/plugins-workspace/otel/src/server.js index 4ce99e9b..fe1e08d8 100644 --- a/hypaware-core/plugins-workspace/otel/src/server.js +++ b/hypaware-core/plugins-workspace/otel/src/server.js @@ -28,7 +28,7 @@ const EMPTY_PARTIAL_SUCCESS = { * `start`) is responsible for wrapping that path in an `otel.receive` * span and translating exception types to `error_kind` attributes. * - * Only OTLP/JSON is accepted in this pass — the OTLP/protobuf decoder + * Only OTLP/JSON is accepted in this pass: the OTLP/protobuf decoder * chain from the donor (`collectivus/src/protobuf.js`, * `collectivus/src/otlp/*`) is left out of V1 and can be added later * without changing the request handler shape. diff --git a/hypaware-core/plugins-workspace/otel/src/source.js b/hypaware-core/plugins-workspace/otel/src/source.js index 38c8df17..65a10a93 100644 --- a/hypaware-core/plugins-workspace/otel/src/source.js +++ b/hypaware-core/plugins-workspace/otel/src/source.js @@ -61,8 +61,8 @@ export async function startOtelSource(ctx) { /** * Read `listen_host` / `listen_port` out of the activation config slice. - * Falls back to defaults when the keys are missing or wrongly typed — - * mistyped values log a warning so the operator notices on first boot. + * Falls back to defaults when the keys are missing or wrongly typed. + * Mistyped values log a warning so the operator notices on first boot. * * @param {PluginActivationContext} ctx */ diff --git a/hypaware-core/plugins-workspace/s3/src/blob-store.js b/hypaware-core/plugins-workspace/s3/src/blob-store.js index 0a1a42ce..e9b6aff0 100644 --- a/hypaware-core/plugins-workspace/s3/src/blob-store.js +++ b/hypaware-core/plugins-workspace/s3/src/blob-store.js @@ -19,7 +19,7 @@ export const BLOB_STORE_KIND = 's3' * smoke and unit tests can supply a fake S3 client without spinning up * the AWS SDK. Production builds wire `defaultS3BlobStoreClientFactory`. * - * Keys passed to put/get/delete are relative — the BlobStore prepends + * Keys passed to put/get/delete are relative: the BlobStore prepends * the configured `prefix` (slash-joined) before calling into S3. `prefix` * is normalized at construction so callers do not have to think about * trailing slashes. @@ -67,7 +67,7 @@ export function createS3BlobStore({ bucket, prefix, client }) { // `bucket` and `prefix` are surfaced on the returned BlobStore so // consumers that care about S3-specific telemetry (e.g. the iceberg // commit span) can read them without reaching back into config. - // They are advisory — the BlobStore methods do not consult these + // They are advisory: the BlobStore methods do not consult these // properties, the original closure values are the source of truth. bucket, prefix: normalized, @@ -289,7 +289,7 @@ export async function defaultS3BlobStoreClientFactory(opts) { /** * Build a sentinel `BlobStore` that fails every call with a clear * actionable error. The s3 plugin returns this when activation runs - * without a plugin-level bucket — the capability still resolves (so + * without a plugin-level bucket: the capability still resolves (so * downstream consumers can discover the s3 provider exists) but using * it without configuration is a programming error, not a silent * fallback. diff --git a/hypaware-core/plugins-workspace/s3/src/client.js b/hypaware-core/plugins-workspace/s3/src/client.js index e1f58452..4e343105 100644 --- a/hypaware-core/plugins-workspace/s3/src/client.js +++ b/hypaware-core/plugins-workspace/s3/src/client.js @@ -28,12 +28,12 @@ /** * Resolve which credential branch the SDK will use without actually * resolving the credentials themselves. We never log the resolved - * access key id, session token, or any signed value — only the + * access key id, session token, or any signed value, only the * `kind` token, which is safe to expose. * * Precedence matches the AWS SDK v3 default chain. We don't sniff the * web-identity / sso / process / metadata branches deeply (they require - * a network request) — `unknown` falls back to `metadata` because that + * a network request). `unknown` falls back to `metadata` because that * is the last link in the chain. * * @param {S3ClientOptions} opts diff --git a/hypaware-core/plugins-workspace/s3/src/config.js b/hypaware-core/plugins-workspace/s3/src/config.js index 71e8e67d..d087adb8 100644 --- a/hypaware-core/plugins-workspace/s3/src/config.js +++ b/hypaware-core/plugins-workspace/s3/src/config.js @@ -6,7 +6,7 @@ * The validator runs at config-load time. It returns a normalized config * object on success (trailing-slash-stripped prefix, defaulted booleans) * and a list of `s3_config_invalid` errors on failure. Sink-instance - * config carries a `schedule` field too — the kernel-level validator + * config carries a `schedule` field too. The kernel-level validator * already enforces standard 5-field cron, so this module ignores it. */ diff --git a/hypaware-core/plugins-workspace/s3/src/errors.js b/hypaware-core/plugins-workspace/s3/src/errors.js index dd3ba614..499abb82 100644 --- a/hypaware-core/plugins-workspace/s3/src/errors.js +++ b/hypaware-core/plugins-workspace/s3/src/errors.js @@ -73,7 +73,7 @@ export function classifyAwsError(err) { * Render a safe, non-credential-bearing diagnostic message for an * error_kind. The original `err.message` can carry signed URLs, * access key ids, or session tokens in some AWS SDK error paths, so - * we never log it directly — only the classified kind plus a short + * we never log it directly, only the classified kind plus a short * human description. * * @param {S3ErrorKind} errorKind diff --git a/hypaware-core/plugins-workspace/s3/src/index.js b/hypaware-core/plugins-workspace/s3/src/index.js index 07d6611f..1a300dca 100644 --- a/hypaware-core/plugins-workspace/s3/src/index.js +++ b/hypaware-core/plugins-workspace/s3/src/index.js @@ -28,7 +28,7 @@ const PLUGIN_VERSION = '1.0.0' * Activate `@hypaware/s3`. Provides `hypaware.blob-store@1` as a full * `BlobStore` (put/get/list/delete; put supports `ifNoneMatch` via * S3's `If-None-Match` header) AND contributes a single `s3` sink. The - * sink contribution is untouched by the BlobStore migration — existing + * sink contribution is untouched by the BlobStore migration. Existing * encoder-writer + s3 sinks keep working unchanged. * * BlobStore resolution: @@ -37,18 +37,18 @@ const PLUGIN_VERSION = '1.0.0' * config shape. Test wiring may inject a fake client factory by * setting `ctx.config.__blobStoreClientFactory`. * - If no `bucket` is configured at plugin level, activation provides - * a sentinel BlobStore that throws an actionable error on any call - * — the capability is still discoverable but its use without + * a sentinel BlobStore that throws an actionable error on any call. + * The capability is still discoverable but its use without * configuration is a programming error, not a silent fallback. * * Test/smoke wiring can override the sink's client factory by passing * `clientFactory` on `SinkCreateContext.config` (under the * intentionally-prefixed key `__clientFactory`). Production configs - * never carry this key — it lives outside the validated config shape + * never carry this key. It lives outside the validated config shape * specifically so it cannot be set from JSON config files. * * @param {PluginActivationContext} ctx - * @ref LLP 0014#bytes-flow-down-semantics-flow-up [implements] — one blob-store for parquet or iceberg bytes; format stays separable + * @ref LLP 0014#bytes-flow-down-semantics-flow-up [implements]: one blob-store for parquet or iceberg bytes, format stays separable */ export async function activate(ctx) { const blobStore = await resolveBlobStore(ctx) @@ -120,7 +120,7 @@ export async function activate(ctx) { * Register a kernel query dataset for each configured `query_sources` * entry, so `hyp query sql` can read parquet / Iceberg data back from * S3. No-op when the plugin config carries no `query_sources`. Invalid - * `query_sources` config fails activation — a misconfigured read target + * `query_sources` config fails activation. A misconfigured read target * should surface at boot, not at query time. * * @param {PluginActivationContext} ctx @@ -156,8 +156,8 @@ async function registerQuerySources(ctx) { /** * Build a `BlobStore` for a query source. It is rooted exactly where the - * sink writes — the plugin-level `prefix` when reading the plugin's own - * bucket — and `source.prefix` names the dataset path relative to that + * sink writes. The plugin-level `prefix` when reading the plugin's own + * bucket, and `source.prefix` names the dataset path relative to that * root. This matters for Iceberg: its manifests embed data-file paths * relative to the writer's table URL base, so the reader must reproduce * the same (bucket, root-prefix) split or the data files won't resolve. @@ -176,7 +176,7 @@ async function buildQuerySourceBlobStore(ctx, source) { const bucket = source.bucket ?? optString(config.bucket) if (!bucket) { throw new Error( - `${PLUGIN_NAME}: query_source '${source.name}' has no bucket — set query_sources[].bucket or a plugin-level bucket` + `${PLUGIN_NAME}: query_source '${source.name}' has no bucket - set query_sources[].bucket or a plugin-level bucket` ) } // Same bucket as the plugin → inherit the plugin prefix as the root so @@ -336,8 +336,8 @@ function buildSink({ config, client, encoder, sinkCtx, query, storage }) { if (lastConfigFailure !== undefined) { // Only the partitions that actually failed should be retried. // Without this, the driver's fallback in src/core/sinks/driver.js - // outboxes every partition in the batch — including ones already - // PUT to S3 — when a terminal error trips mid-batch. + // outboxes every partition in the batch, including ones already + // sent to S3, when a terminal error trips mid-batch. return { status: 'failed', partitionsExported: exported, @@ -388,7 +388,7 @@ function errorKindFor(err) { /** * Configuration / credential / region-mismatch / bucket-missing errors - * are not transient — retrying the same partition will fail the same + * are not transient. Retrying the same partition will fail the same * way next tick. Return `status=failed` so the sink driver does not * loop on them. * @@ -464,8 +464,8 @@ async function materializeBytes(bytes) { /** * Build the s3 plugin's BlobStore from plugin-level config. When no - * `bucket` is configured the activation provides a sentinel BlobStore - * — callers see a clear `s3_blob_store_unconfigured` error instead of + * `bucket` is configured the activation provides a sentinel BlobStore. + * Callers see a clear `s3_blob_store_unconfigured` error instead of * the capability silently no-op-ing. * * @param {PluginActivationContext} ctx diff --git a/hypaware-core/plugins-workspace/s3/src/query-config.js b/hypaware-core/plugins-workspace/s3/src/query-config.js index 4d3cffdb..d491b02e 100644 --- a/hypaware-core/plugins-workspace/s3/src/query-config.js +++ b/hypaware-core/plugins-workspace/s3/src/query-config.js @@ -107,7 +107,7 @@ function parseEntry(entry, pointer, errors) { const endpointUrl = readString(raw, 'endpoint_url', pointer, errors) if (endpointUrl !== undefined && !isHttpUrl(endpointUrl)) { // Validate connection fields here so a bad read target surfaces at - // boot, not at query time — mirrors the sink config validator. + // boot, not at query time: mirrors the sink config validator. errors.push({ pointer: `${pointer}/endpoint_url`, message: `endpoint_url must be a valid http(s) URL (got '${endpointUrl}')`, diff --git a/hypaware-core/plugins-workspace/s3/src/query-dataset.js b/hypaware-core/plugins-workspace/s3/src/query-dataset.js index 10ac4d1e..91e99262 100644 --- a/hypaware-core/plugins-workspace/s3/src/query-dataset.js +++ b/hypaware-core/plugins-workspace/s3/src/query-dataset.js @@ -96,7 +96,7 @@ async function createDataSource(source, blobStore, partitions) { /** * Read an Iceberg table from S3 by adapting the BlobStore into the - * `Resolver`/`Lister` pair icebird speaks — the same path the local + * `Resolver`/`Lister` pair icebird speaks, the same path the local * cache uses, only the bytes come from S3. The `@hypaware/format-iceberg` * adapter and `icebird` are imported lazily so parquet-only deployments * never load them. @@ -107,7 +107,7 @@ async function createDataSource(source, blobStore, partitions) { */ async function createIcebergDataSource(source, blobStore) { // Guard against a missing/empty table the way the local cache does - // (`tableExists` before load) — without it, loading catalog metadata + // (`tableExists` before load). Without it, loading catalog metadata // for a table that was never written can throw instead of reading as // an empty result. if (!(await icebergTableHasMetadata(blobStore, source.prefix))) { @@ -139,7 +139,7 @@ async function icebergTableHasMetadata(blobStore, prefix) { /** * Materialize an S3 object into an in-memory `AsyncBuffer`. This reads - * the whole object — fine for the modest parquet files HypAware writes; + * the whole object (fine for the modest parquet files HypAware writes); * range-based reads can be layered on later by extending `getObject`. * * @param {BlobStore} blobStore diff --git a/hypaware-core/plugins-workspace/vector-search/src/commands.js b/hypaware-core/plugins-workspace/vector-search/src/commands.js index 84de8bb9..400aa7d7 100644 --- a/hypaware-core/plugins-workspace/vector-search/src/commands.js +++ b/hypaware-core/plugins-workspace/vector-search/src/commands.js @@ -33,7 +33,7 @@ export async function runVector(_argv, ctx) { /** * @param {string[]} argv * @param {CommandRunContext} ctx - * @ref LLP 0024#cli-surface [implements] — contributed through the CLI registry; results format through the intrinsic formatter + * @ref LLP 0024#cli-surface [implements]: contributed through the CLI registry; results format through the intrinsic formatter */ export async function runVectorSearch(argv, ctx) { const parsed = parseVectorSearchArgv(argv) diff --git a/hypaware-core/plugins-workspace/vector-search/src/config.js b/hypaware-core/plugins-workspace/vector-search/src/config.js index e9eaa9bc..e1cd4fed 100644 --- a/hypaware-core/plugins-workspace/vector-search/src/config.js +++ b/hypaware-core/plugins-workspace/vector-search/src/config.js @@ -12,7 +12,7 @@ // Deliberately longer than cache maintenance's 60-minute default: index // freshness is a background nicety, and every tick can spend embedding // API tokens. -// @ref LLP 0024#freshness-rides-the-cache-maintenance-pattern [implements] — own interval + max_tick_ms budget, modeled on the maintenance tick +// @ref LLP 0024#freshness-rides-the-cache-maintenance-pattern [implements]: own interval + max_tick_ms budget, modeled on the maintenance tick export const REFRESH_DEFAULTS = Object.freeze({ enabled: true, interval_minutes: 240, @@ -20,7 +20,7 @@ export const REFRESH_DEFAULTS = Object.freeze({ // Per-tick embedding spend bound (rows). Soft: checked before each // shard build so one oversized partition can overshoot once rather // than starve forever. - // @ref LLP 0024#open-questions [implements] — per-tick row budget resolves the cost-visibility question for the daemon timer + // @ref LLP 0024#open-questions [implements]: per-tick row budget resolves the cost-visibility question for the daemon timer max_rows_per_tick: 5_000, }) @@ -33,7 +33,7 @@ const INDEX_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/ * * @param {unknown} value * @returns {VectorConfigResult} - * @ref LLP 0024#indexes-are-declared-in-config-sharded-per-partition [implements] — index definitions are portable config, not per-host state + * @ref LLP 0024#indexes-are-declared-in-config-sharded-per-partition [implements]: index definitions are portable config, not per-host state */ export function validateVectorSearchConfig(value) { /** @type {VectorConfigError[]} */ diff --git a/hypaware-core/plugins-workspace/vector-search/src/hypvector.js b/hypaware-core/plugins-workspace/vector-search/src/hypvector.js index 783ecde0..1641f53c 100644 --- a/hypaware-core/plugins-workspace/vector-search/src/hypvector.js +++ b/hypaware-core/plugins-workspace/vector-search/src/hypvector.js @@ -15,7 +15,7 @@ let cached = null * missing dependency instead. * * @returns {Promise} - * @ref LLP 0024#packaging [implements] — root optionalDependency, graceful degradation when absent + * @ref LLP 0024#packaging [implements]: root optionalDependency, graceful degradation when absent */ export async function loadHypvector() { if (cached) return cached @@ -31,7 +31,7 @@ export async function loadHypvector() { cached = { ok: false, message: - "optional dependency 'hypvector' is not installed — reinstall hypaware without --omit=optional to enable vector search", + "optional dependency 'hypvector' is not installed - reinstall hypaware without --omit=optional to enable vector search", } } return cached diff --git a/hypaware-core/plugins-workspace/vector-search/src/index.js b/hypaware-core/plugins-workspace/vector-search/src/index.js index 6a5f4020..b8027058 100644 --- a/hypaware-core/plugins-workspace/vector-search/src/index.js +++ b/hypaware-core/plugins-workspace/vector-search/src/index.js @@ -32,7 +32,7 @@ const CAPABILITY_VERSION = '1.0.0' * `plugins[]` decision. * * @param {PluginActivationContext} ctx - * @ref LLP 0024#plugin-not-kernel [implements] — vector search ships as a bundled plugin; query stays the SQL/dataset surface + * @ref LLP 0024#plugin-not-kernel [implements]: vector search ships as a bundled plugin; query stays the SQL/dataset surface */ export async function activate(ctx) { ctx.configRegistry.registerSection({ @@ -44,12 +44,12 @@ export async function activate(ctx) { const validated = validateVectorSearchConfig(ctx.config) if (!validated.ok) { const detail = validated.errors.map((e) => `${e.pointer || '/'}: ${e.message}`).join('; ') - const err = /** @type {HypError} */ (new Error(`${PLUGIN_NAME}: invalid config — ${detail}`)) + const err = /** @type {HypError} */ (new Error(`${PLUGIN_NAME}: invalid config - ${detail}`)) err.hypErrorKind = 'vector_config_invalid' throw err } - // @ref LLP 0024#embedding-is-a-separate-capability [constrained-by] — embedding always resolves through the capability registry, never a baked-in provider + // @ref LLP 0024#embedding-is-a-separate-capability [constrained-by]: embedding always resolves through the capability registry, never a baked-in provider const embedder = /** @type {EmbedderCapability} */ (ctx.requireCapability('hypaware.embedder', '^1.0.0')) /** @type {VectorSearchRuntime} */ diff --git a/hypaware-core/plugins-workspace/vector-search/src/refresh.js b/hypaware-core/plugins-workspace/vector-search/src/refresh.js index a12300e8..d45c1e87 100644 --- a/hypaware-core/plugins-workspace/vector-search/src/refresh.js +++ b/hypaware-core/plugins-workspace/vector-search/src/refresh.js @@ -22,7 +22,7 @@ const PLUGIN_NAME = '@hypaware/vector-search' * Bring every configured index up to date, incrementally: only * missing/stale shards are rebuilt, orphaned shards are swept, and the * optional budget bounds wall-clock and embedding spend. Budgets are - * soft — they gate *starting* a shard, not finishing it, so one + * soft: they gate *starting* a shard, not finishing it, so one * oversized partition overshoots a tick once instead of starving * forever. * @@ -40,7 +40,7 @@ const PLUGIN_NAME = '@hypaware/vector-search' * onShard?: (info: { index: string, fileBase: string, state: string, rowsEmbedded: number }) => void, * }} args * @returns {Promise} - * @ref LLP 0024#freshness-rides-the-cache-maintenance-pattern [implements] — incremental per-partition shard builds under a tick budget; per-shard writes are durable + * @ref LLP 0024#freshness-rides-the-cache-maintenance-pattern [implements]: incremental per-partition shard builds under a tick budget; per-shard writes are durable */ export async function refreshIndexes({ decls, embedder, storage, indexesDir, log, budget, dimension, onShard }) { /** @type {RefreshReport} */ @@ -66,7 +66,7 @@ export async function refreshIndexes({ decls, embedder, storage, indexesDir, log dimension: embedder.dimensions ?? dimension, }) - // Orphans sweep even on an exhausted budget — deletion is cheap and + // Orphans sweep even on an exhausted budget: deletion is cheap and // never spends embedding tokens. for (const state of states) { if (state.state === 'orphan') { @@ -160,7 +160,7 @@ async function withShardBuildLock(key, fn) { * id, embed, and write the hypvector file + sidecar atomically. Builds * of the same shard serialize in-process; temp names carry pid + a * UUID so concurrent processes (CLI search vs daemon tick) can never - * write the same temp file, and the final rename stays atomic — + * write the same temp file, and the final rename stays atomic: * last-writer-wins on identical inputs. * * @param {{ @@ -173,7 +173,7 @@ async function withShardBuildLock(key, fn) { * log: PluginLogger, * }} args * @returns {Promise<{ rowsEmbedded: number }>} - * @ref LLP 0024#index-files-are-plugin-state [implements] — shards are derived artifacts under plugin state, rebuilt from the cache + * @ref LLP 0024#index-files-are-plugin-state [implements]: shards are derived artifacts under plugin state, rebuilt from the cache */ async function buildShard({ decl, partition, state, embedder, storage, indexesDir, log }) { const { file } = shardPaths(indexesDir, decl.name, state.fileBase) diff --git a/hypaware-core/plugins-workspace/vector-search/src/runtime.js b/hypaware-core/plugins-workspace/vector-search/src/runtime.js index 904e5a6f..1f9f7dca 100644 --- a/hypaware-core/plugins-workspace/vector-search/src/runtime.js +++ b/hypaware-core/plugins-workspace/vector-search/src/runtime.js @@ -4,7 +4,7 @@ * Module-local activation state, mirroring the `@hypaware/gascity` * pattern: `activate()` captures what command bodies and the refresh * source need (paths, validated config, the resolved embedder), and - * they read it back here — `CommandRunContext` deliberately does not + * they read it back here. `CommandRunContext` deliberately does not * carry per-plugin paths. */ @@ -25,7 +25,7 @@ export function setVectorSearchRuntime(value) { */ export function getVectorSearchRuntime() { if (!runtime) { - throw new Error('@hypaware/vector-search: runtime not initialized — plugin is not activated') + throw new Error('@hypaware/vector-search: runtime not initialized - plugin is not activated') } return runtime } diff --git a/hypaware-core/plugins-workspace/vector-search/src/search.js b/hypaware-core/plugins-workspace/vector-search/src/search.js index f33a5874..eea4ae40 100644 --- a/hypaware-core/plugins-workspace/vector-search/src/search.js +++ b/hypaware-core/plugins-workspace/vector-search/src/search.js @@ -17,7 +17,7 @@ const DEFAULT_TOP_K = 10 * Embed the query, fan out across every shard of the matching indexes, * and merge the global top-K. The query embeds *before* any refresh: * its dimension is the staleness signal that catches dimension drift - * (same model, different vector length — a changed embedder + * (same model, different vector length: a changed embedder * `dimensions` config, or a different server behind the same model * name) so auto-refresh re-embeds instead of hard-failing on shards it * just declared fresh. @@ -29,12 +29,12 @@ const DEFAULT_TOP_K = 10 * interactive caller sees where embedding spend goes. * - `never`: search existing shards as-is. A shard built with a * different model or dimension than the live embedder is a hard - * error here — cross-model scores are meaningless and must not + * error here: cross-model scores are meaningless and must not * silently degrade. * * @param {{ runtime: VectorSearchRuntime, opts: VectorSearchOptions, onProgress?: (line: string) => void }} args * @returns {Promise} - * @ref LLP 0024#indexes-are-declared-in-config-sharded-per-partition [implements] — partition discovery via the registry-backed cache, fan-out, top-K merge; model/dimension mismatch is never a silent degraded search + * @ref LLP 0024#indexes-are-declared-in-config-sharded-per-partition [implements]: partition discovery via the registry-backed cache, fan-out, top-K merge; model/dimension mismatch is never a silent degraded search */ export async function searchVectorIndexes({ runtime, opts, onProgress }) { const decls = selectIndexes(runtime.config.indexes, opts) @@ -42,7 +42,7 @@ export async function searchVectorIndexes({ runtime, opts, onProgress }) { throw newVectorError( 'vector_no_indexes', runtime.config.indexes.length === 0 - ? 'no vector indexes configured — add indexes[] to the vector-search plugin config' + ? 'no vector indexes configured - add indexes[] to the vector-search plugin config' : `no configured vector index matches${opts.index ? ` index '${opts.index}'` : ''}${opts.dataset ? ` dataset '${opts.dataset}'` : ''}` ) } @@ -101,7 +101,7 @@ export async function searchVectorIndexes({ runtime, opts, onProgress }) { 'vector_dimension_mismatch', refresh === 'never' ? `shard ${decl.name}/${state.fileBase} has dimension ${state.meta.dimension} but the query embedded to ${queryEmbed.dimension}; rerun without --no-refresh to re-embed` - : `shard ${decl.name}/${state.fileBase} was just rebuilt at dimension ${state.meta.dimension} but the query embedded to ${queryEmbed.dimension} — the embedder is returning inconsistent dimensions for model '${runtime.embedder.model}'` + : `shard ${decl.name}/${state.fileBase} was just rebuilt at dimension ${state.meta.dimension} but the query embedded to ${queryEmbed.dimension} - the embedder is returning inconsistent dimensions for model '${runtime.embedder.model}'` ) } searchable.push({ decl, state }) @@ -157,7 +157,7 @@ function selectIndexes(indexes, opts) { /** * Search-time refresh: report the pending work upfront (so the caller * sees the embedding spend before it happens), then rebuild with no row - * budget — an interactive search wants a complete answer. `dimension` + * budget: an interactive search wants a complete answer. `dimension` * is the length the query embedded to, so dimension drift classifies * stale here even when the embedder has no configured `dimensions`. * @@ -193,7 +193,7 @@ async function refreshForSearch({ runtime, decls, dimension, onProgress }) { /** * Resolve hit texts back out of the cache. One pass per partition that * actually holds hits, stopping early once every id in that partition - * is resolved — never a scan over partitions without hits. + * is resolved. Never a scan over partitions without hits. * * @param {{ runtime: VectorSearchRuntime, decls: VectorIndexDeclaration[], hits: VectorSearchHit[] }} args */ diff --git a/hypaware-core/plugins-workspace/vector-search/src/shards.js b/hypaware-core/plugins-workspace/vector-search/src/shards.js index 18e34c02..e7babcb8 100644 --- a/hypaware-core/plugins-workspace/vector-search/src/shards.js +++ b/hypaware-core/plugins-workspace/vector-search/src/shards.js @@ -11,7 +11,7 @@ import path from 'node:path' /** * Shard layout: one hypvector parquet file per cache partition, plus a - * JSON sidecar carrying what the parquet KV metadata cannot — the + * JSON sidecar carrying what the parquet KV metadata cannot: the * embedder model and the source partition's row count at build time. * Files live under `/indexes//`: * @@ -35,14 +35,14 @@ function sortedEntries(partition) { * a human-readable label plus a short hash of the canonical partition * JSON. The sanitized label alone is lossy (`source=a/b` and * `source=a_b` both render `source=a_b`, and a value containing `,` - * or `=` can mimic another partition's entry list), so the hash — - * not the label — is what makes distinct partitions map to distinct + * or `=` can mimic another partition's entry list), so the hash (not the label) + * is what makes distinct partitions map to distinct * shard files. Keys are sorted so discovery order can never produce * two names for one partition; `all` covers partition-less datasets. * * @param {Record} partition * @returns {string} - * @ref LLP 0024#indexes-are-declared-in-config-sharded-per-partition [implements] — shard file names are label + partition hash so sanitization can never collide two partitions + * @ref LLP 0024#indexes-are-declared-in-config-sharded-per-partition [implements]: shard file names are label + partition hash so sanitization can never collide two partitions */ export function shardFileBase(partition) { const entries = sortedEntries(partition) @@ -85,7 +85,7 @@ export function contentId(text) { /** * Read every shard sidecar under one index dir. Unreadable or - * malformed sidecars are skipped — the shard will classify as + * malformed sidecars are skipped: the shard will classify as * `missing` and rebuild through the normal path. * * @param {string} indexesDir @@ -115,7 +115,7 @@ export function readShardMetas(indexesDir, indexName) { if (parsed.row_count > 0 && !fs.existsSync(path.join(dir, `${fileBase}.parquet`))) continue metas.set(fileBase, /** @type {ShardMeta} */ (parsed)) } - } catch { /* malformed sidecar — rebuild path handles it */ } + } catch { /* malformed sidecar: rebuild path handles it */ } } return metas } @@ -155,11 +155,11 @@ function matchesDeclaration(meta, decl, partition) { * (an index name reused over a different dataset/column must never * pass row-count + model checks and serve the old vectors) * - sidecar model differs from config model → `stale_model` - * (stale, not an error — the refresh path re-embeds) + * (stale, not an error: the refresh path re-embeds) * - sidecar dimension differs from the expected dimension, when the * caller knows one → `stale_dimension` * (the embedder's configured `dimensions`, or the dimension the - * query embedded to — same model, different vector length) + * query embedded to: same model, different vector length) * - sidecar source_row_count differs from the partition's current * rowCount → `stale_rows` * (compaction dedup can shrink rowCount without new content; the @@ -171,7 +171,7 @@ function matchesDeclaration(meta, decl, partition) { * * @param {{ partitions: CachePartitionMeta[], metas: Map, decl: VectorIndexDeclaration, model: string, dimension?: number }} args * @returns {ShardState[]} - * @ref LLP 0024#indexes-are-declared-in-config-sharded-per-partition [implements] — declaration, model, and dimension drift are all staleness; retention coupling dissolves into orphan sweep + * @ref LLP 0024#indexes-are-declared-in-config-sharded-per-partition [implements]: declaration, model, and dimension drift are all staleness; retention coupling dissolves into orphan sweep */ export function computeShardStates({ partitions, metas, decl, model, dimension }) { /** @type {ShardState[]} */ @@ -208,7 +208,7 @@ export function computeShardStates({ partitions, metas, decl, model, dimension } /** * Merge per-shard hit lists into one global top-K. Scores are cosine - * over normalized vectors (higher = better) — fixed at shard write + * over normalized vectors (higher = better): fixed at shard write * time, so a plain descending sort is a correct merge. * * @template {RawShardHit} T @@ -226,7 +226,7 @@ export function mergeTopK(hitLists, topK) { /** * Render a partition for display (`source=claude`, or `all`). Unlike - * {@link shardFileBase} this is unsanitized and unhashed — display + * {@link shardFileBase} this is unsanitized and unhashed: display * strings don't need to be collision-free file names. * * @param {Record} partition diff --git a/hypaware-core/plugins-workspace/vector-search/src/source.js b/hypaware-core/plugins-workspace/vector-search/src/source.js index 9fe59ec4..78191216 100644 --- a/hypaware-core/plugins-workspace/vector-search/src/source.js +++ b/hypaware-core/plugins-workspace/vector-search/src/source.js @@ -16,8 +16,8 @@ import { validateVectorSearchConfig } from './config.js' * interval, a wall-clock budget per tick, an in-flight guard so a slow * tick never stacks, and an unref'd handle so the timer cannot keep the * process alive. Registered as a source contribution because the daemon - * starts every registered source — that is the kernel's "give a plugin - * a periodic foothold" seam. + * starts every registered source (that is, the kernel's "give a plugin + * a periodic foothold") seam. * * Per-tick embedding spend is additionally bounded by * `refresh.max_rows_per_tick`; bounded per-partition shard writes match @@ -26,7 +26,7 @@ import { validateVectorSearchConfig } from './config.js' * * @param {PluginActivationContext} _ctx * @returns {Promise} - * @ref LLP 0024#freshness-rides-the-cache-maintenance-pattern [implements] — daemon timer with interval + per-tick wall-clock and row budgets + * @ref LLP 0024#freshness-rides-the-cache-maintenance-pattern [implements]: daemon timer with interval + per-tick wall-clock and row budgets */ export async function startVectorRefreshSource(_ctx) { const runtime = getVectorSearchRuntime() diff --git a/hypaware-core/plugins-workspace/vector-search/src/status.js b/hypaware-core/plugins-workspace/vector-search/src/status.js index 718d0e30..edad85d6 100644 --- a/hypaware-core/plugins-workspace/vector-search/src/status.js +++ b/hypaware-core/plugins-workspace/vector-search/src/status.js @@ -10,7 +10,7 @@ import { computeShardStates, readShardMetas } from './shards.js' /** * Per-index, per-partition shard coverage: state, model, dimension, * row counts, build time. Works without the optional hypvector - * dependency — everything here reads sidecar metas and the cache + * dependency. Everything here reads sidecar metas and the cache * partition listing only. * * @param {VectorSearchRuntime} runtime diff --git a/hypaware-core/smoke/flows/activation_lifecycle.js b/hypaware-core/smoke/flows/activation_lifecycle.js index 9a671316..e9ed89f4 100644 --- a/hypaware-core/smoke/flows/activation_lifecycle.js +++ b/hypaware-core/smoke/flows/activation_lifecycle.js @@ -30,7 +30,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'activation_lifecycle: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'activation_lifecycle: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/ai_gateway_passthrough.js b/hypaware-core/smoke/flows/ai_gateway_passthrough.js index 0927ad08..2a61ef2c 100644 --- a/hypaware-core/smoke/flows/ai_gateway_passthrough.js +++ b/hypaware-core/smoke/flows/ai_gateway_passthrough.js @@ -31,7 +31,7 @@ import { requireAiGatewayRuntime } from '../../plugins-workspace/ai-gateway/src/ * - Capability `hypaware.ai-gateway` registered at `2.0.0`. * - The echo upstream saw the request verbatim (gateway is a * pass-through). - * - No rows are written into `ai_gateway_messages` — phase 1 + * - No rows are written into `ai_gateway_messages`: phase 1 * intentionally ships no built-in projector, so without an adapter * plugin the gateway records nothing into the dataset. * - Pass-through telemetry STILL fires: the `aigw.exchange` log @@ -50,7 +50,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'ai_gateway_passthrough: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'ai_gateway_passthrough: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -71,7 +71,7 @@ export async function run({ harness, expect }) { // Config slice handed to the plugin's activate(). Listen on // 127.0.0.1:0 so the test grabs an ephemeral port; route every path - // ('/') to the echo upstream. No exchange projector is registered — + // ('/') to the echo upstream. No exchange projector is registered. // the gateway 2.0 contract is that with no projector the dataset // gets zero rows but pass-through telemetry still flows. const aiGatewayConfig = { @@ -118,7 +118,7 @@ export async function run({ harness, expect }) { await kernel.sources.start('ai-gateway', runtime.ctx) runtime.started = true - // Read the bound endpoint off the capability facade — exactly the + // Read the bound endpoint off the capability facade, exactly the // path an adapter plugin would use to discover the gateway. const registered = kernel.capabilities.list().find((c) => c.name === 'hypaware.ai-gateway') expect.that( @@ -182,7 +182,7 @@ export async function run({ harness, expect }) { // Query the dataset through the dispatcher. With no projector // registered the gateway must have written ZERO rows for this - // dev_run_id — the zero-projector contract. + // dev_run_id: the zero-projector contract. const sqlStdout = makeBuf() const sqlStderr = makeBuf() const sqlCode = await dispatch( @@ -232,7 +232,7 @@ export async function run({ harness, expect }) { await obs.shutdown() await echo.close() - // Telemetry assertions — pass-through telemetry MUST fire even + // Telemetry assertions: pass-through telemetry MUST fire even // without a projector. const traces = await expect.traces() const logs = await expect.logs() @@ -313,7 +313,7 @@ export async function run({ harness, expect }) { m.attributes?.hyp_plugin === '@hypaware/ai-gateway' ) expect.that( - 'metrics: hyp_rows_written for ai_gateway_messages is absent — projector wrote zero rows', + 'metrics: hyp_rows_written for ai_gateway_messages is absent - projector wrote zero rows', rowsWritten, (v) => v === undefined ) diff --git a/hypaware-core/smoke/flows/backfill_claude_fixture.js b/hypaware-core/smoke/flows/backfill_claude_fixture.js index 7bfa1ba1..3cb42c9f 100644 --- a/hypaware-core/smoke/flows/backfill_claude_fixture.js +++ b/hypaware-core/smoke/flows/backfill_claude_fixture.js @@ -14,11 +14,11 @@ import { loadManifests } from '../../../src/core/manifest.js' import { resolveDependencies } from '../../../src/core/dep_graph.js' /** - * Phase 7 smoke — Claude transcript backfill → query → idempotent rerun. + * Phase 7 smoke: Claude transcript backfill → query → idempotent rerun. * * Boots `@hypaware/ai-gateway` + `@hypaware/claude` against a tmp * HYP_HOME with a staged Claude transcript fixture under the fake HOME, - * then drives `hyp backfill claude` directly (no daemon — backfill is a + * then drives `hyp backfill claude` directly (no daemon: backfill is a * local file import) and asserts the bead-6 contract end to end: * * - **User-visible query result**: `ai_gateway_messages` holds the two @@ -30,7 +30,7 @@ import { resolveDependencies } from '../../../src/core/dep_graph.js' * span for `ai_gateway_messages`. * - **Idempotency (phase 8)**: a second `hyp backfill claude` (a fresh * run id, so the materializer re-scans committed partitions) writes - * ZERO new rows and the query still returns exactly two rows — the + * ZERO new rows and the query still returns exactly two rows. The * rerun did not duplicate. * * @param {{ harness: any, expect: any }} args @@ -39,7 +39,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'backfill_claude_fixture: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'backfill_claude_fixture: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/backfill_codex_fixture.js b/hypaware-core/smoke/flows/backfill_codex_fixture.js index 4b9c7717..a9b90eae 100644 --- a/hypaware-core/smoke/flows/backfill_codex_fixture.js +++ b/hypaware-core/smoke/flows/backfill_codex_fixture.js @@ -14,7 +14,7 @@ import { loadManifests } from '../../../src/core/manifest.js' import { resolveDependencies } from '../../../src/core/dep_graph.js' /** - * Phase 7 smoke — Codex rollout backfill → query → idempotent rerun. + * Phase 7 smoke: Codex rollout backfill → query → idempotent rerun. * * Boots `@hypaware/ai-gateway` + `@hypaware/codex` against a tmp * HYP_HOME with a staged modern Codex rollout under the fake HOME's @@ -38,7 +38,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'backfill_codex_fixture: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'backfill_codex_fixture: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/blob_sink_parquet_local_fs.js b/hypaware-core/smoke/flows/blob_sink_parquet_local_fs.js index 6d7d24a2..1eefb2ce 100644 --- a/hypaware-core/smoke/flows/blob_sink_parquet_local_fs.js +++ b/hypaware-core/smoke/flows/blob_sink_parquet_local_fs.js @@ -43,23 +43,23 @@ const ROW_COUNT = 50 * - the encoder emitted an `encoder.encode_parquet` span carrying * `row_count=50`, `bytes_written>0`, and `compression='SNAPPY'` * - * The smoke is a real plugin integration — the dataset rows are written + * The smoke is a real plugin integration: the dataset rows are written * into the kernel's Iceberg cache, then exported through the production * sink driver, encoder, and destination paths. * * @param {{ harness: any, expect: any }} args - * @ref LLP 0014#queryable-sinks [tests] — parquet-on-local-fs export through the production writer/destination path + * @ref LLP 0014#queryable-sinks [tests]: parquet-on-local-fs export through the production writer/destination path */ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'blob_sink_parquet_local_fs: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'blob_sink_parquet_local_fs: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } if (!obs.meter.provider) { throw new Error( - 'blob_sink_parquet_local_fs: meter provider not installed — expected HYP_DEV_TELEMETRY=1' + 'blob_sink_parquet_local_fs: meter provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -96,7 +96,7 @@ export async function run({ harness, expect }) { const { loaded, failed } = await loadManifests([parquetDir, localFsDir, fixtureDir]) if (failed.length > 0) { throw new Error( - `blob_sink_parquet_local_fs: manifest failures — ${failed + `blob_sink_parquet_local_fs: manifest failures - ${failed .map((f) => `${f.manifestPath}: ${f.message}`) .join('; ')}` ) diff --git a/hypaware-core/smoke/flows/blobstore_api_local_fs.js b/hypaware-core/smoke/flows/blobstore_api_local_fs.js index 77394d67..cf8e46cd 100644 --- a/hypaware-core/smoke/flows/blobstore_api_local_fs.js +++ b/hypaware-core/smoke/flows/blobstore_api_local_fs.js @@ -31,7 +31,7 @@ import { loadManifests } from '../../../src/core/manifest.js' * - `ifNoneMatch='*'` precondition; assert a conflicting put * rejects with the canonical error_kind * - * The smoke deliberately exercises the BlobStore API DIRECTLY — no + * The smoke deliberately exercises the BlobStore API DIRECTLY: no * sink instance is instantiated, no `driver.tick()` is fired. The * sink-instance code path is already exercised by * `blob_sink_parquet_local_fs.js`; this smoke proves the new @@ -44,7 +44,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'blobstore_api_local_fs: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'blobstore_api_local_fs: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/cache_roundtrip.js b/hypaware-core/smoke/flows/cache_roundtrip.js index 06ceb952..5aa058c0 100644 --- a/hypaware-core/smoke/flows/cache_roundtrip.js +++ b/hypaware-core/smoke/flows/cache_roundtrip.js @@ -32,13 +32,13 @@ import { loadManifests } from '../../../src/core/manifest.js' * - stdout: the count comes back as 100 * * @param {{ harness: any, expect: any }} args - * @ref LLP 0013#write-path-and-query [tests] — rows written to the cache come back through hyp query + * @ref LLP 0013#write-path-and-query [tests]: rows written to the cache come back through hyp query */ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'cache_roundtrip: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'cache_roundtrip: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/central_forward_outbox.js b/hypaware-core/smoke/flows/central_forward_outbox.js index 10c08ebb..0f6aab04 100644 --- a/hypaware-core/smoke/flows/central_forward_outbox.js +++ b/hypaware-core/smoke/flows/central_forward_outbox.js @@ -53,12 +53,12 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'central_forward_outbox: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'central_forward_outbox: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } if (!obs.meter.provider) { throw new Error( - 'central_forward_outbox: meter provider not installed — expected HYP_DEV_TELEMETRY=1' + 'central_forward_outbox: meter provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/claude_attach_detach.js b/hypaware-core/smoke/flows/claude_attach_detach.js index 25f9e5aa..3b918c6c 100644 --- a/hypaware-core/smoke/flows/claude_attach_detach.js +++ b/hypaware-core/smoke/flows/claude_attach_detach.js @@ -39,7 +39,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'claude_attach_detach: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'claude_attach_detach: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -49,7 +49,7 @@ export async function run({ harness, expect }) { // Seed an existing settings file with a non-managed env var so the // golden compare can verify the detach really restored the pre-attach - // state — not just deleted everything HypAware added. + // state: not just deleted everything HypAware added. const originalSettings = { env: { ANTHROPIC_API_KEY: 'sk-original-key' }, permissions: { allow: ['Bash(ls *)'] }, @@ -152,7 +152,7 @@ export async function run({ harness, expect }) { (v) => typeof v === 'string' && v.includes('Claude Code attached') && v.includes(settingsPath) ) - // Read patched settings — golden compare against an expected shape. + // Read patched settings: golden compare against an expected shape. const attached = JSON.parse(await fs.readFile(settingsPath, 'utf8')) expect.that( 'settings: pre-existing env.ANTHROPIC_API_KEY was preserved', diff --git a/hypaware-core/smoke/flows/cli_bundled_plugins_activated.js b/hypaware-core/smoke/flows/cli_bundled_plugins_activated.js index b3b35b3a..55831d4d 100644 --- a/hypaware-core/smoke/flows/cli_bundled_plugins_activated.js +++ b/hypaware-core/smoke/flows/cli_bundled_plugins_activated.js @@ -23,7 +23,7 @@ import { defaultConfigPath } from '../../../src/core/config/schema.js' * 4. `hyp status --json` emits a stable JSON document listing the * configured sources, sinks, clients, and active plugins. Because * neither `@hypaware/central` nor `@hypaware/gascity` is in this - * config, they must not appear — they are excluded from default + * config, they must not appear as they are excluded from default * activation but remain discoverable through the plugin catalog and * activatable via explicit config or init presets. * @@ -42,7 +42,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'cli_bundled_plugins_activated: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'cli_bundled_plugins_activated: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -50,7 +50,7 @@ export async function run({ harness, expect }) { // plugins. `@hypaware/format-jsonl`, `@hypaware/s3`, and // `@hypaware/format-iceberg` are intentionally omitted so the smoke // can assert the "skipped" log surface. `@hypaware/central` and - // `@hypaware/gascity` are not in this config — they are excluded from + // `@hypaware/gascity` are not in this config, they are excluded from // default activation but activatable via explicit config. const configPath = defaultConfigPath(harness.hypHome) await fs.mkdir(path.dirname(configPath), { recursive: true }) @@ -262,7 +262,7 @@ export async function run({ harness, expect }) { ) // Skipped = allowlist plugins this flow's config does not name (the // excluded-from-default set never reaches the skip loop). Bumps - // whenever a plugin joins V1_BUNDLED_PLUGIN_ALLOWLIST — most + // whenever a plugin joins V1_BUNDLED_PLUGIN_ALLOWLIST, most // recently @hypaware/context-graph (3 -> 4). expect.that( 'traces: at least one config-profile boot reports plugins_skipped=4', diff --git a/hypaware-core/smoke/flows/client_attach_idempotent.js b/hypaware-core/smoke/flows/client_attach_idempotent.js index 6ba38ade..c7de8031 100644 --- a/hypaware-core/smoke/flows/client_attach_idempotent.js +++ b/hypaware-core/smoke/flows/client_attach_idempotent.js @@ -40,13 +40,13 @@ import { requireAiGatewayRuntime } from '../../plugins-workspace/ai-gateway/src/ * span whose `status=failed` and `error_kind=cap_missing`. * * @param {{ harness: any, expect: any }} args - * @ref LLP 0017#attach-is-idempotent-and-reversible [tests] — re-running attach is a no-op; detach restores prior settings + * @ref LLP 0017#attach-is-idempotent-and-reversible [tests]: re-running attach is a no-op; detach restores prior settings */ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'client_attach_idempotent: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'client_attach_idempotent: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -68,7 +68,7 @@ export async function run({ harness, expect }) { await fs.writeFile(claudeSettingsPath, seedClaudeBody, 'utf8') const seedCodexBody = [ - '# user preferences — must survive attach/detach', + '# user preferences - must survive attach/detach', 'model = "gpt-5-codex"', '', '[history]', diff --git a/hypaware-core/smoke/flows/command_dispatch.js b/hypaware-core/smoke/flows/command_dispatch.js index b0b299d0..d141283f 100644 --- a/hypaware-core/smoke/flows/command_dispatch.js +++ b/hypaware-core/smoke/flows/command_dispatch.js @@ -24,7 +24,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'command_dispatch: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'command_dispatch: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -44,7 +44,7 @@ export async function run({ harness, expect }) { const listCode = await dispatch(['plugin', 'list', '--json'], { stdout, stderr }) expect.that('dispatch: plugin list --json exited 0', listCode, (v) => v === 0) - // Help path — verifies `cli.help_rendered` log + command_count. + // Help path: verifies `cli.help_rendered` log + command_count. const helpCode = await dispatch(['--help'], { stdout, stderr }) expect.that('dispatch: --help exited 0', helpCode, (v) => v === 0) diff --git a/hypaware-core/smoke/flows/config_load_validate.js b/hypaware-core/smoke/flows/config_load_validate.js index c124e0dd..b7144156 100644 --- a/hypaware-core/smoke/flows/config_load_validate.js +++ b/hypaware-core/smoke/flows/config_load_validate.js @@ -7,11 +7,11 @@ import { installObservability } from '../../../src/core/observability/index.js' import { dispatch } from '../../../src/core/cli/dispatch.js' /** - * Phase 6 smoke (renamed from `config_migrate_v1` in the plan — this + * Phase 6 smoke, renamed from `config_migrate_v1` in the original plan: this * fresh-start rig has no v1 install to migrate, so the smoke only * exercises schema + cross-plugin validation). * - * Case 1 — happy path: + * Case 1: happy path: * - Stages a tmp `~/.hyp/hypaware-config.json` with `version: 2`, * two enabled plugins (`@hypaware/ai-gateway`, `@hypaware/claude`), * and one blob sink (`@hypaware/format-parquet` + @@ -21,20 +21,20 @@ import { dispatch } from '../../../src/core/cli/dispatch.js' * `status=ok`, and that no log row carries `error_kind=...` * in this run. * - * Case 2 — incompatible sink pair: + * Case 2: incompatible sink pair: * - Same config but the sink pairs `@hypaware/format-parquet` with * `@hypaware/webhook` (a request destination, not a blob store). * - Asserts the command exits non-zero and at least one log row * carries `error_kind=sink_pair_incompatible`. * * @param {{ harness: any, expect: any }} args - * @ref LLP 0010#validation [tests] — config load + cross-plugin validation, incl. incompatible sink pairing + * @ref LLP 0010#validation [tests]: tests config load and cross-plugin validation, including incompatible sink pairing */ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'config_load_validate: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'config_load_validate: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -196,7 +196,7 @@ export async function run({ harness, expect }) { l.attributes?.error_kind !== undefined ) // Filter only entries that *could* have come from the OK run by - // pointer prefix — case 2 emits errors via the same logger, so we + // pointer prefix, as case 2 emits errors via the same logger and we // partition by error_kind. The case 1 contract is "no error_kind on // any config log row in the OK run". Because both runs share the // same dev_run_id, the strict test is "the count of error_kind logs @@ -208,7 +208,7 @@ export async function run({ harness, expect }) { (rows) => rows.length >= 1 ) - // The OK case must contribute zero error_kind logs — verify by + // The OK case must contribute zero error_kind logs. Verify by // checking that every error_kind log is one of the kinds case 2 // produces (sink_pair_incompatible, plus optional follow-ups from // the validator). diff --git a/hypaware-core/smoke/flows/context_graph_projects_rows.js b/hypaware-core/smoke/flows/context_graph_projects_rows.js index 0956f005..07ac7247 100644 --- a/hypaware-core/smoke/flows/context_graph_projects_rows.js +++ b/hypaware-core/smoke/flows/context_graph_projects_rows.js @@ -26,7 +26,7 @@ import { * - `select count(*) from edge` = 6 (via, used_model, 2× used, 2× touched) * - node_type breakdown matches the fixture * - re-running `graph project` leaves the counts unchanged (idempotent) - * - a `graph.project` span carries the write counts — the internal signal + * - a `graph.project` span carries the write counts: the internal signal * that the projection path (not just the CLI wrapper) actually ran * * @param {{ harness: any, expect: any }} args @@ -69,7 +69,7 @@ export async function run({ harness, expect }) { ) // Seed a small ai_gateway_messages fixture: two tool calls touching two - // files, plus a text part — all in one conversation, same app/model. + // files, plus a text part: all in one conversation, same app/model. const tablePath = aiGatewayTablePath(kernel.storage) await kernel.storage.appendRows(tablePath, [...AI_GATEWAY_SCHEMA_COLUMNS], [ fixtureRow({ message_id: 'm1', message_index: 0, role: 'assistant', part_type: 'tool_call', tool_name: 'Read', tool_call_id: 'tc1', tool_args: { file_path: '/repo/auth.py' } }), diff --git a/hypaware-core/smoke/flows/core_boot_noop.js b/hypaware-core/smoke/flows/core_boot_noop.js index 0afb04e8..151d2b68 100644 --- a/hypaware-core/smoke/flows/core_boot_noop.js +++ b/hypaware-core/smoke/flows/core_boot_noop.js @@ -27,7 +27,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'core_boot_noop: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'core_boot_noop: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/daemon_foreground_start_stop.js b/hypaware-core/smoke/flows/daemon_foreground_start_stop.js index 0c386a52..e3972a71 100644 --- a/hypaware-core/smoke/flows/daemon_foreground_start_stop.js +++ b/hypaware-core/smoke/flows/daemon_foreground_start_stop.js @@ -16,7 +16,7 @@ import { readStatusFile } from '../../../src/core/daemon/status.js' * graceful shutdown and verifies the status file + telemetry. * * The smoke opts out of OS signal handlers (`installSignalHandlers: - * false`) and uses `handle.stop()` to simulate SIGTERM — the actual + * false`) and uses `handle.stop()` to simulate SIGTERM. The actual * shutdown path is identical, but we avoid trampling the harness * process's own SIGTERM handling. Tick interval is set to 0 so the * scheduled sink loop never fires (Phase 5 owns sink-driven assertions). @@ -29,13 +29,13 @@ import { readStatusFile } from '../../../src/core/daemon/status.js' * children for every started source. * * @param {{ harness: any, expect: any }} args - * @ref LLP 0017#the-primary-daemon [tests] — boots the daemon, drives start/stop, asserts the lifecycle spans + * @ref LLP 0017#the-primary-daemon [tests]: boots the daemon, drives start/stop, asserts the lifecycle spans */ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'daemon_foreground_start_stop: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'daemon_foreground_start_stop: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -191,7 +191,7 @@ export async function run({ harness, expect }) { // `kernel.boot` opens its own root span (each boot is a logical // unit of work that survives the calling context), so we don't - // assert parent-of-daemon.run here — we only assert that the + // assert parent-of-daemon.run here. We only assert that the // daemon boot path emitted one with `mode=daemon`. const kernelBootSpans = traces.filter((/** @type {any} */ t) => t.name === 'kernel.boot') expect.that( @@ -203,8 +203,8 @@ export async function run({ harness, expect }) { // `source.start` either lands inside `daemon.run` (for sources the // daemon starts explicitly, like ai-gateway) or inside the // plugin's `activate` span (for sources that auto-start, like otel). - // We check that at least one source.start came up under daemon.run - // — that's the new code path Phase 3 actually adds. + // We check that at least one source.start came up under daemon.run. + // That's the new code path Phase 3 actually adds. const daemonRunIds = new Set( daemonRunSpans.map((/** @type {any} */ s) => s.spanId) ) diff --git a/hypaware-core/smoke/flows/daemon_install_render.js b/hypaware-core/smoke/flows/daemon_install_render.js index 61553b99..98bd0618 100644 --- a/hypaware-core/smoke/flows/daemon_install_render.js +++ b/hypaware-core/smoke/flows/daemon_install_render.js @@ -37,7 +37,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'daemon_install_render: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'daemon_install_render: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/gascity_attach_writes_partition.js b/hypaware-core/smoke/flows/gascity_attach_writes_partition.js index 540e5984..b1485ba6 100644 --- a/hypaware-core/smoke/flows/gascity_attach_writes_partition.js +++ b/hypaware-core/smoke/flows/gascity_attach_writes_partition.js @@ -29,8 +29,8 @@ import { loadManifests } from '../../../src/core/manifest.js' * - cache: `cache.append` spans for `hyp_dataset=gascity_messages` * * The fixture supervisor lives entirely in this file and is wired to - * the plugin via `globalThis[Symbol.for('hypaware-gascity:transport')]` - * — the well-known escape hatch documented in + * the plugin via `globalThis[Symbol.for('hypaware-gascity:transport')]`, + * the well-known escape hatch documented in * `plugins-workspace/gascity/src/transport.js`. * * @param {{ harness: any, expect: any }} args @@ -39,7 +39,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'gascity_attach_writes_partition: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'gascity_attach_writes_partition: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -83,11 +83,11 @@ export async function run({ harness, expect }) { } ) - // First attach — drives `source.start`. + // First attach: drives `source.start`. await dispatchCommand(['gascity', 'attach', 'hyptown'], { kernel, registry, harness }) await fixture.flush() - // Second attach — drives `source.reload`. + // Second attach: drives `source.reload`. await dispatchCommand(['gascity', 'attach', 'hypburb'], { kernel, registry, harness }) await fixture.flush() diff --git a/hypaware-core/smoke/flows/gateway_claude_capture.js b/hypaware-core/smoke/flows/gateway_claude_capture.js index 7f333260..bd5d86d3 100644 --- a/hypaware-core/smoke/flows/gateway_claude_capture.js +++ b/hypaware-core/smoke/flows/gateway_claude_capture.js @@ -12,7 +12,7 @@ import { runDaemon } from '../../../src/core/daemon/runtime.js' import { dispatch } from '../../../src/core/cli/dispatch.js' /** - * Phase 2 smoke — Claude exchange capture through the daemon. + * Phase 2 smoke: Claude exchange capture through the daemon. * * Boots `runDaemon` with `@hypaware/ai-gateway@2.0.0` AND * `@hypaware/claude@2.0.0` activated against an in-process echo @@ -46,7 +46,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'gateway_claude_capture: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'gateway_claude_capture: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -131,7 +131,7 @@ export async function run({ harness, expect }) { // ----- Compute the plugin state-file path that the Claude plugin // will use, then drive the hook command to populate it. This // mirrors how Claude Code itself would call the hook on - // SessionStart — only the entry path differs. The kernel resolves + // SessionStart: only the entry path differs. The kernel resolves // `ctx.paths.stateDir` to `/hypaware/plugins/` (see // `src/core/runtime/paths.js`), so we mirror that recipe here. const stateFile = path.join( diff --git a/hypaware-core/smoke/flows/gateway_codex_capture.js b/hypaware-core/smoke/flows/gateway_codex_capture.js index 47446858..0ea69e99 100644 --- a/hypaware-core/smoke/flows/gateway_codex_capture.js +++ b/hypaware-core/smoke/flows/gateway_codex_capture.js @@ -11,15 +11,15 @@ import { runDaemon } from '../../../src/core/daemon/runtime.js' import { dispatch } from '../../../src/core/cli/dispatch.js' /** - * Phase 7 smoke — OpenAI `/v1` passthrough plus the Codex-specific + * Phase 7 smoke: OpenAI `/v1` passthrough plus the Codex-specific * ChatGPT `/backend-api/codex/responses` capture under the daemon boot path. * * Boots `runDaemon` with `@hypaware/ai-gateway` activated and * OpenAI plus ChatGPT Codex upstreams. Two requests run through it: * - * - `POST /v1/chat/completions` — the legacy OpenAI Chat path, a + * - `POST /v1/chat/completions`: the legacy OpenAI Chat path, a * proxy of every non-streaming inference call. - * - `POST /backend-api/codex/responses` — the ChatGPT endpoint Codex + * - `POST /backend-api/codex/responses`: the ChatGPT endpoint Codex * Desktop uses; the response is an SSE stream so the recorder also * exercises the `is_sse=true` / `stream_event_count>0` columns and * Codex metadata projection. @@ -41,7 +41,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'gateway_codex_capture: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'gateway_codex_capture: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -63,7 +63,7 @@ export async function run({ harness, expect }) { // local entries appear first in the merged routing table // (lower seq), so with identical priority + prefix length // they outrank the plugin presets at routing time. The - // plugin presets remain in the table — they just never win + // plugin presets remain in the table. They just never win // routing for this smoke's traffic. { name: 'local-openai', base_url: openai.url, path_prefix: '/v1', provider: 'openai' }, { name: 'local-chatgpt', base_url: openai.url, path_prefix: '/backend-api/codex', provider: 'chatgpt' }, @@ -353,9 +353,8 @@ export async function run({ harness, expect }) { * Spin up an upstream that mimics the two OpenAI endpoints the smoke * exercises: * - * - `POST /chat/completions` — non-streaming JSON response. - * - `POST /responses` and `/backend-api/codex/responses` - * — SSE stream with three events ending in + * - `POST /chat/completions`: non-streaming JSON response. + * - `POST /responses` and `/backend-api/codex/responses`: SSE stream with three events ending in * `response.completed`. * * The fake upstream accepts both the OpenAI `/v1` paths and the diff --git a/hypaware-core/smoke/flows/iceberg_export_local_fs.js b/hypaware-core/smoke/flows/iceberg_export_local_fs.js index 9127719d..9b9856da 100644 --- a/hypaware-core/smoke/flows/iceberg_export_local_fs.js +++ b/hypaware-core/smoke/flows/iceberg_export_local_fs.js @@ -63,12 +63,12 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'iceberg_export_local_fs: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'iceberg_export_local_fs: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } if (!obs.meter.provider) { throw new Error( - 'iceberg_export_local_fs: meter provider not installed — expected HYP_DEV_TELEMETRY=1' + 'iceberg_export_local_fs: meter provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -107,7 +107,7 @@ export async function run({ harness, expect }) { const { loaded, failed } = await loadManifests([parquetDir, localFsDir, icebergDir, fixtureDir]) if (failed.length > 0) { throw new Error( - `iceberg_export_local_fs: manifest failures — ${failed + `iceberg_export_local_fs: manifest failures - ${failed .map((f) => `${f.manifestPath}: ${f.message}`) .join('; ')}` ) @@ -189,13 +189,13 @@ export async function run({ harness, expect }) { ) // Settle the spool so the fixture's rows are committed to their - // routed `source=...` partition table before discovery — appendRows + // routed `source=...` partition table before discovery. appendRows // only schedules a background flush at the size threshold. await kernel.storage.flushAll({ force: true }) // Discover the dataset partition the fixture registered and // simulate one sink tick by calling `exportBatch` directly. (The - // sink driver auto-tick path is still being wired up — flows in + // sink driver auto-tick path is still being wired up. Flows in // this repo all hand-tick for now.) const dataset = kernel.query.getDataset(DATASET) if (!dataset) throw new Error(`fixture dataset ${DATASET} did not register`) @@ -240,7 +240,7 @@ export async function run({ harness, expect }) { ) // The state marker proves the writer recorded idempotency state for - // the batch — a future tick would short-circuit instead of + // the batch. A future tick would short-circuit instead of // re-staging. const markerPath = path.join( destinationDir, @@ -266,7 +266,7 @@ export async function run({ harness, expect }) { ) expect.that('export2: status=exported', exportResult2.status, (v) => v === 'exported') - // Maintenance, default mode: snapshot expiration only — compaction is + // Maintenance, default mode: snapshot expiration only. Compaction is // out-of-band (LLP 0022) and must not run without the explicit opt-in. const maintainReport = await maintainExportTables({ blobStore, @@ -318,7 +318,7 @@ export async function run({ harness, expect }) { ) // After maintenance the table holds exactly the rows both batches - // committed — batch-1 and batch-2 each appended the same ROW_COUNT + // committed. Batch-1 and batch-2 each appended the same ROW_COUNT // fixture rows, so every id 0..ROW_COUNT-1 must appear exactly twice. // An exact check here is the smoke-tier guard against the rewrite's // central risk (LLP 0022): a compaction that silently drops rows. diff --git a/hypaware-core/smoke/flows/iceberg_export_partitioned_local_fs.js b/hypaware-core/smoke/flows/iceberg_export_partitioned_local_fs.js index b03092e2..23307f2c 100644 --- a/hypaware-core/smoke/flows/iceberg_export_partitioned_local_fs.js +++ b/hypaware-core/smoke/flows/iceberg_export_partitioned_local_fs.js @@ -59,7 +59,7 @@ const ROWS = [ * - export succeeds (`status='exported'`, `bytesWritten > 0`). * - the committed metadata carries a `day(message_created_at)` partition * spec and a `conversation_id`-led sort order. - * - the 4 rows land in exactly 2 data files — one per day partition. + * - the 4 rows land in exactly 2 data files: one per day partition. * - each data file's rows read back ordered by `conversation_id` (the * sort happened on write, not just in metadata). * - the `iceberg.table.create` span carries `hyp_partition_spec` and @@ -74,7 +74,7 @@ const ROWS = [ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { - throw new Error('iceberg_export_partitioned_local_fs: tracer not installed — expected HYP_DEV_TELEMETRY=1') + throw new Error('iceberg_export_partitioned_local_fs: tracer not installed - expected HYP_DEV_TELEMETRY=1') } const cacheRoot = path.join(harness.stateDir, 'cache') @@ -103,7 +103,7 @@ export async function run({ harness, expect }) { async () => { const { loaded, failed } = await loadManifests([parquetDir, localFsDir, icebergDir]) if (failed.length > 0) { - throw new Error(`manifest failures — ${failed.map((f) => `${f.manifestPath}: ${f.message}`).join('; ')}`) + throw new Error(`manifest failures - ${failed.map((f) => `${f.manifestPath}: ${f.message}`).join('; ')}`) } const entries = loaded.map((l) => ({ manifest: l.manifest, diff --git a/hypaware-core/smoke/flows/iceberg_export_s3_fixture.js b/hypaware-core/smoke/flows/iceberg_export_s3_fixture.js index 41fc6433..5525067b 100644 --- a/hypaware-core/smoke/flows/iceberg_export_s3_fixture.js +++ b/hypaware-core/smoke/flows/iceberg_export_s3_fixture.js @@ -58,10 +58,10 @@ const PREFIX = 'lake/iceberg' export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { - throw new Error('iceberg_export_s3_fixture: tracer provider not installed — expected HYP_DEV_TELEMETRY=1') + throw new Error('iceberg_export_s3_fixture: tracer provider not installed - expected HYP_DEV_TELEMETRY=1') } if (!obs.meter.provider) { - throw new Error('iceberg_export_s3_fixture: meter provider not installed — expected HYP_DEV_TELEMETRY=1') + throw new Error('iceberg_export_s3_fixture: meter provider not installed - expected HYP_DEV_TELEMETRY=1') } const cacheRoot = path.join(harness.stateDir, 'cache') @@ -181,7 +181,7 @@ export async function run({ harness, expect }) { const { loaded, failed } = await loadManifests([parquetDir, s3Dir, icebergDir, fixtureDir]) if (failed.length > 0) { throw new Error( - `iceberg_export_s3_fixture: manifest failures — ${failed + `iceberg_export_s3_fixture: manifest failures - ${failed .map((f) => `${f.manifestPath}: ${f.message}`) .join('; ')}` ) @@ -270,7 +270,7 @@ export async function run({ harness, expect }) { }) // Settle the spool so the fixture's rows are committed to their - // routed `source=...` partition table before discovery — appendRows + // routed `source=...` partition table before discovery. appendRows // only schedules a background flush at the size threshold. await kernel.storage.flushAll({ force: true }) diff --git a/hypaware-core/smoke/flows/iceberg_export_s3_roundtrip.js b/hypaware-core/smoke/flows/iceberg_export_s3_roundtrip.js index a569d66c..43a7cdce 100644 --- a/hypaware-core/smoke/flows/iceberg_export_s3_roundtrip.js +++ b/hypaware-core/smoke/flows/iceberg_export_s3_roundtrip.js @@ -101,7 +101,7 @@ export async function run({ harness, expect }) { const { loaded, failed } = await loadManifests([parquetDir, s3Dir, icebergDir, fixtureDir]) if (failed.length > 0) { throw new Error( - `iceberg_export_s3_roundtrip: manifest failures — ${failed + `iceberg_export_s3_roundtrip: manifest failures - ${failed .map((f) => `${f.manifestPath}: ${f.message}`) .join('; ')}` ) @@ -189,7 +189,7 @@ export async function run({ harness, expect }) { }) // Settle the spool so the fixture's rows are committed to their - // routed `source=...` partition table before discovery — appendRows + // routed `source=...` partition table before discovery. appendRows // only schedules a background flush at the size threshold. await kernel.storage.flushAll({ force: true }) diff --git a/hypaware-core/smoke/flows/join_flow_remote_config.js b/hypaware-core/smoke/flows/join_flow_remote_config.js index d41f070f..38752749 100644 --- a/hypaware-core/smoke/flows/join_flow_remote_config.js +++ b/hypaware-core/smoke/flows/join_flow_remote_config.js @@ -17,7 +17,7 @@ import { dispatch } from '../../../src/core/cli/dispatch.js' /** * Join-flow smoke (LLP 0025): drives the full remote-config lifecycle - * against a stub central server — + * against a stub central server: * * join (seed write) → seed boot → identity bootstrap → config pull * (200) → kernel apply → staged restart → relaunch on the served @@ -29,7 +29,7 @@ import { dispatch } from '../../../src/core/cli/dispatch.js' * service manager would. * * Under layering (LLP 0031) the seed is the **central** layer, written - * to config-control/ — never to the user's `hypaware-config.json`. This + * to config-control/, never to the user's `hypaware-config.json`. This * smoke seeds a pre-existing local config to prove `join` leaves it * intact (#111) and that its colliding entry drops at the boot-time * merge. @@ -44,14 +44,14 @@ import { dispatch } from '../../../src/core/cli/dispatch.js' * rows, `join.run` span. * * @param {{ harness: any, expect: any }} args - * @ref LLP 0025#the-join-sequence [tests] — seed → bootstrap → pull → apply → restart → operational, end to end against a stub server - * @ref LLP 0031#physical-layout [tests] — join writes the central seed (not the local layer); boot merges central ⊕ local + * @ref LLP 0025#the-join-sequence [tests]: seed → bootstrap → pull → apply → restart → operational, end to end against a stub server + * @ref LLP 0031#physical-layout [tests]: join writes the central seed (not the local layer); boot merges central ⊕ local */ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'join_flow_remote_config: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'join_flow_remote_config: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -95,7 +95,7 @@ export async function run({ harness, expect }) { // A pre-existing local install (LLP 0031 / #111): `join` must not // touch this file. Its `@hypaware/central` entry collides with the - // central layer, so it is dropped at the boot-time merge — surfaced + // central layer, so it is dropped at the boot-time merge, surfaced // as a structured `config.local_entry_dropped` log asserted below. const localConfig = { version: 2, plugins: [{ name: '@hypaware/central' }] } await fs.writeFile(localConfigPath, JSON.stringify(localConfig, null, 2) + '\n') diff --git a/hypaware-core/smoke/flows/local_parquet_export.js b/hypaware-core/smoke/flows/local_parquet_export.js index 5db011ca..fb8eaf94 100644 --- a/hypaware-core/smoke/flows/local_parquet_export.js +++ b/hypaware-core/smoke/flows/local_parquet_export.js @@ -15,16 +15,16 @@ import { dispatch } from '../../../src/core/cli/dispatch.js' const HERE = path.dirname(fileURLToPath(import.meta.url)) /** - * Phase 7 smoke — Parquet export through the `@hypaware/local-fs` + + * Phase 7 smoke: Parquet export through the `@hypaware/local-fs` + * `@hypaware/format-parquet` pair under the daemon boot path, with a * CLI-driven forced tick and a failed-export → outbox path. * * Two sink instances are wired through the daemon's runtime: * - * - `good` — base dir is a clean tmp dir; the tick lands a + * - `good`: base dir is a clean tmp dir; the tick lands a * readable Parquet file under * `/logs/partition=all/partition=all.parquet`. - * - `broken` — a regular file is pre-staged at + * - `broken`: a regular file is pre-staged at * `/logs`, so `fs.mkdir(partitionDir, * recursive: true)` inside `local-fs.writeBlob` hits * ENOTDIR. The driver then routes the failed batch @@ -33,7 +33,7 @@ const HERE = path.dirname(fileURLToPath(import.meta.url)) * The forced tick is driven through `hyp sink force` so the CLI * surface is exercised, not just the in-process driver. Dispatch is * handed the daemon's runtime via `opts.kernel` because Phase 7 does - * not yet wire config-driven sink instantiation — the smoke owns the + * not yet wire config-driven sink instantiation. The smoke owns the * `kernel.sinks.instantiate` calls until that lands. * * Bead `hy-bbyi` assertions: @@ -55,7 +55,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'local_parquet_export: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'local_parquet_export: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -107,7 +107,7 @@ export async function run({ harness, expect }) { env: process.env, runId: harness.devRunId, // 60ms cadence so a few scheduled ticks fire during the smoke and - // the daemon's `sink.tick` span shows up in JSONL — the CLI force + // the daemon's `sink.tick` span shows up in JSONL: the CLI force // tick exercises the dispatcher path but does not emit `sink.tick` // itself. tickIntervalMs: 60, @@ -294,9 +294,9 @@ export async function run({ harness, expect }) { (rows) => Array.isArray(rows) && rows.length >= 1, ) - // The encoder.encode_parquet span is what produces the bytes — both + // The encoder.encode_parquet span is what produces the bytes: both // the good and the broken instances open it (the broken one fails - // after — its failure surfaces during writeBlob, not encoding). + // after. Its failure surfaces during writeBlob, not encoding). const encodeSpans = traces.filter((/** @type {any} */ t) => t.name === 'encoder.encode_parquet') expect.that( 'traces: at least one encoder.encode_parquet span emitted', diff --git a/hypaware-core/smoke/flows/manifest_dep_resolve.js b/hypaware-core/smoke/flows/manifest_dep_resolve.js index 50ce9831..e38cadcc 100644 --- a/hypaware-core/smoke/flows/manifest_dep_resolve.js +++ b/hypaware-core/smoke/flows/manifest_dep_resolve.js @@ -32,7 +32,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'manifest_dep_resolve: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'manifest_dep_resolve: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/mcp_stdio_tools.js b/hypaware-core/smoke/flows/mcp_stdio_tools.js index 3eefc618..7adc918b 100644 --- a/hypaware-core/smoke/flows/mcp_stdio_tools.js +++ b/hypaware-core/smoke/flows/mcp_stdio_tools.js @@ -26,7 +26,7 @@ import { * - `tools/list` advertises the intrinsic `query_sql` tool; * - `tools/call query_sql` over the seeded cache returns the right rows; * - `resources/list` exposes the `ai_gateway_messages` dataset schema; - * - **stdout is protocol-clean** — every line parses as JSON-RPC, proving + * - **stdout is protocol-clean**: every line parses as JSON-RPC, proving * the host never leaked human text onto the protocol channel * (LLP 0034 §stdio-stdout-discipline); * - a `mcp.serve_start` log records the lifecycle (log-driven-development). diff --git a/hypaware-core/smoke/flows/otel_listener_writes_rows.js b/hypaware-core/smoke/flows/otel_listener_writes_rows.js index 67672a9a..4b30362d 100644 --- a/hypaware-core/smoke/flows/otel_listener_writes_rows.js +++ b/hypaware-core/smoke/flows/otel_listener_writes_rows.js @@ -32,13 +32,13 @@ import { loadManifests } from '../../../src/core/manifest.js' * - traces: at least one `cache.append` for `hyp_dataset=logs` * * @param {{ harness: any, expect: any }} args - * @ref LLP 0012#source-kinds [tests] — OTLP receiver source ingests a signal and writes cache rows + * @ref LLP 0012#source-kinds [tests]: OTLP receiver source ingests a signal and writes cache rows */ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'otel_listener_writes_rows: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'otel_listener_writes_rows: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -257,7 +257,7 @@ export async function run({ harness, expect }) { * in the log record's `attributes`. The harness pipes the run id * through so the smoke can `select count(*) ... where dev_run_id=...` * without colliding with rows from a previous smoke that shared the - * same on-disk cache (we don't — but the assertion is still strict). + * same on-disk cache (we don't, but the assertion is still strict). * * @param {string} devRunId */ diff --git a/hypaware-core/smoke/flows/otel_loopback_capture.js b/hypaware-core/smoke/flows/otel_loopback_capture.js index 7e9be537..72e47190 100644 --- a/hypaware-core/smoke/flows/otel_loopback_capture.js +++ b/hypaware-core/smoke/flows/otel_loopback_capture.js @@ -11,7 +11,7 @@ import { runDaemon } from '../../../src/core/daemon/runtime.js' import { dispatch } from '../../../src/core/cli/dispatch.js' /** - * Phase 7 smoke — OTLP HTTP listener acceptance plus daemon + * Phase 7 smoke: OTLP HTTP listener acceptance plus daemon * self-telemetry capture into local storage, under the daemon boot * path. * @@ -25,11 +25,11 @@ import { dispatch } from '../../../src/core/cli/dispatch.js' * * - **Daemon self-telemetry loopback.** With `HYP_DEV_TELEMETRY=1` * the kernel's JSONL exporter records every daemon-emitted span - * into `/dev-telemetry/` — that's the "local storage" the + * into `/dev-telemetry/`: that's the "local storage" the * bead asks for. The smoke asserts the expected spans landed: * `source.start` (otlp), `sink.tick`, `daemon.shutdown`. No OTLP * loopback into the cache is required (the kernel's exporters are - * JSONL-vs-OTLP mutually exclusive — see + * JSONL-vs-OTLP mutually exclusive: see * `src/core/observability/tracer.js`). * * @param {{ harness: any, expect: any }} args @@ -38,7 +38,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'otel_loopback_capture: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'otel_loopback_capture: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -68,7 +68,7 @@ export async function run({ harness, expect }) { }) // Snapshot reports the bound otel host/port via the source's - // `status().details` — that's how external OTLP clients discover it. + // `status().details`: that's how external OTLP clients discover it. const otelDetails = /** @type {{ listen_host: string, listen_port: number }} */ ( handle.snapshot().sources.find((s) => s.name === 'otlp')?.details ) @@ -154,7 +154,7 @@ export async function run({ harness, expect }) { (arr) => Array.isArray(arr) && arr.length >= 3 && arr.every((v) => v === harness.devRunId), ) - // sentinel — ports got reaped on stop so a follow-up listener could + // sentinel: ports got reaped on stop so a follow-up listener could // re-bind cleanly if a downstream phase wants it. const probe = await canBind(otelDetails.listen_host, otelDetails.listen_port) expect.that( diff --git a/hypaware-core/smoke/flows/package_bin_boot.js b/hypaware-core/smoke/flows/package_bin_boot.js index 12ac2615..006977c5 100644 --- a/hypaware-core/smoke/flows/package_bin_boot.js +++ b/hypaware-core/smoke/flows/package_bin_boot.js @@ -25,7 +25,7 @@ import { * * Subprocesses inherit the harness's `HYP_HOME` so their telemetry * lands in the same tmpdir we shut down here. We only assert against - * exit codes and the pack manifest — the dispatched subprocess's + * exit codes and the pack manifest: the dispatched subprocess's * spans are exercised by their own smokes. * * @param {{ harness: any, expect: any }} args @@ -34,7 +34,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'package_bin_boot: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'package_bin_boot: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -65,7 +65,7 @@ export async function run({ harness, expect }) { expect.that( 'hypaware --help prints the usage header', helpResult.stdout, - (v) => typeof v === 'string' && v.includes('hyp — HypAware kernel CLI') + (v) => typeof v === 'string' && v.includes('hyp - HypAware kernel CLI') ) const smokeResult = spawnSync( diff --git a/hypaware-core/smoke/flows/plugin_authoring_roundtrip.js b/hypaware-core/smoke/flows/plugin_authoring_roundtrip.js index 1c8e652a..330b47cd 100644 --- a/hypaware-core/smoke/flows/plugin_authoring_roundtrip.js +++ b/hypaware-core/smoke/flows/plugin_authoring_roundtrip.js @@ -15,7 +15,7 @@ import { createKernelRuntime } from '../../../src/core/runtime/activation.js' /** * Plugin authoring loop smoke. Drives the exact CLI an author/agent - * uses — `hyp plugin new` then `hyp plugin doctor` — through the + * uses (`hyp plugin new` then `hyp plugin doctor`) through the * dispatcher in a temp HYP_HOME, and proves the doctor's headline * check actually fires: * @@ -33,7 +33,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'plugin_authoring_roundtrip: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'plugin_authoring_roundtrip: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -80,7 +80,7 @@ export async function run({ harness, expect }) { .then(() => true, () => false) expect.that('scaffold wrote a manifest', manifestExists, (v) => v === true) - // 2. Doctor the clean scaffold — should be green. + // 2. Doctor the clean scaffold: should be green. const okOut = makeBuf() const okCode = await runRoot( 'smoke.driver', @@ -109,7 +109,7 @@ export async function run({ harness, expect }) { ) // 3. Scaffold a *separate* plugin, strip its register() call, and - // doctor it — should fail with the headline diagnostic. A fresh + // doctor it: should fail with the headline diagnostic. A fresh // directory is required: ESM import() caches by URL, so re-importing // the already-doctored `widget` would return its clean module. const brokenNewCode = await dispatch( diff --git a/hypaware-core/smoke/flows/plugin_boot_installed.js b/hypaware-core/smoke/flows/plugin_boot_installed.js index f971f5e5..3b6b8e3c 100644 --- a/hypaware-core/smoke/flows/plugin_boot_installed.js +++ b/hypaware-core/smoke/flows/plugin_boot_installed.js @@ -17,7 +17,7 @@ import { defaultConfigPath } from '../../../src/core/config/schema.js' * installed third-party plugin from a local bare git repo and one * config file activating it. The fixture plugin contributes a single * command (`hyp installed-greet`) so the smoke can prove the boot - * loaded the installed manifest into the merged pool — not just that + * loaded the installed manifest into the merged pool, not just that * `plugin list` saw the lock entry. * * Acceptance points from the bead: @@ -38,7 +38,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'plugin_boot_installed: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'plugin_boot_installed: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/plugin_install_git_url.js b/hypaware-core/smoke/flows/plugin_install_git_url.js index 5680a644..311c0f2e 100644 --- a/hypaware-core/smoke/flows/plugin_install_git_url.js +++ b/hypaware-core/smoke/flows/plugin_install_git_url.js @@ -23,7 +23,7 @@ import { * Hy-gh-1 smoke. Builds a local bare git repo containing a tiny * `@hypaware/git-url-fixture` plugin and installs it through the CLI * dispatcher via `file://` so the install exercises the - * SAME git fetch path remote URLs would take — there is no + * SAME git fetch path remote URLs would take. There is no * special-case for `file://`. After the install we verify: * * - `plugin-lock.json` has a `source.kind="git"` entry. @@ -40,7 +40,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'plugin_install_git_url: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'plugin_install_git_url: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -300,7 +300,7 @@ export async function run({ harness, expect }) { /** * Drop a minimal fixture plugin under `dir`. The shape mirrors a tiny * remote-installable plugin: a manifest and an entrypoint, nothing - * else. The smoke does not activate the plugin — installing it + * else. The smoke does not activate the plugin, but installing it * through the git path is enough to exercise the §hy-gh-1 contract. * * @param {string} dir diff --git a/hypaware-core/smoke/flows/plugin_install_github_url.js b/hypaware-core/smoke/flows/plugin_install_github_url.js index ab0bfe7b..654aa205 100644 --- a/hypaware-core/smoke/flows/plugin_install_github_url.js +++ b/hypaware-core/smoke/flows/plugin_install_github_url.js @@ -22,9 +22,9 @@ import { pluginLockPath } from '../../../src/core/plugin_install/paths.js' * HYP_SMOKE_REAL_GITHUB=1 npm run smoke -- plugin_install_github_url * * Defaults: - * HYP_SMOKE_GITHUB_URL — git URL of a tiny public plugin fixture - * HYP_SMOKE_GITHUB_REF — pinned tag (or commit SHA) to install - * HYP_SMOKE_GITHUB_NAME — manifest name the install should report + * HYP_SMOKE_GITHUB_URL: git URL of a tiny public plugin fixture + * HYP_SMOKE_GITHUB_REF: pinned tag (or commit SHA) to install + * HYP_SMOKE_GITHUB_NAME: manifest name the install should report * * The smoke skips silently with a clear marker line when the env var * is not set so CI runs of `npm run smoke` do not hit the network. @@ -48,7 +48,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'plugin_install_github_url: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'plugin_install_github_url: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/plugin_install_local_dir.js b/hypaware-core/smoke/flows/plugin_install_local_dir.js index 8eb31253..eded489a 100644 --- a/hypaware-core/smoke/flows/plugin_install_local_dir.js +++ b/hypaware-core/smoke/flows/plugin_install_local_dir.js @@ -40,7 +40,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'plugin_install_local_dir: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'plugin_install_local_dir: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -57,8 +57,8 @@ export async function run({ harness, expect }) { // Drive the install through the CLI dispatcher so we exercise the // exact path `hyp plugin install ./hypaware-core/plugins-workspace/dummy-a` - // takes — argv parsing, dispatch span, plugin.install + plugin.update_check - // span emission, lock writes — in one shot. + // takes (argv parsing, dispatch span, plugin.install + plugin.update_check + // span emission, lock writes) in one shot. const installStdout = makeBuf() const installStderr = makeBuf() const installCode = await runRoot( @@ -261,7 +261,7 @@ export async function run({ harness, expect }) { * Drop a minimal fixture plugin under `dir`. The shape mirrors what * Phase 8 plugins will commit to `hypaware-core/plugins-workspace//`: * one manifest and one entrypoint. The smoke does not activate the - * plugin — installing it is enough to exercise the §Phase 7 + * plugin, installing it is enough to exercise the §Phase 7 * resolver/fetch/lock/update_check contract. * * @param {string} dir diff --git a/hypaware-core/smoke/flows/s3_query_roundtrip.js b/hypaware-core/smoke/flows/s3_query_roundtrip.js index 36683d57..ec102c40 100644 --- a/hypaware-core/smoke/flows/s3_query_roundtrip.js +++ b/hypaware-core/smoke/flows/s3_query_roundtrip.js @@ -78,7 +78,7 @@ const QUERY_SOURCES = [ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { - throw new Error('s3_query_roundtrip: tracer provider not installed — expected HYP_DEV_TELEMETRY=1') + throw new Error('s3_query_roundtrip: tracer provider not installed - expected HYP_DEV_TELEMETRY=1') } const cacheRoot = path.join(harness.stateDir, 'cache') @@ -163,7 +163,7 @@ export async function run({ harness, expect }) { const { loaded, failed } = await loadManifests([parquetDir, s3Dir, icebergDir, fixtureDir]) if (failed.length > 0) { throw new Error( - `s3_query_roundtrip: manifest failures — ${failed.map((f) => `${f.manifestPath}: ${f.message}`).join('; ')}` + `s3_query_roundtrip: manifest failures - ${failed.map((f) => `${f.manifestPath}: ${f.message}`).join('; ')}` ) } /** @type {PluginActivationEntry[]} */ diff --git a/hypaware-core/smoke/flows/s3_sink_export_fixture.js b/hypaware-core/smoke/flows/s3_sink_export_fixture.js index c9cd3894..353b31a5 100644 --- a/hypaware-core/smoke/flows/s3_sink_export_fixture.js +++ b/hypaware-core/smoke/flows/s3_sink_export_fixture.js @@ -48,7 +48,7 @@ const PREFIX = 'hypaware' * appears in any captured log, span, or metric attribute. * * The injection seam is `sinkCtx.config.__clientFactory`. Production - * configs never carry this key — it lives outside the validated config + * configs never carry this key: it lives outside the validated config * shape so it cannot be set via JSON config files. * * @param {{ harness: any, expect: any }} args @@ -56,10 +56,10 @@ const PREFIX = 'hypaware' export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { - throw new Error('s3_sink_export_fixture: tracer provider not installed — expected HYP_DEV_TELEMETRY=1') + throw new Error('s3_sink_export_fixture: tracer provider not installed - expected HYP_DEV_TELEMETRY=1') } if (!obs.meter.provider) { - throw new Error('s3_sink_export_fixture: meter provider not installed — expected HYP_DEV_TELEMETRY=1') + throw new Error('s3_sink_export_fixture: meter provider not installed - expected HYP_DEV_TELEMETRY=1') } const cacheRoot = path.join(harness.stateDir, 'cache') @@ -89,7 +89,7 @@ export async function run({ harness, expect }) { const { loaded, failed } = await loadManifests([parquetDir, s3Dir, fixtureDir]) if (failed.length > 0) { throw new Error( - `s3_sink_export_fixture: manifest failures — ${failed + `s3_sink_export_fixture: manifest failures - ${failed .map((f) => `${f.manifestPath}: ${f.message}`) .join('; ')}` ) @@ -130,7 +130,7 @@ export async function run({ harness, expect }) { // Build the fake S3 client. The factory records every PutObject input // so we can assert on bucket/key/body content downstream, and returns // a synthetic `ETag` to mimic AWS SDK behavior. The factory itself - // is the injection seam — production never carries `__clientFactory`. + // is the injection seam. Production never carries `__clientFactory`. /** @type {Array<{ Bucket: string, Key: string, Body: Uint8Array, ContentType?: string, StorageClass?: string }>} */ const recordedPuts = [] const fakeClientFactory = async (opts) => { diff --git a/hypaware-core/smoke/flows/sink_export_driver.js b/hypaware-core/smoke/flows/sink_export_driver.js index ec122b4b..d1654c1b 100644 --- a/hypaware-core/smoke/flows/sink_export_driver.js +++ b/hypaware-core/smoke/flows/sink_export_driver.js @@ -21,12 +21,12 @@ import { createSinkDriver } from '../../../src/core/sinks/driver.js' /** * Phase 5 smoke. Stands up: * - * - `@hypaware/test-encoder` — provides `hypaware.encoder@1` with a + * - `@hypaware/test-encoder`: provides `hypaware.encoder@1` with a * trivial CSV-ish encoder. - * - `@hypaware/test-fs` — provides `hypaware.blob-store@1` and + * - `@hypaware/test-fs`: provides `hypaware.blob-store@1` and * registers a sink contribution `local-fs` that writes encoded * partition bytes to `/`. - * - `@hypaware/test-fixture` — registers the `dummy_rows` dataset and + * - `@hypaware/test-fixture`: registers the `dummy_rows` dataset and * materializes 50 rows into the cache so the driver has something to * hand to the sink. * @@ -46,12 +46,12 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'sink_export_driver: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'sink_export_driver: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } if (!obs.meter.provider) { throw new Error( - 'sink_export_driver: meter provider not installed — expected HYP_DEV_TELEMETRY=1' + 'sink_export_driver: meter provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -146,7 +146,7 @@ export async function run({ harness, expect }) { }) // Fire the tick at a minute boundary so `"* * * * *"` matches in - // every field — the cron evaluator otherwise needs no help here. + // every field (the cron evaluator otherwise needs no help here). const report = await driver.tick({ now: new Date('2026-02-15T10:00:00Z') }) expect.that( @@ -328,8 +328,8 @@ const encoder = { async encodePartition(partition, ctx) { const datasetTag = partition.dataset || 'unknown' const partitionTag = Object.entries(partition.partition || {}).map(([k, v]) => k + '=' + v).join(',') || 'all' - // Trivial fixed payload — the smoke is testing the driver, not the - // encoder's row fidelity. 50 lines so bytes_written > 0 unambiguously. + // Trivial fixed payload (the smoke is testing the driver, not the + // encoder's row fidelity). 50 lines so bytes_written > 0 unambiguously. const lines = [] for (let i = 0; i < 50; i++) { lines.push(datasetTag + ',' + partitionTag + ',' + i + ',v' + i) @@ -413,7 +413,7 @@ export async function activate(ctx) { await fs.writeFile(out, blob.bytes) bytesWritten += blob.bytes.byteLength } else { - // Streaming case — assemble into a single write for the + // Streaming case: assemble into a single write for the // smoke; production blob stores would pipe instead. const chunks = [] for await (const chunk of blob.bytes) { diff --git a/hypaware-core/smoke/flows/status_diagnostics.js b/hypaware-core/smoke/flows/status_diagnostics.js index 4eb218ba..3bc232ad 100644 --- a/hypaware-core/smoke/flows/status_diagnostics.js +++ b/hypaware-core/smoke/flows/status_diagnostics.js @@ -15,7 +15,7 @@ import { resolveDependencies } from '../../../src/core/dep_graph.js' /** * Phase 8 status diagnostics smoke. Drives `hyp status` against two - * configs — one healthy, one broken — and validates that: + * configs, one healthy and one broken, and validates that: * * 1. Healthy config reports `overall=healthy`, no error diagnostics, * and the printed status lists the expected sources/sinks/clients @@ -35,7 +35,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'status_diagnostics: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'status_diagnostics: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -66,7 +66,7 @@ export async function run({ harness, expect }) { } const otelConfig = { listen_host: '127.0.0.1', listen_port: 0 } - // Avoid mutating the real HOME under tests — every probe in the + // Avoid mutating the real HOME under tests: every probe in the // status collector walks $HOME for client settings files. Swap in a // tmp HOME so the smoke never picks up the developer's attach state. const fakeHome = path.join(harness.tmpDir, 'home') @@ -224,7 +224,7 @@ export async function run({ harness, expect }) { ) ) - // V1 contract — no `central` or `gascity` references in JSON. + // V1 contract: no `central` or `gascity` references in JSON. const okJsonText = JSON.stringify(okJson) expect.that( 'healthy json: contains no @hypaware/central reference', @@ -249,7 +249,7 @@ export async function run({ harness, expect }) { await writeJson(badConfigPath, { version: 2, plugins: [ - // Claude enabled but ai-gateway intentionally omitted — the + // Claude enabled but ai-gateway intentionally omitted: the // status collector should surface client_without_gateway plus // the gateway_missing_anthropic_upstream warning. { name: '@hypaware/claude', config: { proxy: '@hypaware/ai-gateway' } }, diff --git a/hypaware-core/smoke/flows/vector_search_local_fixture.js b/hypaware-core/smoke/flows/vector_search_local_fixture.js index 68beb5f7..8cb31989 100644 --- a/hypaware-core/smoke/flows/vector_search_local_fixture.js +++ b/hypaware-core/smoke/flows/vector_search_local_fixture.js @@ -39,16 +39,16 @@ const COLUMNS = [{ name: 'text', type: 'STRING', nullable: true }] * the real CLI dispatch path, and the daemon-style refresh source. * * Steps (each a `smoke_step`-tagged root span): - * 1. populate — seed two cache partitions with distinct texts. - * 2. plugin_activate — activate both plugins against the fake server. - * 3. status_missing — `hyp vector status --json` reports missing shards. - * 4. first_search — `hyp vector search` auto-builds both shards and + * 1. populate: seed two cache partitions with distinct texts. + * 2. plugin_activate: activate both plugins against the fake server. + * 3. status_missing: `hyp vector status --json` reports missing shards. + * 4. first_search: `hyp vector search` auto-builds both shards and * ranks the matching text first. - * 5. staleness — appending rows flips one shard to stale_rows. - * 6. timer_refresh — the `vector-search-refresh` source tick rebuilds + * 5. staleness: appending rows flips one shard to stale_rows. + * 6. timer_refresh: the `vector-search-refresh` source tick rebuilds * exactly the stale shard. - * 7. no_refresh_search — `--no-refresh` finds the newly indexed row. - * 8. telemetry — spans prove the build/search/tick paths ran, and + * 7. no_refresh_search: `--no-refresh` finds the newly indexed row. + * 8. telemetry: spans prove the build/search/tick paths ran, and * neither the API key nor raw text leaked. * * @param {{ harness: any, expect: any }} args @@ -130,7 +130,7 @@ export async function run({ harness, expect }) { path.join(PLUGINS_WORKSPACE, 'vector-search'), ]) if (failed.length > 0) { - throw new Error(`manifest failures — ${failed.map((f) => `${f.manifestPath}: ${f.message}`).join('; ')}`) + throw new Error(`manifest failures - ${failed.map((f) => `${f.manifestPath}: ${f.message}`).join('; ')}`) } // Embedder first: vector-search requires hypaware.embedder. const byName = new Map(loaded.map((l) => [l.manifest.name, l])) @@ -150,7 +150,7 @@ export async function run({ harness, expect }) { config: { indexes: [{ dataset: DATASET, column: 'text' }], // ~120ms so the timer step is fast; the budget fields keep - // their defaults — this smoke never approaches them. + // their defaults, this smoke never approaches them. refresh: { interval_minutes: 0.002 }, }, }, diff --git a/hypaware-core/smoke/flows/walkthrough_backfill_client_history.js b/hypaware-core/smoke/flows/walkthrough_backfill_client_history.js index 90dca623..5eb64972 100644 --- a/hypaware-core/smoke/flows/walkthrough_backfill_client_history.js +++ b/hypaware-core/smoke/flows/walkthrough_backfill_client_history.js @@ -14,7 +14,7 @@ import { loadManifests } from '../../../src/core/manifest.js' import { resolveDependencies } from '../../../src/core/dep_graph.js' /** - * Phase 7 smoke — full onboarding walkthrough with the client-history + * Phase 7 smoke: full onboarding walkthrough with the client-history * backfill step. * * Boots `@hypaware/ai-gateway` + `@hypaware/claude` + `@hypaware/codex` @@ -38,7 +38,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'walkthrough_backfill_client_history: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'walkthrough_backfill_client_history: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/walkthrough_picker_to_first_query.js b/hypaware-core/smoke/flows/walkthrough_picker_to_first_query.js index ef0c97cb..13321fb7 100644 --- a/hypaware-core/smoke/flows/walkthrough_picker_to_first_query.js +++ b/hypaware-core/smoke/flows/walkthrough_picker_to_first_query.js @@ -55,7 +55,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'walkthrough_picker_to_first_query: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'walkthrough_picker_to_first_query: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } diff --git a/hypaware-core/smoke/flows/walkthrough_to_first_query.js b/hypaware-core/smoke/flows/walkthrough_to_first_query.js index a8e343e0..615a4cc2 100644 --- a/hypaware-core/smoke/flows/walkthrough_to_first_query.js +++ b/hypaware-core/smoke/flows/walkthrough_to_first_query.js @@ -56,7 +56,7 @@ export async function run({ harness, expect }) { const obs = installObservability() if (!obs.tracer.provider) { throw new Error( - 'walkthrough_to_first_query: tracer provider not installed — expected HYP_DEV_TELEMETRY=1' + 'walkthrough_to_first_query: tracer provider not installed - expected HYP_DEV_TELEMETRY=1' ) } @@ -76,7 +76,7 @@ export async function run({ harness, expect }) { claude: path.join(pluginsRoot, 'claude'), } - // Plugins listen on ephemeral ports for the smoke — the preset's + // Plugins listen on ephemeral ports for the smoke. The preset's // written config carries the standard defaults (8787, 4318) but the // running test instances use port 0 so multiple smoke runs do not // collide. The golden assertion below checks the written config, @@ -224,7 +224,7 @@ export async function run({ harness, expect }) { ) const gatewayUrl = gatewayApi.localEndpoint() - // POST one OTLP log payload — `attributes.dev_run_id` is what the + // POST one OTLP log payload: `attributes.dev_run_id` is what the // SQL assertion below counts on. const otlpPayload = buildOtlpLogPayload(harness.devRunId) const otlpResponse = await fetch( diff --git a/hypaware-core/smoke/index.js b/hypaware-core/smoke/index.js index 0ec52718..75569c15 100755 --- a/hypaware-core/smoke/index.js +++ b/hypaware-core/smoke/index.js @@ -18,7 +18,7 @@ try { } catch (err) { const message = err instanceof Error ? err.message : String(err) process.stderr.write(`smoke ${flowName}: FAIL\n${message}\n`) - // @ts-ignore — `expect` errors attach a `detail` line + // @ts-ignore: `expect` errors attach a `detail` line if (err && err.detail) process.stderr.write(` ${err.detail}\n`) process.exit(1) } diff --git a/hypaware-core/smoke/lib/expect.js b/hypaware-core/smoke/lib/expect.js index 9aa8e749..86479234 100644 --- a/hypaware-core/smoke/lib/expect.js +++ b/hypaware-core/smoke/lib/expect.js @@ -43,7 +43,7 @@ async function readJsonlByPrefix(dir, prefix) { /** * Build the `expect` toolkit handed to each smoke flow. Each method * returns plain records read straight off disk so the flow can use - * ordinary array filters — exactly the language used in the design's + * ordinary array filters: exactly the language used in the design's * SQL assertions, ahead of `hyp query` existing. * * @param {{ telemetryDir: string, runId: string, smokeName: string }} ctx @@ -88,7 +88,7 @@ function assertionError(ctx, message, value, cause) { const err = new Error( `${message}\n value=${valuePreview}\n smoke=${ctx.smokeName} dev_run_id=${ctx.runId}\n telemetry=${ctx.telemetryDir}${causeLine}` ) - // @ts-ignore — attach context for the harness to surface + // @ts-ignore: attach context for the harness to surface err.detail = `re-open: ls ${ctx.telemetryDir}` return err } diff --git a/hypaware-core/smoke/lib/harness.js b/hypaware-core/smoke/lib/harness.js index 4999d796..5bed2722 100644 --- a/hypaware-core/smoke/lib/harness.js +++ b/hypaware-core/smoke/lib/harness.js @@ -21,8 +21,8 @@ import { makeExpect } from './expect.js' * - Dynamically imports `hypaware-core/smoke/flows/.js` and * invokes its exported `run({ harness, expect })`. * - * The harness intentionally does not auto-shutdown observability — - * each flow flushes via `installObservability().shutdown()` once it + * The harness intentionally does not auto-shutdown observability. + * Each flow flushes via `installObservability().shutdown()` once it * has emitted everything it needs to assert against. * * @param {string} name diff --git a/llp/0001-adopting-llp.plan.md b/llp/0001-adopting-llp.plan.md index 4dfcd4ae..5ac8ca4b 100644 --- a/llp/0001-adopting-llp.plan.md +++ b/llp/0001-adopting-llp.plan.md @@ -57,7 +57,7 @@ Note: the `grill-with-docs` / `improve-codebase-architecture` skills assume a - **Anchors:** heading slugs (`#token-strategy`) as default — they survive restructuring, which matters because these are living docs. Numbered anchors only for settled Spec docs. -- **`@ref` syntax:** `// @ref LLP NNNN#anchor — gloss` (≤80-char gloss). +- **`@ref` syntax:** `// @ref LLP NNNN#anchor: gloss` (≤80-char gloss). Relation types (`[implements]`, `[constrained-by]`, `[tests]`, `[explains]`) used where they sharpen agent retrieval, not mechanically. - **Living-doc rule:** update in place; `Superseded` or `tombstones/` + @@ -150,7 +150,7 @@ the agent workflow until then. A new `## Design docs (LLP)` section telling agents: LLPs live in `llp/`; read the ones tagged with the `Systems` you're touching before changing code; add -`@ref LLP NNNN#anchor — gloss` when implementing a non-obvious documented +`@ref LLP NNNN#anchor: gloss` when implementing a non-obvious documented decision; update the LLP (not just the code) when the design changes; never leave a stale `@ref`. diff --git a/src/core/cache/iceberg/resolver.js b/src/core/cache/iceberg/resolver.js index 429fb70d..3d33118f 100644 --- a/src/core/cache/iceberg/resolver.js +++ b/src/core/cache/iceberg/resolver.js @@ -60,7 +60,7 @@ export function tableUrlForDir(dir) { } /** - * Inverse of `tableUrlForDir` — resolves a `file://` URL or a relative + * Inverse of `tableUrlForDir` - resolves a `file://` URL or a relative * path back into an absolute filesystem path. * * @param {string} url diff --git a/src/core/cache/iceberg/schema.js b/src/core/cache/iceberg/schema.js index 4b9f0696..35ca88fc 100644 --- a/src/core/cache/iceberg/schema.js +++ b/src/core/cache/iceberg/schema.js @@ -11,8 +11,8 @@ * needs to create a table. * * Field ids start at 1 and are assigned in declaration order. The - * cache does not (yet) maintain id stability across schema evolution - * — V1 tables are append-only and re-created on schema change. + * cache does not (yet) maintain id stability across schema evolution: + * V1 tables are append-only and re-created on schema change. * * @param {readonly ColumnSpec[]} columns * @returns {Schema} @@ -35,7 +35,7 @@ export function icebergSchemaForColumns(columns) { /** * Reconcile a `ColumnSpec[]` with an existing Iceberg table schema so * subsequent appends keep field IDs stable. The result is the schema to - * use for writes — same fields as `icebergSchemaForColumns` but with IDs + * use for writes: same fields as `icebergSchemaForColumns` but with IDs * re-bound to the existing table. * * Rules: diff --git a/src/core/cache/iceberg/store.js b/src/core/cache/iceberg/store.js index 6d014646..a4b3ecc4 100644 --- a/src/core/cache/iceberg/store.js +++ b/src/core/cache/iceberg/store.js @@ -39,7 +39,7 @@ import { */ /** - * Reusable cache for the local IO pair. Constructed once per process — + * Reusable cache for the local IO pair. Constructed once per process, * the resolver/lister are pure functions over the filesystem so there's * no per-table state to worry about. * @@ -54,7 +54,7 @@ function getLocalIO() { /** * Tests reset the IO cache so `installObservability` resets are - * matched by a fresh resolver — keeps smoke-flow isolation honest. + * matched by a fresh resolver - keeps smoke-flow isolation honest. */ export function resetLocalIO() { cachedIO = null @@ -110,7 +110,7 @@ export async function appendRowsToTable(tablePath, columns, rows, options) { // Coerce/validate rows up front, before any metadata commit (table create // or schema evolution). A row-level rejection (null in a required column, a // bad numeric coercion) must abort BEFORE we durably advance the table's - // schema — otherwise evolveSchemaInPlace lands the new column and the append + // schema: otherwise evolveSchemaInPlace lands the new column and the append // then throws, leaving the table's schema ahead of its data. const records = rows.length > 0 ? rowsToIcebergRecords(columns, rows) : [] @@ -146,12 +146,12 @@ export async function appendRowsToTable(tablePath, columns, rows, options) { if (existingSpec) { validatePartitionSpecStability(declaration, existingSpec, effectiveSchema) } - // @ref LLP 0029#in-place-evolution [implements] — the single switch point. + // @ref LLP 0029#in-place-evolution [implements]: the single switch point. // effectiveSchema is the merged write schema; if it adds nullable columns - // or widens an existing column required→nullable that the table's current + // or widens an existing column required->nullable that the table's current // schema doesn't reflect yet, evolve the table in place (add-schema + // set-current-schema) so the append below lands under the new schema and - // the columns become queryable — no recreate, old rows read null. + // the columns become queryable: no recreate, old rows read null. if (existingSchema) { await evolveSchemaInPlace({ catalog, tableUrl: url, resolver, lister, existingSchema, effectiveSchema, @@ -176,8 +176,8 @@ export async function appendRowsToTable(tablePath, columns, rows, options) { * nullable columns the table doesn't have yet. Additive-only: by the time we * reach here, `mergeFieldIdsFromTable` has already rejected every breaking * change, so any delta between `existingSchema` and `effectiveSchema` is one or - * more new nullable fields (or a widened required→nullable). No-op when the two - * schemas carry the same field ids — the common case — so a steady-state append + * more new nullable fields (or a widened required->nullable). No-op when the two + * schemas carry the same field ids: the common case, so a steady-state append * pays only a cheap id-set comparison, no extra commit. * * Mechanism: stage `add-schema` (with the merged schema) + `set-current-schema` @@ -185,7 +185,7 @@ export async function appendRowsToTable(tablePath, columns, rows, options) { * commit. The subsequent `icebergAppend` reloads metadata, sees the new * current schema, and writes the new columns; pre-existing data files simply * lack the new field ids and icebird reads them back as `null`. This is the - * single place the merged schema actually reaches storage — historically it + * single place the merged schema actually reaches storage: historically it * was computed and discarded, so the new column never appeared without a full * cache recreate (issue #102). * @@ -196,7 +196,7 @@ export async function appendRowsToTable(tablePath, columns, rows, options) { * required fields without defaults) as a second guard behind * `mergeFieldIdsFromTable`. * - * @ref LLP 0029#reachable-path [implements] — fileCatalogCommit is the reachable + * @ref LLP 0029#reachable-path [implements]: fileCatalogCommit is the reachable * icebird primitive; icebergTransaction's tx has no schema method (icebird#25). * @param {object} options * @param {Catalog} options.catalog @@ -238,9 +238,9 @@ async function evolveSchemaInPlace({ catalog, tableUrl, resolver, lister, existi * kinds of additive delta, and both must trigger evolution: * * - a new field id `prior` lacks (a new nullable column), and - * - a shared field id whose `required` flag WIDENED (required → nullable): + * - a shared field id whose `required` flag WIDENED (required -> nullable): * the merge keeps the same id when only nullability flips, so an id-set - * check alone misses it — the table would stay marked `required` and a + * check alone misses it: the table would stay marked `required` and a * later append writing `null` would be rejected (issue #102 / LLP 0029 * lists widening as additive). * @@ -304,7 +304,7 @@ export async function* scanRowsFromTable(tablePath, columns) { /** * Build a squirreling-compatible `AsyncDataSource` over the latest * snapshot of the table. Returns `null` if the table does not exist - * yet or has no committed snapshot — the query layer treats that as + * yet or has no committed snapshot: the query layer treats that as * an empty table. * * @param {string} tablePath diff --git a/src/core/cache/maintenance.js b/src/core/cache/maintenance.js index 6c1d4571..a01b60e0 100644 --- a/src/core/cache/maintenance.js +++ b/src/core/cache/maintenance.js @@ -195,14 +195,14 @@ async function maintainSourceTable(r, cursor, cfg, opts, settle, snapshotsExpire } if (!opts.expireOnly) { - // @ref LLP 0027#re-settle-sweep — a partition holding a committed + // @ref LLP 0027#re-settle-sweep: a partition holding a committed // fallback row may carry a split twin pair the flush-time settle // never collapsed; force a rewrite so the sweep can re-settle it even // when the file-count heuristics say compaction isn't due. Gate that // force on NEW data having flushed since the last sweep (the live // data-file count moved off the recorded baseline): an unmatchable - // fallback — one whose transcript line never lands (harness aux, - // wire-only reminders) — would otherwise force a full rewrite every + // fallback: one whose transcript line never lands (harness aux, + // wire-only reminders) - would otherwise force a full rewrite every // tick forever. The cheap baseline check also lets us skip the // attributes scan entirely when nothing new has flushed. const hasResettle = settle @@ -255,7 +255,7 @@ async function maintainLegacyPartition(r, part, cursor, cfg, opts, settle, snaps } if (!opts.expireOnly) { - // @ref LLP 0027#re-settle-sweep — same backstop on the legacy layout, + // @ref LLP 0027#re-settle-sweep: same backstop on the legacy layout, // with the same new-data gate so an unmatchable fallback does not force // a rewrite every tick. const hasResettle = settle @@ -460,13 +460,13 @@ function estimateRowBytes(row) { /** * Compact a source-table partition by rewriting into a fresh table * directory. Iceberg metadata stores absolute `file://` URLs, so we - * cannot rename directories after writing — we write to a new dir + * cannot rename directories after writing: we write to a new dir * name and update the cursor to point to it. * * Rows are flushed to a data file whenever the batch reaches either * `COMPACT_BATCH_SIZE` rows or `cfg.compact_batch_bytes` estimated * bytes, whichever comes first. The byte cap keeps peak heap bounded - * regardless of per-row payload size — without it, a fat denormalized + * regardless of per-row payload size: without it, a fat denormalized * column (e.g. tool definitions repeated on every row) pushes a * 10k-row batch into the gigabytes and OOMs the daemon mid-compaction. * @@ -521,7 +521,7 @@ async function compactSourceTable(partitionDir, cursor, cfg, settle) { // Buffer committed fallback rows so the re-settle sweep can upgrade them // after the full partition has been seen (a fallback row may stream // before its native twin). Non-fallback rows emit immediately to keep - // peak heap bounded — settlement is rare and only touches the buffer. + // peak heap bounded: settlement is rare and only touches the buffer. /** @type {Record[]} */ const fallbackBuffer = [] const emittedPartIds = settle ? new Set() : null @@ -551,8 +551,8 @@ async function compactSourceTable(partitionDir, cursor, cfg, settle) { nullable: true, })) } - // @ref LLP 0027#re-settle-sweep — hold provisional fallback rows back; - // emit only after the sweep upgrades and de-twins them at end-of-scan. + // @ref LLP 0027#re-settle-sweep: hold provisional fallback rows back. + // Emit only after the sweep upgrades and de-twins them at end-of-scan. if (settle && isGatewayFallbackRow(row)) { fallbackBuffer.push(row) continue @@ -695,7 +695,7 @@ async function compactPartition(partitionDir, cursor, cfg, settle) { nullable: true, })) } - // @ref LLP 0027#re-settle-sweep — same backstop on the legacy layout. + // @ref LLP 0027#re-settle-sweep: same backstop on the legacy layout. if (settle && isGatewayFallbackRow(row)) { fallbackBuffer.push(row) continue @@ -745,8 +745,8 @@ async function compactPartition(partitionDir, cursor, cfg, settle) { * * Flush-time settlement (LLP 0027 "Decision") only collapses a * fallback/uuid twin pair when both rows land in the same flush batch. A - * fallback row that flushed alone — its transcript line not yet on disk, - * its uuid twin still in a later flush — commits unsettled, and the flush + * fallback row that flushed alone: its transcript line not yet on disk, + * its uuid twin still in a later flush: commits unsettled, and the flush * path can never revisit it. The twins share the Iceberg partition key * (`conversation_id`/`cwd`/`date`), so they always live in the SAME * partition; a single-partition compaction rewrite is therefore enough to @@ -757,10 +757,10 @@ async function compactPartition(partitionDir, cursor, cfg, settle) { /** * Resolve the per-partition settle context. Returns null unless the * caller threaded both a storage handle and a settle hook for this - * dataset — so every existing maintenance path (CLI, tests) stays a pure + * dataset, so every existing maintenance path (CLI, tests) stays a pure * compaction with no behavioural change. * - * @ref LLP 0027#re-settle-sweep — compaction-time settle became acceptable + * @ref LLP 0027#re-settle-sweep: compaction-time settle became acceptable * once the registry/storage handle is threaded in (LLP 0027 option B's * blocker). * @param {MaintenanceOptions} opts @@ -784,13 +784,13 @@ function resolveSettleContext(opts, dataset) { * it can match to its native transcript identity (re-stamping * `message_id`/`part_id`, clearing the fallback marker), leaving the * rest as provisional fallbacks. The hook NEVER drops a row, so an - * enricher miss or failure degrades safely — the row survives + * enricher miss or failure degrades safely: the row survives * unchanged for a later sweep. * * 2. **De-twin within the rewrite set.** A fallback that upgraded onto a * `part_id` already emitted from this same partition (its native twin, * which streamed as a normal non-fallback row) is the duplicate the - * race left behind — drop it; the native twin wins. The twins share + * race left behind: drop it. The native twin wins. The twins share * the partition key (`conversation_id`/`cwd`/`date`), so the twin is * guaranteed to be in this rewrite set: no committed-partition scan is * needed, and a row whose identity did NOT change can never collide @@ -803,7 +803,7 @@ function resolveSettleContext(opts, dataset) { * collides is dropped, and a second pass is a no-op because the survivor * is no longer a fallback. * - * @ref LLP 0027#re-settle-sweep — reuse the flush enricher; de-twin within the partition rewrite. + * @ref LLP 0027#re-settle-sweep: reuse the flush enricher. De-twin within the partition rewrite. * @param {Record[]} fallbackRows * @param {SettleContext} settle * @param {Set} emittedPartIds part_ids already emitted from this partition rewrite @@ -863,12 +863,12 @@ function rowPartId(row) { /** * Does the table hold at least one committed gateway fallback row? Used * to force a compaction rewrite even when the file-count heuristics say - * compaction isn't due — otherwise a split twin pair in a small, + * compaction isn't due: otherwise a split twin pair in a small, * never-compacted partition would never get re-settled. Scans only the * `attributes` column and short-circuits on the first hit, so the cost is * bounded and paid only for settle-eligible datasets. * - * @ref LLP 0027#re-settle-sweep — gate the sweep on a cheap fallback scan. + * @ref LLP 0027#re-settle-sweep: gate the sweep on a cheap fallback scan. * @param {string} tableDir * @returns {Promise} */ @@ -888,7 +888,7 @@ async function hasResettleCandidate(tableDir) { * A committed row is a re-settle candidate when it carries the gateway's * provisional-identity marker. This is the documented contract * (`attributes.gateway.identity_source === 'gateway_fallback'`, LLP 0027 - * "Decision") — a dataset-agnostic predicate, so the marker is the only + * "Decision") - a dataset-agnostic predicate, so the marker is the only * coupling between core compaction and the gateway plugin. Tolerates the * `attributes` column whether stored as an object or a JSON string. * @@ -949,7 +949,7 @@ async function cleanRetiredEpochs(cacheRoot) { * * Two cases are removed: * 1. A generation that carries a `.retired` marker older than the grace - * period — the normal "compaction succeeded, the previous generation + * period: the normal "compaction succeeded, the previous generation * can go" path. * 2. A generation that is NOT the live one named by the partition cursor * and is older than {@link ORPHAN_GRACE_MS}, even without a `.retired` @@ -995,7 +995,7 @@ async function walkForRetired(dir) { removed = true } } catch { - // no .retired marker or parse error — fall through to orphan check + // no .retired marker or parse error: fall through to orphan check } // Orphan sweep: a generation the cursor does not reference and that @@ -1007,7 +1007,7 @@ async function walkForRetired(dir) { fs.rmSync(full, { recursive: true, force: true }) } } catch { - // stat/remove race — skip, a later tick will retry + // stat/remove race: skip, a later tick will retry } } } else { diff --git a/src/core/cache/migrate.js b/src/core/cache/migrate.js index 7e0c2b38..2f7ce197 100644 --- a/src/core/cache/migrate.js +++ b/src/core/cache/migrate.js @@ -76,9 +76,9 @@ export async function migrateLegacyPartitions({ cacheRoot, force }) { /** * A partition needs migration unless it is already in the source-table - * layout (`source=` partition key). Everything else — direct Iceberg + * layout (`source=` partition key). Everything else - direct Iceberg * tables without cursors, bare table names (`proxy_messages_v4`, `all`), - * and the old `client=/date=` epoch layout — is legacy. + * and the old `client=/date=` epoch layout - is legacy. * * @param {CachePartitionMeta} partition * @returns {boolean} diff --git a/src/core/cache/paths.js b/src/core/cache/paths.js index 62d0f5d0..c7618313 100644 --- a/src/core/cache/paths.js +++ b/src/core/cache/paths.js @@ -10,7 +10,7 @@ const DATASETS_SEGMENT = 'datasets' * /datasets/// * * Plugins that materialize rows ask `ctx.storage` for `tablePath`s - * and never assemble paths themselves — but the storage and retention + * and never assemble paths themselves; but the storage and retention * layers need a stable on-disk convention so they can attribute spans * back to the originating dataset. * @@ -22,7 +22,7 @@ export function datasetsRoot(cacheRoot) { /** * Build the absolute `tablePath` for a dataset partition. The - * directory is **not** created here — `appendRows` and Iceberg's + * directory is **not** created here: `appendRows` and Iceberg's * writer handle creation on first commit. * * @param {string} cacheRoot diff --git a/src/core/cache/retention.js b/src/core/cache/retention.js index 69669fc9..69187e13 100644 --- a/src/core/cache/retention.js +++ b/src/core/cache/retention.js @@ -31,7 +31,7 @@ const DEFAULT_TIMESTAMP_COLUMNS = ['timestamp', 'created_at', 'recorded_at', 'da /** * @param {{ cacheRoot: string, config: RetentionConfig | undefined, getDataset?: (dataset: string) => Pick | undefined }} args - * @ref LLP 0013#retention-is-the-central-tradeoff [implements] — per-dataset window; rows past it are deleted permanently + * @ref LLP 0013#retention-is-the-central-tradeoff [implements]: per-dataset window; rows past it are deleted permanently */ export function createRetentionEnforcer({ cacheRoot, config, getDataset }) { const cfg = normalizeConfig(config) @@ -424,7 +424,7 @@ async function scanFileForExpiredRows(filePath, cutoffMs, resolver, timestampCol } } } catch { - // unreadable file — skip rather than block retention + // unreadable file: skip rather than block retention } return positions } @@ -508,7 +508,7 @@ async function loadDeletedPositions(metadata, resolver, dataFileMap) { /** * @param {RetentionConfig | undefined} config * @returns {Required> & { datasets: Record, wait_for_sink_ack: boolean }} - * @ref LLP 0013#open-question [explains] — wait_for_sink_ack is the parsed-but-unwired evict-on-ack vs evict-on-retention knob + * @ref LLP 0013#open-question [explains]: wait_for_sink_ack is the parsed-but-unwired evict-on-ack vs evict-on-retention knob */ function normalizeConfig(config) { const default_days = diff --git a/src/core/cache/spool.js b/src/core/cache/spool.js index eb45522f..f968ceed 100644 --- a/src/core/cache/spool.js +++ b/src/core/cache/spool.js @@ -189,14 +189,14 @@ export function createCacheSpool(args) { } /** - * Read every row currently pending in a table's spool — the rows that + * Read every row currently pending in a table's spool: the rows that * `append` wrote but `flushTable` has not yet committed to Iceberg. * Walks `active.jsonl` and every rotated `flush-*.jsonl`, parsing each * line's `{ version, columns, rows }` envelope and yielding the raw * `rows`. This is a read-only inspection surface; it never rotates, * removes, or advances flush progress, so it is safe to call alongside * live capture. Any error (missing dir, unreadable file, malformed - * line) degrades to skipping that file/line rather than throwing — the + * line) degrades to skipping that file/line rather than throwing: the * spool is provisional (LLP 0013) and a partial read is always better * than aborting the caller. * @@ -235,7 +235,7 @@ async function* readSpooledRows(tablePath) { // Mirror streamFlushFile's envelope-validity contract exactly: a // parseable envelope missing `columns` is malformed, and the flush // reader drops it (its rows never reach a committed partition). - // Backfill dedupe must skip the same rows — otherwise it would dedupe + // Backfill dedupe must skip the same rows: otherwise it would dedupe // against, and so refuse to materialize, rows flush will never commit. if ( !envelope || diff --git a/src/core/cache/storage.js b/src/core/cache/storage.js index 08e13568..c03d6a1d 100644 --- a/src/core/cache/storage.js +++ b/src/core/cache/storage.js @@ -56,7 +56,7 @@ export function resolveIcebergDir(tablePath) { * scans; the dispatcher hands the same instance to built-in commands. * * Every `appendRows` call is wrapped in a `cache.append` span carrying - * `hyp_dataset`, `row_count`, and `bytes_written` — the contract the + * `hyp_dataset`, `row_count`, and `bytes_written` - the contract the * Phase 4 smoke (and the SQL assertion in the implementation plan) * exercise. * @@ -66,7 +66,7 @@ export function resolveIcebergDir(tablePath) { * getSettleHook?: (dataset: string) => ((rows: Record[], ctx: { storage: ExtendedQueryStorageService }) => Promise[]>) | undefined, * }} args * @returns {ExtendedQueryStorageService} - * @ref LLP 0013#write-path-and-query [implements] — kernel-owned cache write path; every source row lands here + * @ref LLP 0013#write-path-and-query [implements]: kernel-owned cache write path; every source row lands here */ export function createQueryStorageService({ cacheRoot, getDeclaration, getSettleHook }) { if (!cacheRoot) throw new Error('createQueryStorageService: cacheRoot is required') @@ -79,7 +79,7 @@ export function createQueryStorageService({ cacheRoot, getDeclaration, getSettle cacheRoot, async appendChunk(tablePath, columns, rows) { const dataset = datasetForTablePath(cacheRoot, tablePath) ?? 'unknown' - // @ref LLP 0027#decision — flush-time settlement: the owning dataset + // @ref LLP 0027#decision: flush-time settlement: the owning dataset // may upgrade provisional (fallback) row identity and dedupe before // the batch is committed. Generic and optional; the hook itself // short-circuits cheaply when a batch has nothing to settle. @@ -297,7 +297,7 @@ export function createQueryStorageService({ cacheRoot, getDeclaration, getSettle return discoverCachePartitionsImpl(cacheRoot, scope) }, - // @ref LLP 0027#open-questions [implements] — read-only spool surface so + // @ref LLP 0027#open-questions [implements]: read-only spool surface so // `hyp backfill` can dedupe against rows captured live but not yet flushed, // which the committed-partition scan cannot see. Inspection-only: never // rotates or advances flush progress, so it is safe alongside live capture. diff --git a/src/core/cache/streaming-reader.js b/src/core/cache/streaming-reader.js index 93a0fa46..ba752857 100644 --- a/src/core/cache/streaming-reader.js +++ b/src/core/cache/streaming-reader.js @@ -15,14 +15,14 @@ export const BATCH_ROW_LIMIT = 100_000 /** * Read a rotated spool file as a stream, yielding batches of rows that * respect both a byte-size ceiling and a row-count ceiling. Partial - * trailing lines are left in the buffer — the caller should treat them + * trailing lines are left in the buffer: the caller should treat them * as data for the next read cycle (in practice the spool writer always * ends lines with `\n`, so a partial line means the file was truncated * or is still being written). * * Each emitted row is decorated with: - * - `_hyp_cache_row_id` — SHA-256 of the serialized row (stable dedup key) - * - `_hyp_cache_batch_id` — caller-supplied batch identifier + * - `_hyp_cache_row_id`: SHA-256 of the serialized row (stable dedup key) + * - `_hyp_cache_batch_id`: caller-supplied batch identifier * * Resume support: if `startOffset` > 0 the reader seeks past already- * flushed bytes and continues from there. After each yielded batch the diff --git a/src/core/cli/core_commands.js b/src/core/cli/core_commands.js index aab9a1fa..25fa0a5c 100644 --- a/src/core/cli/core_commands.js +++ b/src/core/cli/core_commands.js @@ -75,7 +75,7 @@ import { SCAFFOLD_KINDS, scaffoldPlugin } from '../plugin_doctor/scaffold.js' /** * Register the V1 core command set onto the supplied registry. These - * commands are NOT plugin contributions — they ship with the kernel + * commands are NOT plugin contributions: they ship with the kernel * (per Phase 3 plan §Built-In Commands and the V1 Parity Table). * * Phase 3 implementations are deliberately thin: each command emits the @@ -91,7 +91,7 @@ export function registerCoreCommands(registry) { registry.register(cmd) } // Project the intrinsic core verbs (query_sql) as CLI commands here too, - // so `hyp --help` — rendered before the kernel boots — lists `query sql`. + // so `hyp --help` (rendered before the kernel boots) lists `query sql`. // The kernel verb registry re-projects them idempotently during boot and // owns the MCP tool surface (LLP 0034 §verbs). for (const verb of CORE_VERBS) { @@ -400,9 +400,9 @@ function buildCoreCommands() { /** * `hyp status [--json]` * - * Renders the V1 install state — config path, daemon install/run + * Renders the V1 install state (config path, daemon install/run * state, active plugins, source/sink/client status, cache + retention - * window, recent error count — and any diagnostics + repair + * window, recent error count) and any diagnostics + repair * suggestions surfaced by the Phase 8 collector. * * Span: `status.render`. Attributes match the bead contract @@ -520,7 +520,7 @@ export function renderStatusJson({ report, clientNames, datasets, cacheRoot }) { exists: report.configExists, valid: report.configValid, }, - // V1 stable shape — array of `{name}` so consumers can pin keys + // V1 stable shape: array of `{name}` so consumers can pin keys // without needing to know the version. The collector currently // does not track per-plugin version (Phase 2 set version on each // entry but it was always 'unknown'); keeping the field reserved @@ -670,7 +670,7 @@ export function renderStatusText({ report, clientNames, datasets, cacheRoot, std stdout.write(' active plugins:\n') if (report.activePlugins.length === 0) { - stdout.write(' (none — no config or no plugins selected)\n') + stdout.write(' (none - no config or no plugins selected)\n') } else { for (const name of report.activePlugins) { stdout.write(` - ${name}${provenanceTag(report.layered, isCentralPlugin(report.layered, name))}\n`) @@ -688,7 +688,7 @@ export function renderStatusText({ report, clientNames, datasets, cacheRoot, std stdout.write(' sinks:\n') if (report.sinks.length === 0) { - stdout.write(' (none — keeping captured data local only)\n') + stdout.write(' (none - keeping captured data local only)\n') } else { for (const s of report.sinks) { stdout.write(` - ${s.instance} (${s.plugin}, ${s.kind})${provenanceTag(report.layered, isCentralSink(report.layered, s.instance))}\n`) @@ -727,8 +727,8 @@ export function renderStatusText({ report, clientNames, datasets, cacheRoot, std stdout.write(` recent errors: ${report.recentErrorCount}\n`) // Local entries the central layer overrides (LLP 0031): dropped at - // merge, listed here with their reason. Loud, but not an outage signal - // — the gateway runs fine on the central config. + // merge, listed here with their reason. Loud, but not an outage signal. + // The gateway runs fine on the central config. if (report.layered && (report.layered.drops.length > 0 || report.layered.centralQueryIgnored)) { stdout.write(' local config (not applied):\n') for (const d of report.layered.drops) { @@ -743,7 +743,7 @@ export function renderStatusText({ report, clientNames, datasets, cacheRoot, std } // Remote-config section appears only once the gateway has state to - // show — a never-joined install keeps the V1 status surface. + // show. A never-joined install keeps the V1 status surface. const rc = report.remoteConfig if (rc && (rc.runningEtag || rc.probation || rc.lastRollback || rc.badEtag)) { stdout.write(' remote config:\n') @@ -761,7 +761,7 @@ export function renderStatusText({ report, clientNames, datasets, cacheRoot, std // Client-action reconciler section (LLP 0036 / 0041). Appears only once a // backfill-on-join target is configured or a pass has run; a `failed` - // line is loud but informational — it never degrades `overall`. + // line is loud but informational. It never degrades `overall`. if (report.clientActions && report.clientActions.actions.length > 0) { stdout.write(' client actions:\n') for (const a of report.clientActions.actions) { @@ -799,13 +799,13 @@ export function renderStatusText({ report, clientNames, datasets, cacheRoot, std * that never joined (no central layer → the V1 surface is unchanged); * otherwise `[central · locked]` for entries the central layer owns and * `[local]` for the user's additive entries. Used for plugin, source, - * sink, and client lines — sources and clients inherit their owning + * sink, and client lines. Sources and clients inherit their owning * plugin's layer. * * @param {HypAwareStatusReport['layered']} layered * @param {boolean} isCentral * @returns {string} - * @ref LLP 0031#status-provenance [implements] — every active plugin/source/sink/client line tagged central·locked or local + * @ref LLP 0031#status-provenance [implements]: every active plugin/source/sink/client line tagged central·locked or local */ function provenanceTag(layered, isCentral) { if (!layered) return '' @@ -926,7 +926,7 @@ async function runQuerySchema(argv, ctx) { const schema = schemaForDataset(ctx.query, dataset) if (!schema) { ctx.stdout.write(`dataset: ${dataset}\n`) - ctx.stdout.write(' (no dataset registered — install a plugin that contributes it)\n') + ctx.stdout.write(' (no dataset registered - install a plugin that contributes it)\n') return 0 } ctx.stdout.write(renderSchema(dataset, schema)) @@ -1042,7 +1042,7 @@ async function runQueryMaintain(argv, ctx) { compactOnly, expireOnly, config: maintenanceConfig, - // @ref LLP 0027#re-settle-sweep — `hyp query maintain` re-settles + // @ref LLP 0027#re-settle-sweep: `hyp query maintain` re-settles // committed fallback rows too, so a manual sweep also closes the race. storage: ctx.storage, getSettleHook: (dataset) => ctx.query.getDataset(dataset)?.resettleBatch, @@ -1153,7 +1153,7 @@ function pluginStateDir(ctx) { * `buildPluginCatalog`, so config validation runs against the actual * declared capabilities rather than a hardcoded table. * - * Discovery failures are absorbed silently — `hyp config validate` + * Discovery failures are absorbed silently: `hyp config validate` * keeps working when the lock is missing or any installed manifest is * corrupt; the underlying discovery layer logs its own diagnostics. * @@ -1253,7 +1253,7 @@ function buildPluginInstallConfirm({ yes, ctx, headerKind }) { for (const w of warnings) stderr.write(`${w}\n`) stderr.write(summary) // Prompt on stderr so stdout stays parseable. We require both - // stderr and stdin to be a TTY before asking — piping either + // stderr and stdin to be a TTY before asking: piping either // direction means "no human watching, prompt is useless." const tty = isTty(stderr) && isTty(process.stdin) const ask = tty @@ -1271,7 +1271,7 @@ function buildPluginInstallConfirm({ yes, ctx, headerKind }) { * Parse `hyp plugin install [--ref ] [--path ] [--yes]`. * Flags accept both `--flag value` and `--flag=value` forms. The * function does NOT verify mutual exclusion of `--ref` with a URL - * fragment — that lives in the resolver so the same rule applies to + * fragment. That lives in the resolver so the same rule applies to * programmatic callers. * * @param {string[]} argv @@ -1299,7 +1299,7 @@ function parsePluginInstallArgs(argv) { if (value === undefined || value.startsWith('--')) { return { ok: false, code: 2, message: `flag ${arg} requires a value` } } - // Block `-X` style values too — `applyGitSourceFlags` enforces + // Block `-X` style values too: `applyGitSourceFlags` enforces // the same rule but rejecting at the CLI layer gives a friendlier // error before the install span opens. if (value.startsWith('-')) { @@ -1824,7 +1824,7 @@ async function runDaemonHelp(argv, ctx) { } /** - * `hyp daemon run --foreground [--config ]` — boot the kernel as a daemon and + * `hyp daemon run --foreground [--config ]`: boot the kernel as a daemon and * tend it in the current process until SIGTERM/SIGINT. Phase 3 * intentionally only supports `--foreground`; the detached run path * lands with the Phase 4 launchd/systemd installers, so a no-flag @@ -1906,7 +1906,7 @@ async function runDaemonStatus(argv, ctx) { ctx.stdout.write(' (none)\n') } else { for (const source of status.sources) { - ctx.stdout.write(` - ${source.name} (${source.plugin}): ${source.state}${source.error ? ' — ' + source.error : ''}\n`) + ctx.stdout.write(` - ${source.name} (${source.plugin}): ${source.state}${source.error ? ' - ' + source.error : ''}\n`) } } ctx.stdout.write(' sinks:\n') @@ -1941,7 +1941,7 @@ async function runDaemonStop(_argv, ctx) { } /** - * `hyp daemon restart` — restart the installed service if present, + * `hyp daemon restart`: restart the installed service if present, * otherwise fall back to a stop + operator-relaunch hint for the * foreground path. * @@ -1972,7 +1972,7 @@ async function runDaemonRestart(_argv, ctx) { } /** - * `hyp daemon install` — install the persistent platform service. + * `hyp daemon install`: install the persistent platform service. * Supports `--dry-run [--json]` to render the planned plist / unit * file without touching disk. * @@ -2039,7 +2039,7 @@ async function runDaemonInstall(argv, ctx) { } /** - * `hyp daemon uninstall` — remove the persistent service while + * `hyp daemon uninstall`: remove the persistent service while * leaving config, recordings, and logs in place. * * @param {string[]} argv @@ -2068,7 +2068,7 @@ async function runDaemonUninstall(argv, ctx) { } /** - * `hyp daemon start` — start (kickstart) the installed service. + * `hyp daemon start`: start (kickstart) the installed service. * * @param {string[]} argv * @param {CommandRunContext} ctx @@ -2177,11 +2177,11 @@ function parseDaemonRunArgs(argv) { /* ---------- mcp ---------- */ /** - * `hyp mcp` — serve this host's verbs as an MCP server. The tool surface is + * `hyp mcp`: serve this host's verbs as an MCP server. The tool surface is * assembled dynamically from the verbs the active plugins registered (LLP * 0034): a bare host offers `query_sql`; add `@hypaware/context-graph` and - * `graph_neighbors` appears. Local stdio is local-user trust — same as - * running `hyp query` at the terminal — so no auth and operator tools are + * `graph_neighbors` appears. Local stdio is local-user trust (same as + * running `hyp query` at the terminal), so no auth and operator tools are * exposed. * * stdout is the JSON-RPC channel; the lifecycle line and all logs go to @@ -2190,7 +2190,7 @@ function parseDaemonRunArgs(argv) { * @param {string[]} argv * @param {CommandRunContext} ctx * @returns {Promise} - * @ref LLP 0034#kernel-wide-not-server-only [implements] — a local gateway exposes its own active plugins' tools to a local AI client + * @ref LLP 0034#kernel-wide-not-server-only [implements]: a local gateway exposes its own active plugins' tools to a local AI client */ async function runMcp(argv, ctx) { const parsed = parseMcpArgv(argv) @@ -2228,7 +2228,7 @@ async function runMcp(argv, ctx) { transport: 'stdio', tool_count: tools.length, }) - // Lifecycle line to stderr — stdout is reserved for the protocol. + // Lifecycle line to stderr (stdout is reserved for the protocol). ctx.stderr.write(`hyp mcp: serving ${tools.length} tool(s) over stdio${tools.length ? ` (${tools.map((t) => t.name).join(', ')})` : ''}\n`) const stdin = /** @type {NodeJS.ReadableStream} */ (ctx.stdin ?? process.stdin) @@ -2303,7 +2303,7 @@ async function runVersion(_argv, ctx) { /* ---------- smoke ---------- */ /** - * `hyp smoke ` — internal developer command. + * `hyp smoke `: internal developer command. * * The smoke harness owns a fresh tmp `HYP_HOME` and installs its own * observability against that tmpdir. Installing observability in the @@ -2345,7 +2345,7 @@ async function runSmoke(argv, ctx) { /* ---------- sink ---------- */ /** - * `hyp sink` group landing — no default behavior, just usage. + * `hyp sink` group landing: no default behavior, just usage. * * @param {string[]} _argv * @param {CommandRunContext} ctx @@ -2363,11 +2363,11 @@ async function runSinkHelp(_argv, ctx) { * * Drives one tick of the sink driver immediately, bypassing each * sink's cron schedule. The optional `instance` argument restricts - * the tick to a single sink — useful when an operator just wants to + * the tick to a single sink (useful when an operator just wants to * flush one configured destination without waking the others. * * The driver writes the same `sink.export_batch` span and outbox - * artifacts it does on a scheduled tick — the only difference is the + * artifacts it does on a scheduled tick. The only difference is the * trigger. * * @param {string[]} argv @@ -2409,10 +2409,10 @@ async function runSinkForce(argv, ctx) { * `hyp sink maintain [instance] [--compact] [--dry-run]` * * Runs export maintenance on table-format (Iceberg) sink instances: - * snapshot expiration on exported tables, and — only with `--compact` — + * snapshot expiration on exported tables, and (only with `--compact`) * a data-file rewrite via icebird's `icebergRewrite`. * - * @ref LLP 0022#compaction — rewrites are out-of-band only: this manual + * @ref LLP 0022#compaction: rewrites are out-of-band only: this manual * CLI invocation is the one place they may run. The daemon loop and the * sink tick never compact. * @@ -2538,7 +2538,7 @@ function describeCompactionSkip(d) { case 'no-table': return 'compaction_skipped (no table metadata)' case 'conflict': - return 'compaction_conflict (concurrent commit won the race; staged files cleaned up — re-run to retry from fresh metadata)' + return 'compaction_conflict (concurrent commit won the race; staged files cleaned up - re-run to retry from fresh metadata)' case 'error': return `compaction_failed (${d.compactionError ?? 'unknown error'})` default: @@ -2579,7 +2579,7 @@ function buildPickerBackfillRunner(ctx) { /** * `hyp init [preset]` * - * Without arguments runs the interactive walkthrough (TTY only — when + * Without arguments runs the interactive walkthrough (TTY only; when * stdout is not a TTY the command prints the available presets and * exits non-zero so scripts get a deterministic failure instead of * blocking on stdin). @@ -2600,7 +2600,7 @@ function buildPickerBackfillRunner(ctx) { * small menu: reconfigure, see full status, or quit (the default; a bare * enter changes nothing). * - * @ref LLP 0011#returning-to-a-configured-install [implements] — the picker + * @ref LLP 0011#returning-to-a-configured-install [implements]: the picker * stays the first-run path; this gate only fronts it once a config exists. * * Returns: @@ -2825,11 +2825,11 @@ async function runInit(argv, ctx) { const available = ctx.initPresets.list() ctx.stderr.write(`hyp init: unknown preset '${presetName}'\n`) if (available.length === 0) { - ctx.stderr.write(' no presets registered — install a plugin that contributes one\n') + ctx.stderr.write(' no presets registered - install a plugin that contributes one\n') } else { ctx.stderr.write(' available:\n') for (const p of available) { - ctx.stderr.write(` ${p.name} (${p.plugin}) — ${p.summary}\n`) + ctx.stderr.write(` ${p.name} (${p.plugin}) - ${p.summary}\n`) } } return 1 @@ -2872,14 +2872,14 @@ async function runInit(argv, ctx) { return result.exitCode } const available = ctx.initPresets.list() - ctx.stderr.write('hyp init: stdin is not a TTY — pass a preset name or non-interactive flags.\n') + ctx.stderr.write('hyp init: stdin is not a TTY - pass a preset name or non-interactive flags.\n') ctx.stderr.write(' non-interactive: hyp init --yes [--client claude] [--source otel] [--force] ...\n') if (available.length === 0) { ctx.stderr.write(' no presets registered\n') } else { ctx.stderr.write(' presets:\n') for (const p of available) { - ctx.stderr.write(` ${p.name} (${p.plugin}) — ${p.summary}\n`) + ctx.stderr.write(` ${p.name} (${p.plugin}) - ${p.summary}\n`) } } return 2 @@ -2891,11 +2891,11 @@ async function runInit(argv, ctx) { const available = ctx.initPresets.list() ctx.stderr.write(`hyp init: unknown preset '${presetName}'\n`) if (available.length === 0) { - ctx.stderr.write(' no presets registered — install a plugin that contributes one\n') + ctx.stderr.write(' no presets registered - install a plugin that contributes one\n') } else { ctx.stderr.write(' available:\n') for (const p of available) { - ctx.stderr.write(` ${p.name} (${p.plugin}) — ${p.summary}\n`) + ctx.stderr.write(` ${p.name} (${p.plugin}) - ${p.summary}\n`) } } return 1 @@ -3017,7 +3017,7 @@ function parseInitFlags(argv) { * * @param {InitFlags} flags * @returns {{ exportChoice: PickerExport, origin: PickerExportOrigin }} - * @ref LLP 0011#autodetect-vs-default [implements] — export defaults to local Parquet, a fixed pick not derived from system state + * @ref LLP 0011#autodetect-vs-default [implements]: export defaults to local Parquet, a fixed pick not derived from system state */ export function resolveInitExportChoice(flags) { if (flags.exportChoice) { @@ -3034,7 +3034,7 @@ export function resolveInitExportChoice(flags) { * @param {InitFlags} flags * @param {CommandRunContext} ctx * @returns {Promise} - * @ref LLP 0011#non-interactive-entry [implements] — flags / preset / --from-file path that bypasses the interactive TUI + * @ref LLP 0011#non-interactive-entry [implements]: flags / preset / --from-file path that bypasses the interactive TUI */ async function runPickerInit(flags, ctx) { // --from-file short-circuits the picker entirely. The supplied @@ -3047,13 +3047,13 @@ async function runPickerInit(flags, ctx) { // Default sources when `--yes` is the only signal: capture Claude + // OTEL. (Export defaults separately, below.) - // @ref LLP 0002#v1-acceptance-criteria-summary [implements] — --yes default install captures Claude + OTEL + // @ref LLP 0002#v1-acceptance-criteria-summary [implements]: --yes default install captures Claude + OTEL const sources = flags.sources.slice() if (sources.length === 0) { if (flags.yes) { sources.push('claude', 'otel') } else { - ctx.stderr.write('hyp init: no sources selected — pass --source or --yes\n') + ctx.stderr.write('hyp init: no sources selected - pass --source or --yes\n') return 2 } } @@ -3094,7 +3094,7 @@ async function runPickerInit(flags, ctx) { } /** - * `hyp init --from-file ` — read a v2 config from disk, validate + * `hyp init --from-file `: read a v2 config from disk, validate * it, and write it to the canonical location. Still emits the * walkthrough spans so the smoke pipeline observes a consistent * lifecycle. @@ -3201,7 +3201,7 @@ async function runInitFromFile(flags, ctx) { } /** - * `hyp join [token]` — join a centrally-managed fleet. Pure + * `hyp join [token]`: join a centrally-managed fleet. Pure * sugar over two existing steps: write the seed config (an ordinary v2 * config containing exactly the central plugin) and run the * non-interactive daemon install. Doing those two steps by hand is @@ -3209,12 +3209,12 @@ async function runInitFromFile(flags, ctx) { * * Because a policy token is a multi-use fleet-wide credential, the * token can (and for MDM scripts, should) arrive via `--token-file` - * or stdin instead of argv — a bare argv token lands in shell history + * or stdin instead of argv. A bare argv token lands in shell history * and process listings. The seed config is written mode 0600. * * @param {string[]} argv * @param {CommandRunContext} ctx - * @ref LLP 0025#seed-config-mode [implements] — join = write-seed-config + daemon install; a wrapper, not a second code path + * @ref LLP 0025#seed-config-mode [implements]: join = write-seed-config + daemon install; a wrapper, not a second code path */ async function runJoin(argv, ctx) { const parsed = parseJoinArgs(argv) @@ -3252,7 +3252,7 @@ async function runJoin(argv, ctx) { } if (token === undefined) { if (isTty(ctx.stdin)) { - ctx.stderr.write('hyp join: no token given — pass it as an argument, via --token-file, or on stdin\n') + ctx.stderr.write('hyp join: no token given - pass it as an argument, via --token-file, or on stdin\n') return 2 } token = (await readAllStdin(ctx.stdin)).trim() @@ -3290,11 +3290,11 @@ async function runJoin(argv, ctx) { } // The seed is the initial *central* layer. It is written to a - // dedicated central-seed file under `config-control/` — never to + // dedicated central-seed file under `config-control/`, never to // `hypaware-config.json`, which is the user-owned local layer. This is // the #111 fix: `join` augments a working install instead of // destroying it. - // @ref LLP 0031#physical-layout [implements] — join writes only the central seed, never the local layer + // @ref LLP 0031#physical-layout [implements]: join writes only the central seed, never the local layer const obsEnv = readObservabilityEnv(ctx.env) const seedPath = centralSeedPath(obsEnv.stateDir) @@ -3319,11 +3319,11 @@ async function runJoin(argv, ctx) { // A re-enrollment (identity broke, operator re-runs `join`) writes a // fresh bootstrap token into the seed, but a prior enrollment may // have left a stale active config slot that boot resolution prefers - // over the seed — silently shadowing the new token, so identity + // over the seed, silently shadowing the new token, so identity // bootstrap keeps failing with no explanation (#139). Reset to // seed-config mode so the freshly written token is honored; on a // first join (no slot) this is a no-op. - // @ref LLP 0031#physical-layout [implements] — join supersedes a stale active slot so the fresh seed wins + // @ref LLP 0031#physical-layout [implements]: join supersedes a stale active slot so the fresh seed wins const reset = resetCentralLayerToSeed(obsEnv.stateDir) span.setAttribute('superseded_active_slot', reset.supersededActiveSlot) if (reset.supersededActiveSlot) { @@ -3342,7 +3342,7 @@ async function runJoin(argv, ctx) { span.setAttribute('error_kind', 'daemon_install_failed') return code } - ctx.stdout.write('✓ Joined — the daemon will pull its configuration from the server\n') + ctx.stdout.write('✓ Joined - the daemon will pull its configuration from the server\n') return 0 }, { component: 'join' } @@ -3513,7 +3513,7 @@ async function runClientLifecycle(action, argv, ctx) { if (action === 'attach') { // In dry-run mode the gateway source may not be started yet, // so `localEndpoint()` could throw. Fall back to a placeholder - // endpoint — adapters are expected to short-circuit before + // endpoint (adapters are expected to short-circuit before // touching it. let endpoint if (parsed.dryRun) { diff --git a/src/core/cli/core_verbs.js b/src/core/cli/core_verbs.js index 3417d6de..73ca49cf 100644 --- a/src/core/cli/core_verbs.js +++ b/src/core/cli/core_verbs.js @@ -19,7 +19,7 @@ export const CORE_VERBS = [querySqlVerb] /** * Register the intrinsic core verbs onto the kernel verb registry. Run * during `createKernelRuntime`, so the projected `query sql` command and - * the `query_sql` MCP tool exist on every boot, with no plugin needed — + * the `query_sql` MCP tool exist on every boot, with no plugin needed: * the intrinsic SQL surface (LLP 0003). * * @param {VerbRegistry} verbs diff --git a/src/core/cli/detect.js b/src/core/cli/detect.js index c74928db..b43e65f8 100644 --- a/src/core/cli/detect.js +++ b/src/core/cli/detect.js @@ -14,8 +14,8 @@ import { resolveClientSettingsPath } from '../daemon/client_settings_path.js' * The client sources the first-run picker can autodetect, paired with * the `settings_file` their plugin manifest declares for the attach * probe. Detection stats the *directory* that holds this file (the - * client's config home) — the "tool is installed on this system" - * signal — not the file itself, so HypAware writing the settings file + * client's config home): the "tool is installed on this system" + * signal, not the file itself, so HypAware writing the settings file * on attach never makes a source detect itself. * * This list is intentionally hardcoded rather than read from @@ -25,7 +25,7 @@ import { resolveClientSettingsPath } from '../daemon/client_settings_path.js' * is itself hardcoded. If the picker is ever made plugin-driven, move * detection to iterate the client descriptors and read each * `attach_probe.settings_file` (see `probeClientAttachFromDescriptor` - * in daemon/status.js) in that same change — not before. + * in daemon/status.js) in that same change, not before. * * @type {{ source: PickerSource, client: string, settingsFile: string }[]} */ diff --git a/src/core/cli/dispatch.js b/src/core/cli/dispatch.js index 16e86c22..86b31bee 100644 --- a/src/core/cli/dispatch.js +++ b/src/core/cli/dispatch.js @@ -61,13 +61,13 @@ function sinkWarningHint(err) { * * Lifecycle: * - * 1. `installObservability()` (idempotent — shares state with smoke + * 1. `installObservability()` (idempotent: shares state with smoke * harnesses and prior dispatch calls within the same process). * 2. Assemble a `CommandRegistry`. Core commands register directly; * plugin-contributed commands land during `bootKernel` below. * 3. Render help and exit when argv is empty on a non-TTY stdout or * begins with a help flag (no kernel boot required). - * 4. Otherwise call `bootKernel({ ... })` — the single shared boot + * 4. Otherwise call `bootKernel({ ... })`: the single shared boot * path that loads the config, discovers bundled plugin manifests, * resolves dependencies, and activates the selected plugins * *before* command dispatch. Active plugins land on @@ -107,8 +107,8 @@ export async function dispatch(argv, opts = {}) { const cacheRoot = path.join(obsEnv.stateDir, 'cache') // Inputs the help path uses to list plugin commands without booting. - // `obsEnv.stateDir` is `/hypaware` — the same `stateRoot` - // boot derives — so manifest discovery and config resolution see the + // `obsEnv.stateDir` is `/hypaware`: the same `stateRoot` + // boot derives; so manifest discovery and config resolution see the // exact plugin set and effective config dispatch would activate. const helpDiscovery = { workspaceDir: opts.workspaceDir, @@ -187,8 +187,8 @@ export async function dispatch(argv, opts = {}) { const matched = registry.match(argv) if (!matched) { // No command owns this argv. Before failing, see whether the leading - // tokens name a *group* — a prefix shared by registered subcommands - // (e.g. `graph`, with `graph neighbors`/`graph project` registered) — + // tokens name a *group*: a prefix shared by registered subcommands + // (e.g. `graph`, with `graph neighbors`/`graph project` registered) // and synthesize group help for it. A group that registers an explicit // bare command (`query`, `remote`, …) never reaches here: it matched // above and renders its own help, so the explicit registration wins. @@ -329,7 +329,7 @@ async function runCommandByName(name, rest, ctx) { * Resolve group-level help for an argv that matched no command. * * A "group" is a command-name prefix shared by registered subcommands but - * with no command of its own — e.g. `graph`, when `graph neighbors` and + * with no command of its own; e.g. `graph`, when `graph neighbors` and * `graph project` are registered but `graph` is not. Walk the leading * non-flag tokens from longest to shortest and return the longest prefix * that has registered children, so `hyp graph`, `hyp graph --help`, and @@ -344,7 +344,7 @@ async function runCommandByName(name, rest, ctx) { * @param {ReturnType} registry * @param {string[]} argv * @returns {{ prefix: string, subcommands: string[], unknownSub: string | undefined } | undefined} - * @ref LLP 0009#core-owns-dispatch — core renders group help; plugins only register the leaf subcommands + * @ref LLP 0009#core-owns-dispatch: core renders group help; plugins only register the leaf subcommands */ function resolveGroupHelp(registry, argv) { /** @type {string[]} */ @@ -520,7 +520,7 @@ function renderHelp({ stdout, registry, pluginCommands = [] }) { a.name < b.name ? -1 : a.name > b.name ? 1 : 0 ) - stdout.write('hyp — HypAware kernel CLI\n') + stdout.write('hyp - HypAware kernel CLI\n') stdout.write('\n') stdout.write('usage: hyp [args...]\n') stdout.write('\n') @@ -538,12 +538,12 @@ function renderHelp({ stdout, registry, pluginCommands = [] }) { * activating any plugin. * * Help renders before `bootKernel`, so it cannot read the activated - * command registry — doing so would cost a full boot: importing every + * command registry; doing so would cost a full boot: importing every * plugin entrypoint and binding the gateway/OTLP listeners some plugins * open during activation (the same reason `decideBootProfile` uses an * empty activation set for `daemon`/`status`/`version`). Instead help - * reads the two cheap inputs boot uses for *discovery* — plugin - * manifests (plain JSON) and the effective config — and lists the + * reads the two cheap inputs boot uses for *discovery*: plugin + * manifests (plain JSON) and the effective config, and lists the * commands each config-active plugin *declares* in its manifest * `contributes.commands`. That scope matches dispatch's `config` boot * profile, so every command shown here is one that will actually @@ -555,7 +555,7 @@ function renderHelp({ stdout, registry, pluginCommands = [] }) { * * @param {{ workspaceDir?: string, stateRoot: string, configPath: string }} args * @returns {Promise<{ name: string, summary: string }[]>} - * @ref LLP 0005#declarative [implements] — manifest lists commands before any plugin code is loaded + * @ref LLP 0005#declarative [implements]: manifest lists commands before any plugin code is loaded */ async function collectPluginHelpCommands({ workspaceDir, stateRoot, configPath }) { try { @@ -563,7 +563,7 @@ async function collectPluginHelpCommands({ workspaceDir, stateRoot, configPath } discoverBundledPlugins(workspaceDir !== undefined ? { workspaceDir } : {}), discoverInstalledPlugins({ stateDir: stateRoot }), ]) - // Resolve the effective config the SAME way `bootKernel` does — with + // Resolve the effective config the SAME way `bootKernel` does: with // the discovered plugin catalog. Without it the merge validator treats // every bundled plugin as unknown and drops local `plugins[]` additions // (e.g. `@hypaware/context-graph`) from a fleet-joined host's effective @@ -581,7 +581,7 @@ async function collectPluginHelpCommands({ workspaceDir, stateRoot, configPath } // activate. Dispatch boots ordinary commands with the `config` profile, // so select with it too. This replicates the two boot selection rules a // hand-rolled pool would miss: an installed plugin that *shadows* a - // bundled first-party name (boot rejects — nothing dispatches) and an + // bundled first-party name (boot rejects; nothing dispatches) and an // installed plugin that *replaces* a same-named excluded bundled // skeleton (its commands win, the skeleton's don't). const { shadowing, selectedManifests } = selectBootPlugins({ diff --git a/src/core/cli/flush-streams.js b/src/core/cli/flush-streams.js index 87789899..e871aff5 100644 --- a/src/core/cli/flush-streams.js +++ b/src/core/cli/flush-streams.js @@ -11,7 +11,7 @@ * never blocked. * * `process.exit()` terminates synchronously and drops whatever is still - * buffered in stdout/stderr — for a pipe that means output past the ~64KiB + * buffered in stdout/stderr: for a pipe that means output past the ~64KiB * pipe buffer is silently truncated. Awaiting this on stdout/stderr before * exiting guarantees every byte reached the OS first. (Writing to a file * never hit this because file writes complete synchronously.) diff --git a/src/core/cli/integration.js b/src/core/cli/integration.js index 2afe0dbc..4f016cc2 100644 --- a/src/core/cli/integration.js +++ b/src/core/cli/integration.js @@ -4,7 +4,7 @@ // Electron app that bundles hypaware and drives it in-process instead of // spawning the `hyp` binary). Every helper here boots the kernel through // the same `dispatch()` the CLI uses, captures stdout/stderr into buffers, -// and returns a structured result — so callers get a typed object and real +// and returns a structured result, so callers get a typed object and real // thrown errors instead of parsing JSON out of a child process's stdout. // // The long-running daemon is intentionally NOT exposed here: it must run as @@ -198,7 +198,7 @@ export function detach(client = 'claude', opts = {}) { * Join a central hypaware server, writing the join seed under * `/hypaware/config-control/`. With `noDaemon` (the default for * embedded hosts that own their own daemon) it performs no network call and - * installs no launchd/systemd unit — the host's already-running daemon + * installs no launchd/systemd unit: the host's already-running daemon * picks up the seed and pulls its configuration. * * `token` may be omitted when the caller supplies it another way (e.g. a @@ -206,18 +206,18 @@ export function detach(client = 'claude', opts = {}) { * * `dryRun` is intentionally not part of this surface: `hyp join` has no * dry-run path and always writes the seed. Passing `dryRun: true` throws - * rather than silently writing — unlike {@link attach}/{@link detach}, + * rather than silently writing, unlike {@link attach}/{@link detach}, * which do honor it. * * @param {string} url * @param {string} [token] * @param {Omit & { noDaemon?: boolean }} [opts] * @returns {Promise} - * @ref LLP 0025#seed-config-mode [implements] — embedded join writes only the central seed; the host's daemon pulls config - * @ref LLP 0017#the-primary-daemon [constrained-by] — noDaemon defaults true: the daemon is a service unit, never hosted in-process + * @ref LLP 0025#seed-config-mode [implements]: embedded join writes only the central seed; the host's daemon pulls config + * @ref LLP 0017#the-primary-daemon [constrained-by]: noDaemon defaults true: the daemon is a service unit, never hosted in-process */ export async function join(url, token, opts = {}) { - // `hyp join` always writes the central seed — there is no dry-run path. + // `hyp join` always writes the central seed, there is no dry-run path. // Refuse a dryRun request instead of writing anyway, so a preview caller // never mutates state. Forward-compatible: a real dry-run can land later. if (/** @type {IntegrationOptions} */ (opts).dryRun) { diff --git a/src/core/cli/remote_commands.js b/src/core/cli/remote_commands.js index 4cd10f23..d7a77edc 100644 --- a/src/core/cli/remote_commands.js +++ b/src/core/cli/remote_commands.js @@ -42,12 +42,12 @@ export async function runRemoteHelp(argv, ctx) { } /** - * `hyp remote add ` — register (or update) a target in the + * `hyp remote add `: register (or update) a target in the * local config's `query.remotes`. The URL is non-secret and committable. * * @param {string[]} argv * @param {CommandRunContext} ctx - * @ref LLP 0033#commands [implements] — `remote add` is a local-layer writer; URL in config, token never in config + * @ref LLP 0033#commands [implements]: `remote add` is a local-layer writer; URL in config, token never in config */ export async function runRemoteAdd(argv, ctx) { const positional = argv.filter((a) => !a.startsWith('-')) @@ -77,13 +77,13 @@ export async function runRemoteAdd(argv, ctx) { } /** - * `hyp remote login ` — store the query-scoped token for a target. + * `hyp remote login `: store the query-scoped token for a target. * Token source: `--token-file ` or piped stdin (a TTY with neither is * an error, never a hang). * * @param {string[]} argv * @param {CommandRunContext} ctx - * @ref LLP 0033#credentials [implements] — token to the 0600 store, never config; one login per server + * @ref LLP 0033#credentials [implements]: token to the 0600 store, never config; one login per server */ export async function runRemoteLogin(argv, ctx) { const positional = argv.filter((a) => !a.startsWith('-')) @@ -125,17 +125,17 @@ export async function runRemoteLogin(argv, ctx) { await writeToken(stateDir, name, token) ctx.stdout.write(`stored query-scoped token for '${name}' (mode 0600)\n`) - // A friendly nudge if the target isn't configured — the token still + // A friendly nudge if the target isn't configured: the token still // stores (an env override may use it), but a typo here is common. const remotes = await readConfiguredRemotes(ctx) if (!remotes[name]) { - ctx.stderr.write(`note: '${name}' is not a configured target — add it with 'hyp remote add ${name} '\n`) + ctx.stderr.write(`note: '${name}' is not a configured target - add it with 'hyp remote add ${name} '\n`) } return 0 } /** - * `hyp remote list` — targets + token status (`stored` / `env` / `missing`), + * `hyp remote list`: targets + token status (`stored` / `env` / `missing`), * never the token itself. * * @param {string[]} argv @@ -163,7 +163,7 @@ export async function runRemoteList(argv, ctx) { return 0 } if (names.length === 0) { - ctx.stdout.write("no remote targets configured — add one with 'hyp remote add '\n") + ctx.stdout.write("no remote targets configured - add one with 'hyp remote add '\n") return 0 } for (const name of names) { @@ -173,7 +173,7 @@ export async function runRemoteList(argv, ctx) { } /** - * `hyp remote remove ` — drop the target from config + its stored token. + * `hyp remote remove `: drop the target from config + its stored token. * * @param {string[]} argv * @param {CommandRunContext} ctx diff --git a/src/core/cli/tui-router.js b/src/core/cli/tui-router.js index 8b8ebdb9..41bccb87 100644 --- a/src/core/cli/tui-router.js +++ b/src/core/cli/tui-router.js @@ -8,7 +8,7 @@ import process from 'node:process' * * The TUI path requires BOTH stdin and stdout to be TTYs because the * runtime needs raw-mode key events and rewindable terminal frames. - * `HYP_NO_TUI=1` forces the legacy path even when both ends are TTYs — + * `HYP_NO_TUI=1` forces the legacy path even when both ends are TTYs: * a deliberate escape hatch for CI shells that report `isTTY=true` but * are wrapped by something that mangles ANSI sequences. * diff --git a/src/core/cli/tui/keypress.js b/src/core/cli/tui/keypress.js index 7fb50e37..763dec63 100644 --- a/src/core/cli/tui/keypress.js +++ b/src/core/cli/tui/keypress.js @@ -14,7 +14,7 @@ * @property {string} [name] Special name: 'up', 'down', 'space', * 'return', 'escape', 'backspace', or a * single character ('a', '1', ...). - * @property {string} [sequence] Raw character(s) — used for printable + * @property {string} [sequence] Raw character(s): used for printable * text input in `text` mode and the y/n * chars in `confirm` mode. * @property {boolean} [ctrl] diff --git a/src/core/cli/tui/render.js b/src/core/cli/tui/render.js index 02886e64..32560290 100644 --- a/src/core/cli/tui/render.js +++ b/src/core/cli/tui/render.js @@ -5,7 +5,7 @@ * that should be written to stdout to display the current frame. * * The returned string ends with a trailing newline. Lines are joined - * with `\n` (no `\r\n` — runtime uses raw mode where `\n` advances a + * with `\n` (no `\r\n`; runtime uses raw mode where `\n` advances a * row without resetting the column, and the runtime emits an explicit * `\r` before redrawing). * diff --git a/src/core/cli/tui/runtime.js b/src/core/cli/tui/runtime.js index c4da0695..162d3b50 100644 --- a/src/core/cli/tui/runtime.js +++ b/src/core/cli/tui/runtime.js @@ -205,7 +205,7 @@ function visibleWidth(line) { * a logical line is wider than the terminal: the terminal soft-wraps it * onto multiple rows, so the cursor descended further than the number of * `\n` written. Undercounting here leaves stale rows on screen on every - * redraw — the classic "the question keeps duplicating when I move the + * redraw: the classic "the question keeps duplicating when I move the * cursor" symptom. * * Frames always end with a trailing `\n`; the empty segment after it diff --git a/src/core/cli/verb_codec.js b/src/core/cli/verb_codec.js index 683df70c..0796f5c9 100644 --- a/src/core/cli/verb_codec.js +++ b/src/core/cli/verb_codec.js @@ -10,8 +10,8 @@ * projects a CLI command and an MCP tool from it * ([LLP 0034 §verbs](../../../llp/0034-mcp-host-intrinsic.decision.md#verbs)). * This module turns argv into typed params (CLI side) and emits the clean - * JSON Schema the MCP tool advertises — so the flag set and the tool - * schema can never drift, they are the same object. + * JSON Schema the MCP tool advertises, so the flag set and the tool + * schema can never drift (they are the same object). */ /** @@ -36,8 +36,8 @@ const REFRESH_MODES = new Set(['never', 'auto', 'always']) * Kernel-owned render/transport control flags, common to every verb and * stripped before the schema codec sees the rest. Keeping them out of the * per-verb `inputSchema` is deliberate: `--refresh` is a local-cache - * control and `--remote` a transport selector — neither is an operation - * param, so neither belongs in the MCP tool schema. + * control and `--remote` a transport selector (neither is an operation + * param), so neither belongs in the MCP tool schema. * * @param {string[]} argv * @returns {{ ok: true, controls: VerbRenderControls & { refresh: 'never'|'auto'|'always', refreshExplicit: boolean, remote: string | undefined }, rest: string[] } | { ok: false, error: string }} @@ -200,7 +200,7 @@ export function argvToParams(inputSchema, argv) { * Validate + coerce an MCP `tools/call` arguments object against the same * schema. MCP delivers typed JSON, but a client may send a string for an * integer; we coerce defensively, apply defaults, and enforce - * required/enum — the identical contract the CLI path enforces. + * required/enum: the identical contract the CLI path enforces. * * @param {VerbInputSchema} inputSchema * @param {Record} args @@ -240,7 +240,7 @@ export function validateToolArguments(inputSchema, args) { /** * Project the verb's `inputSchema` to the clean JSON Schema the MCP tool - * advertises — the same properties, minus the CLI-only `positional` and + * advertises: the same properties, minus the CLI-only `positional` and * per-property `greedy` hints (those describe argv binding, not the wire * contract). * diff --git a/src/core/cli/verb_command.js b/src/core/cli/verb_command.js index d8a2d441..a808f77d 100644 --- a/src/core/cli/verb_command.js +++ b/src/core/cli/verb_command.js @@ -17,7 +17,7 @@ import { argvToParams, parseControlFlags, usageForVerb } from './verb_codec.js' * * @param {VerbRegistration} verb * @returns {CommandRegistration} - * @ref LLP 0034#verbs [implements] — one declaration → a CLI command and an MCP tool; the kernel owns both adapters so the flag set and the tool schema never drift + * @ref LLP 0034#verbs [implements]: one declaration → a CLI command and an MCP tool; the kernel owns both adapters so the flag set and the tool schema never drift */ export function verbToCommand(verb) { return { @@ -58,7 +58,7 @@ export async function runVerbCommand(verb, argv, ctx) { if (ctrl.controls.remote) { // `--refresh` is a local-cache control; the server owns its freshness, // so combining it with `--remote` is a hard error, not a silent ignore. - // @ref LLP 0033#flag-compat [implements] — --remote with --refresh is rejected; other render flags stay valid + // @ref LLP 0033#flag-compat [implements]: --remote with --refresh is rejected; other render flags stay valid if (ctrl.controls.refreshExplicit) { ctx.stderr.write( `hyp ${verb.name}: --refresh is a local cache control and cannot be combined with --remote (the server owns its freshness)\n` @@ -75,7 +75,7 @@ export async function runVerbCommand(verb, argv, ctx) { } // Server-side cap (data volume) surfaced as its own stderr line, distinct // from the client display budget the renderer adds below. - // @ref LLP 0033#two-truncations [implements] — server cap and client display budget are two separate stderr lines + // @ref LLP 0033#two-truncations [implements]: server cap and client display budget are two separate stderr lines for (const line of remote.notices) ctx.stderr.write(line + '\n') result = remote.result } else { diff --git a/src/core/cli/walkthrough.js b/src/core/cli/walkthrough.js index 02b514e1..ba391158 100644 --- a/src/core/cli/walkthrough.js +++ b/src/core/cli/walkthrough.js @@ -115,7 +115,7 @@ export async function runWalkthrough(opts) { return picked } - stdout.write('Welcome to HypAware — the local logs+telemetry collector.\n') + stdout.write('Welcome to HypAware - the local logs+telemetry collector.\n') stdout.write('\n') const sourcesPicked = await askCategory( @@ -210,7 +210,7 @@ function buildSinkOptions(sinks) { const opts = [ { value: '__none__', - label: 'Keep local only — query the cache for the retention window', + label: 'Keep local only - query the cache for the retention window', }, ] for (const { contribution } of sinks.listContributions()) { @@ -413,7 +413,7 @@ function legacyRetentionPromptFactory(opts) { /** * Build the interactive "overwrite existing config?" confirm. Defaults - * to **no** — a bare Enter keeps the existing config — so a stray + * to **no** (a bare Enter keeps the existing config), so a stray * keystroke never destroys a working install. On yes the caller backs * the file up before replacing it. * @@ -495,7 +495,7 @@ function tuiRetentionPromptFactory(opts) { /** * Route between the TUI and legacy prompts. Tests and CI keep getting - * the legacy numbered list — only real TTYs without `HYP_NO_TUI=1` see + * the legacy numbered list, but only real TTYs without `HYP_NO_TUI=1` see * the new interactive multiselect. * * @param {Pick} opts @@ -518,8 +518,8 @@ function defaultRetentionPromptFactory(opts) { /** * Build the interactive backfill-consent prompt. Routes to the TUI * arrow-navigable yes/no select on a real TTY, else a legacy readline - * yes/no. Both default to yes so a bare enter opts in — the bead's - * "default backfill to enabled, but let the user choose no". + * yes/no. Both default to yes, so a bare enter opts in: the design + * is "default backfill to enabled, but let the user choose no". * * @param {Pick} opts * @returns {AsyncBackfillConsentPrompt} @@ -542,8 +542,8 @@ function tuiBackfillConsentPromptFactory(opts) { const choice = await select({ title: backfillConsentTitle(providers, retentionDays), options: [ - { value: 'yes', label: 'Yes — import it now', summary: 'Reads local transcripts into the query cache.' }, - { value: 'no', label: 'No — skip for now', summary: 'You can import later with hyp backfill.' }, + { value: 'yes', label: 'Yes - import it now', summary: 'Reads local transcripts into the query cache.' }, + { value: 'no', label: 'No - skip for now', summary: 'You can import later with hyp backfill.' }, ], default: 'yes', clearOnResolve: true, @@ -587,7 +587,7 @@ function backfillConsentTitle(providers, retentionDays) { /** * Phase 5 picker source contributions. These are the user-facing * inputs for the V1 npx first-run flow. Each value maps to a plugin - * composition rule in `composePickerConfig` — they are NOT tied to + * composition rule in `composePickerConfig`. They are NOT tied to * the source registry (which carries lower-level source contributions * like `ai-gateway` and `otlp`). * @@ -667,7 +667,7 @@ const PICKER_EXPORTS = [ * * @param {RunPickerWalkthroughOptions} opts * @returns {Promise} - * @ref LLP 0011#interactive-walkthrough [implements] — canonical npx first-run; composes plugin-contributed what/where picks + * @ref LLP 0011#interactive-walkthrough [implements]: canonical npx first-run; composes plugin-contributed what/where picks */ export async function runPickerWalkthrough(opts) { const { capabilities, stdout, env } = opts @@ -676,9 +676,9 @@ export async function runPickerWalkthrough(opts) { // Autodetect installed client tools so the picker can pre-check them. // Interactive only: when `picks` are supplied (`--yes` / `--dry-run` / // presets) the selection is explicit and must stay deterministic, so - // detection is skipped entirely. Best-effort — a detector failure + // detection is skipped entirely. Best-effort: a detector failure // leaves the set empty rather than blocking onboarding. - // @ref LLP 0011#autodetect-vs-default [implements] — detection only seeds the initial checkbox; never forces a source on + // @ref LLP 0011#autodetect-vs-default [implements]: detection only seeds the initial checkbox; never forces a source on const interactive = !opts.picks /** @type {Set} */ let detected = new Set() @@ -709,7 +709,7 @@ export async function runPickerWalkthrough(opts) { /** @type {PickerPicks} */ let picks // Provenance of the export choice, for telemetry. Export is no longer - // asked interactively — local-parquet is the out-of-the-box default — + // asked interactively (local-parquet is the out-of-the-box default), // so the origin is `user` only when an explicit `--export` flag was // threaded in on the pre-baked path; otherwise the pick was defaulted. let exportOrigin = 'default' @@ -720,7 +720,7 @@ export async function runPickerWalkthrough(opts) { const ask = opts.prompt ?? defaultPromptFactory(opts) const retentionAsk = opts.retentionPrompt ?? defaultRetentionPromptFactory(opts) - stdout.write('Welcome to HypAware — the local logs+telemetry collector.\n\n') + stdout.write('Welcome to HypAware - the local logs+telemetry collector.\n\n') try { const sourceRaw = await ask({ @@ -786,7 +786,7 @@ export async function runPickerWalkthrough(opts) { // half of #111). Interactive runs prompt for confirmation; // non-interactive runs require `--force`. Either path backs up the // existing file before replacing it. - // @ref LLP 0031#local-layer-writers [implements] — init overwrite safety on the walkthrough write path + // @ref LLP 0031#local-layer-writers [implements]: init overwrite safety on the walkthrough write path const overwriteConfirm = interactive ? (opts.confirmOverwrite ?? defaultOverwriteConfirmFactory({ stdin: opts.stdin, stdout })) : undefined @@ -937,7 +937,7 @@ export async function runPickerWalkthrough(opts) { * hypHome: string, * }} args * @returns {HypAwareV2Config} - * @ref LLP 0011#no-architectural-names [implements] — user picks what/where; HypAware derives the explicit plugin set, no role labels + * @ref LLP 0011#no-architectural-names [implements]: user picks what/where; HypAware derives the explicit plugin set, no role labels */ export function composePickerConfig(args) { const wantsAnthropic = args.sources.includes('claude') || args.sources.includes('raw-anthropic') @@ -1258,8 +1258,8 @@ async function runPickerFinale(args) { // Backfill: import each picked client's local history after the config // write and before the daemon (re)start that resumes live capture. - // Runs independent of the daemon — `--no-daemon` still backfills, since - // it is a local file import — and is bounded by the retention window and + // Runs independent of the daemon (`--no-daemon` still backfills, since + // it is a local file import) and is bounded by the retention window and // the `backfillUntil` cutoff so it never double-counts live rows. await runFinaleBackfill({ ...(args.backfill ? { backfill: args.backfill } : {}), @@ -1303,7 +1303,7 @@ async function runPickerFinale(args) { * - interactive (no pre-baked picks): prompt, defaulting to yes; * - `--yes` / `--dry-run` (picks supplied): run automatically; * - `--dry-run`: scan and report a plan but write nothing; - * - `--no-daemon`: still backfill — it is a local file import. + * - `--no-daemon`: still backfill (it is a local file import). * * Each provider's outcome is pushed onto `summary.backfill` and a * one-line status is written to stdout. Wrapped in a `walkthrough.backfill` @@ -1372,8 +1372,8 @@ async function runFinaleBackfill(args) { return } // Guard each provider so one failure neither aborts sibling - // providers nor the daemon (re)start that resumes live capture — - // matching the attach/restart resilience above. + // providers nor the daemon (re)start that resumes live capture. + // This matches the attach/restart resilience above. for (const provider of providers) { try { // Importing local history reads and writes potentially @@ -1525,8 +1525,8 @@ async function overwriteAbortedResult({ opts, configPath, config, picks }) { * surfaces {@link WALKTHROUGH_CANCEL_EXIT_CODE} (130, matching SIGINT * convention) as the exit code. The returned object satisfies the * required shape of {@link PickerWalkthroughResult} but contains no - * config — callers that key off `exitCode` already short-circuit on - * non-zero values. + * config (callers that key off `exitCode` already short-circuit on + * non-zero values). * * @param {RunPickerWalkthroughOptions} opts * @returns {Promise} diff --git a/src/core/commands/backfill.js b/src/core/commands/backfill.js index d4a5b9b4..4d5bdc8f 100644 --- a/src/core/commands/backfill.js +++ b/src/core/commands/backfill.js @@ -9,7 +9,7 @@ import { DEFAULT_RETENTION_DAYS } from '../cache/retention.js' * Base partition segment for backfilled writes. `storage.appendRows` * re-routes each row to its real source partition using the dataset's * registered `cachePartitioning` declaration, so this segment only - * names the spool bucket and the dataset-attribution path — it keeps + * names the spool bucket and the dataset-attribution path; it keeps * backfill spool state distinct from the live capture spool while * landing rows in the exact same per-source Iceberg tables. */ @@ -132,10 +132,10 @@ export async function runBackfill(argv, ctx) { } /** - * `hyp backfill list [--json]` — enumerate every registered provider. + * `hyp backfill list [--json]`: enumerate every registered provider. * * Unlike `hyp backfill `, list does NOT filter to the - * active config — discovery is the whole point of the command, and a + * active config; discovery is the whole point of the command, and a * later run may opt into an explicit provider name. * * @param {string[]} argv @@ -338,7 +338,7 @@ function markProviderFailed(result, error) { * Emits `backfill.provider_*` / `backfill.scan` / `backfill.materialize` * / `backfill.write` / `backfill.flush` lifecycle spans, all carrying * `dev_run_id` and `provider`. Failures abort the provider but do not - * abort sibling providers — the runner walks them sequentially. + * abort sibling providers; the runner walks them sequentially. * * @param {{ * provider: BackfillContribution, @@ -552,7 +552,7 @@ async function materializeItem(args) { /** * Append rows to the dataset's intrinsic cache table. The runner * resolves the table path via the kernel `QueryRegistry`. Datasets - * without a registered table path are logged and skipped — provider + * without a registered table path are logged and skipped; provider * authors should not yield items for unregistered datasets. * * @param {{ @@ -594,7 +594,7 @@ async function writeRows(args) { } // `appendRows` derives the dataset from the path and re-routes // rows into per-source partitions via the registered - // `cachePartitioning` declaration — the same write path the live + // `cachePartitioning` declaration; the same write path the live // gateway recorder uses. We only need a dataset-attributable base // path plus the dataset's schema columns. const tablePath = ctx.storage.cacheTablePath(dataset, [BACKFILL_PARTITION_SEGMENT]) @@ -609,7 +609,7 @@ async function writeRows(args) { /** * Flush each touched dataset so `hyp query` immediately sees the * imported rows. Storage layers without an explicit flush helper get - * a logged skip — append still committed to the spool path. + * a logged skip; append still committed to the spool path. * * @param {{ * dataset: string, @@ -634,7 +634,7 @@ async function flushDataset(args) { async () => { const registered = ctx.query.getDataset?.(dataset) // `flushTable` lives on the extended storage service, not the - // public `QueryStorageService` surface — feature-detect it. The + // public `QueryStorageService` surface; feature-detect it. The // flushed path must match the base path `writeRows` appended to // so the same spool bucket is committed. /** @type {any} */ diff --git a/src/core/config/action_backfill.js b/src/core/config/action_backfill.js index 26031a20..690cfc92 100644 --- a/src/core/config/action_backfill.js +++ b/src/core/config/action_backfill.js @@ -19,7 +19,7 @@ import { readBackfillPolicy } from './backfill_policy.js' const MS_PER_DAY = 86_400_000 /** - * The backfill action handler — the v1 instance of the generic client-action + * The backfill action handler: the v1 instance of the generic client-action * reconciler (LLP 0036 / LLP 0037). It is the run-once "import this client's * local history once the central config that enabled it is confirmed" effect, * realized as a subprocess `hyp backfill` launch so a months-deep import can @@ -33,8 +33,8 @@ const MS_PER_DAY = 86_400_000 * * @param {CreateBackfillHandlerOptions} [opts] * @returns {ActionHandler} - * @ref LLP 0041#run-once-flow-backfill-handler [implements] — backfillHandler.desired() over selectProviders + per-plugin config.backfill; perform() resolves window_days->--since and spawns `hyp backfill --json` - * @ref LLP 0037 — backfill on join (the instance this realizes) + * @ref LLP 0041#run-once-flow-backfill-handler [implements]: backfillHandler.desired() over selectProviders + per-plugin config.backfill; perform() resolves window_days->--since and spawns `hyp backfill --json` + * @ref LLP 0037: backfill on join (the instance this realizes) */ export function createBackfillHandler(opts = {}) { const spawn = opts.spawn ?? defaultBackfillSpawn @@ -47,12 +47,12 @@ export function createBackfillHandler(opts = {}) { * "enabled-in-config" predicate `hyp backfill` uses (`selectProviders` * with no explicit names), then drops any provider whose owning plugin * set `backfill.on_join: false` (the operator opt-out, which rides the - * locked `plugins[]` entry — there is no local override). Pure: no + * locked `plugins[]` entry - there is no local override). Pure: no * effects, no spawn. * * @param {ActionContext} ctx * @returns {DesiredAction[]} - * @ref LLP 0041#consent-gating [constrained-by] — default-on; suppression is `backfill.on_join:false` in the locked central plugin entry, not a local override + * @ref LLP 0041#consent-gating [constrained-by]: default-on; suppression is `backfill.on_join:false` in the locked central plugin entry, not a local override */ desired(ctx) { const activePlugins = ctx.config.plugins ?? [] @@ -74,7 +74,7 @@ export function createBackfillHandler(opts = {}) { // Default-on: only an explicit `on_join: false` opts out. if (policy.onJoin === false) continue desired.push({ - // The marker key is the owning plugin name — a per-(machine, + // The marker key is the owning plugin name: a per-(machine, // provider) boolean (LLP 0041 §request-key). The CLI positional, // however, is the *provider* name (`hyp backfill claude`, not // `@hypaware/claude`), carried separately in params. @@ -100,7 +100,7 @@ export function createBackfillHandler(opts = {}) { * @param {DesiredAction} action * @param {ActionContext} ctx * @returns {Promise} - * @ref LLP 0041#run-once-flow-backfill-handler [implements] — spawn `hyp backfill [--since ] --json` (runSmoke spawn pattern), parse providers[].rows_written, never advance to done on non-zero exit + * @ref LLP 0041#run-once-flow-backfill-handler [implements]: spawn `hyp backfill [--since ] --json` (runSmoke spawn pattern), parse providers[].rows_written, never advance to done on non-zero exit */ async perform(action, ctx) { const params = action.params ?? {} @@ -132,10 +132,10 @@ export function createBackfillHandler(opts = {}) { let result try { // Spawn with the daemon's resolved env (HYP_HOME forced to the - // daemon's hypHome upstream in the reconcile input) — NOT + // daemon's hypHome upstream in the reconcile input): NOT // `process.env`, which can name a different HYP_HOME on the // direct-`runDaemon`/hermetic-smoke path and import into the wrong - // cache. @ref LLP 0041#run-once-flow-backfill-handler [constrained-by] + // cache. @ref LLP 0041#run-once-flow-backfill-handler [constrained-by]: result = await spawn({ args, env: ctx.env }) } catch (err) { return { status: 'failed', reason: err instanceof Error ? err.message : String(err) } @@ -150,7 +150,7 @@ export function createBackfillHandler(opts = {}) { const rows = parseRowsWritten(result.stdout) // Exit 0 is authoritative: the import committed. A row count is - // best-effort — an unparseable payload still records `done` (rows + // best-effort: an unparseable payload still records `done` (rows // omitted) rather than re-running a successful import. return rows !== undefined ? { status: 'done', rows } : { status: 'done' } }, @@ -197,7 +197,7 @@ function parseRowsWritten(stdout) { /** * The real subprocess seam: spawn `process.execPath bin/hypaware.js ` * resolved off `import.meta.url` (the `runSmoke` spawn pattern) and capture - * stdout. Async (not `spawnSync`) on purpose — a multi-minute import must not + * stdout. Async (not `spawnSync`) on purpose: a multi-minute import must not * block the daemon's event loop (LLP 0041 §Execution isolation). Inherits the * daemon's `env` so the child writes the same cache (`HYP_HOME`). * diff --git a/src/core/config/action_reconciler.js b/src/core/config/action_reconciler.js index 365f2895..6093d27d 100644 --- a/src/core/config/action_reconciler.js +++ b/src/core/config/action_reconciler.js @@ -26,29 +26,29 @@ import { Attr, getLogger } from '../observability/index.js' * The kernel state subdirectory the reconciler shares with the apply * engine. Must match `CONTROL_DIRNAME` in `apply.js`: the marker is kernel * surface and belongs beside `state.json`, not in a plugin state dir - * (LLP 0041 — "the action marker belongs here too, not in a plugin state - * dir — the reconciler is kernel surface"). + * (LLP 0041: "the action marker belongs here too, not in a plugin state + * dir, the reconciler is kernel surface"). */ const CONTROL_DIRNAME = 'config-control' /** * The action-marker file. Namespaced per handler kind and keyed by request * key; written atomically (tmp+rename, mode 0600) exactly like `state.json`. - * @ref LLP 0041#idempotency-and-completion-state [implements] — marker file config-control/client-actions.json, atomic tmp+rename, namespaced per handler kind + * @ref LLP 0041#idempotency-and-completion-state [implements]: marker file config-control/client-actions.json, atomic tmp+rename, namespaced per handler kind */ const CLIENT_ACTIONS_BASENAME = 'client-actions.json' /** * Build the generic, daemon-constructed action reconciler. It is the * run-once / reconcile-on-config machinery and knows nothing about Claude - * vs Codex — only the {@link ActionHandler} interface. The daemon wires + * vs Codex: only the {@link ActionHandler} interface. The daemon wires * its `reconcile()` to the config-confirmation edge and the * after-activation already-confirmed pass. * * @param {CreateActionReconcilerOptions} opts * @returns {ActionReconciler} - * @ref LLP 0041#the-reconciler-component [implements] — createActionReconciler(opts) → { reconcile, readStatus }, constructed by the daemon like createConfigControl - * @ref LLP 0036 — central-config-driven client action seam (the decision this realizes) + * @ref LLP 0041#the-reconciler-component [implements]: createActionReconciler(opts) → { reconcile, readStatus }, constructed by the daemon like createConfigControl + * @ref LLP 0036: central-config-driven client action seam (the decision this realizes) */ export function createActionReconciler(opts) { const { stateRoot, handlers } = opts @@ -78,7 +78,7 @@ export function createActionReconciler(opts) { * * @param {ReconcileInput} input * @returns {Promise} - * @ref LLP 0041#the-reconciler-component [implements] — reconcile() is level-triggered: diff desired() against the marker, act only on the gap; a done marker short-circuits + * @ref LLP 0041#the-reconciler-component [implements]: reconcile() is level-triggered: diff desired() against the marker, act only on the gap; a done marker short-circuits */ async function reconcile(input) { /** @type {ActionContext} */ @@ -178,7 +178,7 @@ export function createActionReconciler(opts) { // Reverse gap: only reversible handlers undo a previously-applied key // the config no longer names (leave/detach). Run-once handlers - // (backfill) omit reverse() — imported data stays, the marker is kept, + // (backfill) omit reverse(): imported data stays, the marker is kept, // and this loop is skipped (LLP 0041 §Undo on leave). const reverse = handler.reverse if (typeof reverse === 'function') { @@ -187,7 +187,7 @@ export function createActionReconciler(opts) { const marker = markers[requestKey] if (!marker || marker.status === 'failed') { // A failed marker for a no-longer-desired key never applied an - // effect, so there is nothing to undo — just drop it. + // effect, so there is nothing to undo: just drop it. delete markers[requestKey] mutated = true continue @@ -245,7 +245,7 @@ export function createActionReconciler(opts) { /** * Invoke a handler hook and normalize a throw into a `failed` outcome, so a * handler that rejects is treated identically to one that returns - * `{ status: 'failed' }` — the marker records the failure and the next pass + * `{ status: 'failed' }`: the marker records the failure and the next pass * retries. * * @param {() => Promise} fn @@ -271,7 +271,7 @@ async function runOutcome(fn) { * while `hyp status` (which already swallows the error in * `readClientActionStatus`) reports clean. An empty store means the next * pass simply re-derives the gap from `desired()` and rewrites a clean - * marker — losing only the (recoverable) completion record, never running a + * marker: losing only the (recoverable) completion record, never running a * pass it should not. * * @param {string} markerPath @@ -295,13 +295,13 @@ function readMarkerStore(markerPath) { } /** - * Read-only view of the client-action markers for `hyp status` — usable + * Read-only view of the client-action markers for `hyp status`: usable * from any process (the CLI is not the daemon), so it never constructs the * reconciler or takes its handlers. Mirrors `readConfigControlStatus`. * * @param {{ stateRoot: string }} args * @returns {ClientActionStatus} - * @ref LLP 0041#idempotency-and-completion-state [implements] — read-only marker view for the status surface, no engine construction + * @ref LLP 0041#idempotency-and-completion-state [implements]: read-only marker view for the status surface, no engine construction */ export function readClientActionStatus({ stateRoot }) { const markerPath = path.join(stateRoot, CONTROL_DIRNAME, CLIENT_ACTIONS_BASENAME) @@ -310,7 +310,7 @@ export function readClientActionStatus({ stateRoot }) { try { store = readMarkerStore(markerPath) } catch { - // unreadable markers surface as empty — status is best-effort + // unreadable markers surface as empty: status is best-effort } return { byKind: store } } diff --git a/src/core/config/apply.js b/src/core/config/apply.js index 23929101..55e69736 100644 --- a/src/core/config/apply.js +++ b/src/core/config/apply.js @@ -25,14 +25,14 @@ import { parseConfigShape } from './schema.js' * parsed and persisted wholesale, so a stated cap bounds memory and * disk regardless of what an authenticated server sends. 1 MiB is * orders of magnitude above any real config. - * @ref LLP 0025#config-pull-loop [implements] — max accepted config document size, settled at 1 MiB + * @ref LLP 0025#config-pull-loop [implements]: max accepted config document size, settled at 1 MiB */ export const MAX_CONFIG_DOCUMENT_BYTES = 1024 * 1024 /** * Default config pull cadence (seconds) when the staged document's * central sink does not set `poll_interval_seconds`. Mirrors the - * central plugin's own default — the kernel needs the value to size + * central plugin's own default: the kernel needs the value to size * the probation window without asking the plugin. */ export const DEFAULT_POLL_INTERVAL_SECONDS = 300 @@ -41,7 +41,7 @@ export const DEFAULT_POLL_INTERVAL_SECONDS = 300 * Probation window floor (seconds). The window is * `max(3 × poll_interval_seconds, floor)` so a fast poll cadence still * leaves room for daemon relaunch + identity refresh + one retry. - * @ref LLP 0025#post-apply-probation [implements] — window formula with the floor settled at 120s + * @ref LLP 0025#post-apply-probation [implements]: window formula with the floor settled at 120s */ export const PROBATION_FLOOR_SECONDS = 120 @@ -52,16 +52,16 @@ const STATE_BASENAME = 'state.json' * The active-slot pointer. A relative symlink under `config-control/` * pointing at the live `config.{a,b}.json` slot. Relocated here (off the * user-facing `hypaware-config.json` path) so the local layer can be a - * plain user file — the atomic symlink-flip crash-safety is preserved. - * @ref LLP 0031#physical-layout [constrained-by] — active-slot pointer relocated into config-control/ + * plain user file: the atomic symlink-flip crash-safety is preserved. + * @ref LLP 0031#physical-layout [constrained-by]: active-slot pointer relocated into config-control/ */ const ACTIVE_BASENAME = 'active' /** * The join seed: the initial central layer, written by `hyp join` (mode - * 0600 — it holds the policy token). Read once on the first apply to + * 0600: it holds the policy token). Read once on the first apply to * preserve it as the rollback target, then retired. - * @ref LLP 0031#physical-layout [implements] — dedicated central-seed file, not hypaware-config.json + * @ref LLP 0031#physical-layout [implements]: dedicated central-seed file, not hypaware-config.json */ const SEED_BASENAME = 'seed.json' @@ -81,11 +81,11 @@ export function centralSeedPath(stateRoot) { * Resolve the central layer's source file for boot, read-only: the * active applied slot if the pointer is set, **else** the join seed, * **else** null (a host that never joined). Reading is safe in any boot - * (CLI or daemon) — only the daemon's apply engine ever *writes* here. + * (CLI or daemon): only the daemon's apply engine ever *writes* here. * * @param {{ stateRoot: string }} args * @returns {string | null} - * @ref LLP 0031#physical-layout [implements] — central = active slot else seed else none + * @ref LLP 0031#physical-layout [implements]: central = active slot else seed else none */ export function resolveCentralLayerPath({ stateRoot }) { const controlDir = path.join(stateRoot, CONTROL_DIRNAME) @@ -104,7 +104,7 @@ export function resolveCentralLayerPath({ stateRoot }) { * pointer, no slots) it is a no-op. * * `hyp join` calls this right after writing a fresh seed. A re-enrollment - * — the operator re-runs `join` because identity broke — writes a new + * - the operator re-runs `join` because identity broke - writes a new * bootstrap token into the seed, but a prior enrollment may have left a * stale active slot whose `identity` carries no token. Boot resolution * prefers the active slot over the seed ({@link resolveCentralLayerPath}), @@ -117,7 +117,7 @@ export function resolveCentralLayerPath({ stateRoot }) { * * @param {string} stateRoot * @returns {{ supersededActiveSlot: boolean }} whether a stale active slot was cleared - * @ref LLP 0031#physical-layout [implements] — re-join resets to seed mode so a stale active slot never shadows the fresh seed token + * @ref LLP 0031#physical-layout [implements]: re-join resets to seed mode so a stale active slot never shadows the fresh seed token */ export function resetCentralLayerToSeed(stateRoot) { const controlDir = path.join(stateRoot, CONTROL_DIRNAME) @@ -145,13 +145,13 @@ export function resetCentralLayerToSeed(stateRoot) { * file under `/config-control/`, with the served ETag in a * per-slot sidecar written *before* the flip. The operative config * path becomes a symlink to the active slot, replaced atomically via - * tmp+rename — so the config document and its etag transition together + * tmp+rename: so the config document and its etag transition together * in both directions (apply and rollback), and last-known-good is * crash-safe by construction (the previous slot is never modified). * * @param {CreateConfigControlOptions} opts * @returns {ConfigControl} - * @ref LLP 0025#apply-engine-is-kernel-surface [implements] — the engine is kernel-owned; plugins only see the narrow facade + * @ref LLP 0025#apply-engine-is-kernel-surface [implements]: the engine is kernel-owned; plugins only see the narrow facade */ export function createConfigControl(opts) { const { stateRoot, requestRestart, onConfirmed } = opts @@ -199,12 +199,12 @@ export function createConfigControl(opts) { /** * Atomically point the active-slot pointer at `slot`. A relative * symlink is created at a tmp path and renamed over the pointer, so a - * crash leaves either the old or the new pointer — never neither. The + * crash leaves either the old or the new pointer: never neither. The * pointer lives inside `config-control/` (not at the user-facing * config path), so the local layer is never a symlink. * * @param {ConfigSlot} slot - * @ref LLP 0031#physical-layout [constrained-by] — pointer relocated into config-control/, atomic flip preserved + * @ref LLP 0031#physical-layout [constrained-by]: pointer relocated into config-control/, atomic flip preserved */ function flipPointer(slot) { const tmp = `${activePath}.tmp.${process.pid}.${now()}` @@ -224,11 +224,11 @@ export function createConfigControl(opts) { * * A rollback needs a **distinct** slot to land on. With no distinct * `previous_slot` the only "rollback" available is a no-op flip that - * leaves the failed config operative — and recording its etag as + * leaves the failed config operative, and recording its etag as * `bad_etag` while it stays active wedges central: the bad-etag backoff * then refuses to re-apply the very revision that is running, probation * bookkeeping never clears, and a boot does not recover (#141). Refuse - * to manufacture that contradiction — clear probation, surface a clear + * to manufacture that contradiction: clear probation, surface a clear * error, and leave recovery to boot's consistency guard / the next * pull. Returns whether the pointer was actually flipped, so callers * don't request a staged restart onto the unchanged config (an @@ -238,7 +238,7 @@ export function createConfigControl(opts) { * @param {ConfigRollbackReason} reason * @param {string} [detail] * @returns {boolean} whether the pointer was flipped to a distinct slot - * @ref LLP 0025#last-known-good-rollback [implements] — flip back + remembered bad etag + structured reason; a bad_etag is never recorded for the still-active slot (#141) + * @ref LLP 0025#last-known-good-rollback [implements]: flip back + remembered bad etag + structured reason; a bad_etag is never recorded for the still-active slot (#141) */ function rollback(marker, reason, detail) { const at = new Date(now()).toISOString() @@ -282,19 +282,19 @@ export function createConfigControl(opts) { /** * Boot consistency guard (#141). The active slot's etag must never - * equal a remembered `bad_etag`: that contradiction — a config marked - * bad yet still operative — wedges central, because the bad-etag + * equal a remembered `bad_etag`: that contradiction: a config marked + * bad yet still operative: wedges central, because the bad-etag * backoff then refuses to re-apply the running revision and no boot * recovers. It is produced by a no-op rollback that had no distinct * slot to flip to (the rollback guard above now prevents new ones, but * existing wedged hosts and hand-edited state must still recover). * Recover by falling back to the join seed if one survives, else - * dropping the contradictory `bad_etag` so the next poll can re-pull — + * dropping the contradictory `bad_etag` so the next poll can re-pull - * the operative config keeps running either way. * * @param {ConfigControlState} state * @returns {{ action: 'recovered_bad_active', recovery: 'seed' | 'repull' } | null} - * @ref LLP 0025#last-known-good-rollback [constrained-by] — the active slot may never carry a bad_etag; recover instead of persisting the contradiction (#141) + * @ref LLP 0025#last-known-good-rollback [constrained-by]: the active slot may never carry a bad_etag; recover instead of persisting the contradiction (#141) */ function recoverBadActiveEtag(state) { if (!state.bad_etag) return null @@ -350,9 +350,9 @@ export function createConfigControl(opts) { /** * Arm the in-process probation timer for the active marker, if any. * Expiry rolls back and requests a staged restart onto - * last-known-good. The kernel owns this timer — a wedged central + * last-known-good. The kernel owns this timer: a wedged central * sink is exactly the failure probation must catch. - * @ref LLP 0025#post-apply-probation [implements] — kernel-owned watchdog, independent of the central plugin functioning + * @ref LLP 0025#post-apply-probation [implements]: kernel-owned watchdog, independent of the central plugin functioning */ function armProbationWatchdog() { disarmProbationWatchdog() @@ -387,7 +387,7 @@ export function createConfigControl(opts) { * kernel-killing-but-valid config can crashloop under the service * manager faster than any in-process timer fires, so each relaunch * checks the marker first. - * @ref LLP 0025#post-apply-probation [implements] — probation expiry is evaluated at boot, before plugin activation + * @ref LLP 0025#post-apply-probation [implements]: probation expiry is evaluated at boot, before plugin activation */ async function evaluateAtBoot() { const state = readState() @@ -403,7 +403,7 @@ export function createConfigControl(opts) { if (!marker) return { action: /** @type {const} */ ('none') } // A marker whose slot is not the operative pointer means the apply - // crashed between persisting the marker and flipping — the new + // crashed between persisting the marker and flipping: the new // config never took effect, so there is nothing to probe. if (activeSlot() !== marker.slot) { delete state.probation @@ -428,11 +428,11 @@ export function createConfigControl(opts) { // Confirmation edge: clear the post-apply probation marker on the first // authenticated poll, then fire `onConfirmed` *only* when a marker was - // actually cleared — the active→cleared transition, not every poll. The + // actually cleared: the active→cleared transition, not every poll. The // early `!state.probation` return makes this exactly the edge, so the // daemon can schedule one reconcile pass per confirmation without polling // status each tick. apply.js stays ignorant of the reconciler. - // @ref LLP 0041#when-the-reconciler-runs-lifecycle-integration [implements] — onConfirmed fires once on the probation active→cleared edge so the daemon schedules a reconcile pass without per-tick status polling + // @ref LLP 0041#when-the-reconciler-runs-lifecycle-integration [implements]: onConfirmed fires once on the probation active→cleared edge so the daemon schedules a reconcile pass without per-tick status polling function confirmPoll() { const state = readState() if (!state.probation) return @@ -513,11 +513,11 @@ export function createConfigControl(opts) { // Shape-gate, then install, then full validation. Catalog-backed // validation can only know a plugin once it is installed, so a // served config naming a not-yet-installed plugin must install - // first — but install must not act on an arbitrary document, so + // first: but install must not act on an arbitrary document, so // the shape (including the pin fields' types) is checked before // anything is fetched, and the hash pin bounds what an install // can bring in. - // @ref LLP 0025#install-on-config-hash-pinned [implements] — shape-gate → install pinned plugins → validate against the post-install catalog + // @ref LLP 0025#install-on-config-hash-pinned [implements]: shape-gate → install pinned plugins → validate against the post-install catalog const shape = parseConfigShape(document) if (!shape.ok) { const first = shape.errors[0] @@ -600,7 +600,7 @@ export function createConfigControl(opts) { * @param {HypAwareV2Config} config * @param {string} serialized * @param {string} etag - * @ref LLP 0025#apply-semantics-staged-restart [implements] — A/B slots with an atomic pointer; never live-mutate; restart does the activation + * @ref LLP 0025#apply-semantics-staged-restart [implements]: A/B slots with an atomic pointer; never live-mutate; restart does the activation */ function commit(config, serialized, etag) { fs.mkdirSync(controlDir, { recursive: true, mode: 0o700 }) @@ -647,11 +647,11 @@ export function createConfigControl(opts) { flipPointer(target) - // First successful apply retires the seed file — its bytes survive + // First successful apply retires the seed file: its bytes survive // in slot 'a' as the rollback target, and the policy token it // carried no longer needs to sit on disk (identity.json carries the // JWT from here on). - // @ref LLP 0031#physical-layout [implements] — seed retired after first successful apply + // @ref LLP 0031#physical-layout [implements]: seed retired after first successful apply if (firstApplyOverSeed) { fs.rmSync(seedPath, { force: true }) } @@ -730,13 +730,13 @@ function readRunningEtag(controlDir) { } /** - * Read-only view of the apply engine's state for `hypaware status` — + * Read-only view of the apply engine's state for `hypaware status`: * usable from any process (the CLI is not the daemon), so it never * constructs the engine or takes its hooks. * * @param {{ stateRoot: string }} args * @returns {ConfigControlStatus} - * @ref LLP 0025#last-known-good-rollback [implements] — operator-visible probation/rollback/bad-etag state without log spelunking + * @ref LLP 0025#last-known-good-rollback [implements]: operator-visible probation/rollback/bad-etag state without log spelunking */ export function readConfigControlStatus({ stateRoot }) { const controlDir = path.join(stateRoot, CONTROL_DIRNAME) @@ -745,7 +745,7 @@ export function readConfigControlStatus({ stateRoot }) { try { state = readControlState(path.join(controlDir, STATE_BASENAME)) } catch { - // unreadable state surfaces as empty — status is best-effort + // unreadable state surfaces as empty: status is best-effort } return { probation: state.probation ?? null, @@ -758,7 +758,7 @@ export function readConfigControlStatus({ stateRoot }) { /** * Extract the config pull cadence from the staged document's central * sink block to size the probation window. The window must track the - * *new* config's cadence — that is the sink that will (or won't) + * *new* config's cadence: that is the sink that will (or won't) * confirm the poll. Knowing the first-party plugin name here mirrors * the client-descriptor precedent in `plugin_catalog.js`. * diff --git a/src/core/config/apply_deps.js b/src/core/config/apply_deps.js index 3dee20bb..e2ad91b9 100644 --- a/src/core/config/apply_deps.js +++ b/src/core/config/apply_deps.js @@ -27,7 +27,7 @@ import { getEntry } from '../plugin_install/lock.js' * The live `configRegistry` (the kernel registry the active plugins * registered their `config_sections` validators into during boot) is * threaded through so apply-time validation actually dispatches to those - * per-plugin validators. Omitting it makes them dead — a served config with + * per-plugin validators. Omitting it makes them dead: a served config with * a malformed plugin `config` block (e.g. claude/codex `backfill`) would be * accepted instead of rejected (LLP 0037). * @@ -68,7 +68,7 @@ export function buildConfigApplyDeps(opts) { // config that first introduces a backfill-capable plugin (e.g. // `@hypaware/claude`) would otherwise skip its `config.backfill` // validation. Discover the section validators for any introduced plugin - // from disk (side-effect-free — never runs `activate()`) and route each + // from disk (side-effect-free, never runs `activate()`) and route each // plugin to the right source: live for active, discovered for introduced. const allManifests = [...bundled.loaded, ...bundled.excluded, ...installed.loaded] const sectionRegistry = await buildSectionRegistry({ @@ -94,7 +94,7 @@ export function buildConfigApplyDeps(opts) { * * @param {PluginConfigInstance[]} entries * @returns {Promise} - * @ref LLP 0025#install-on-config-hash-pinned [implements] — existing LLP 0007 install path; hash mismatch is an apply failure + * @ref LLP 0025#install-on-config-hash-pinned [implements]: existing LLP 0007 install path; hash mismatch is an apply failure */ async function installPinnedPlugins(entries) { const { bundled, installed } = await discover() @@ -111,7 +111,7 @@ export function buildConfigApplyDeps(opts) { const bundledVersion = bundledVersions.get(entry.name) if (bundledVersion !== undefined) { - // @ref LLP 0025#bundled-first-party-plugins [implements] — version checked strictly, artifact hash not checked for bundled plugins + // @ref LLP 0025#bundled-first-party-plugins [implements]: version checked strictly, artifact hash not checked for bundled plugins if (entry.version !== undefined && entry.version !== bundledVersion) { return { ok: false, @@ -134,7 +134,7 @@ export function buildConfigApplyDeps(opts) { stateDir: stateRoot, ...(entry.version !== undefined ? { opts: { ref: `v${entry.version}` } } : {}), // The hash pin is verified against the staged artifact before - // the install commits — nothing may substitute code after the + // the install commits: nothing may substitute code after the // config was authored. confirm: async (staged) => { if (entry.artifact_hash !== undefined && staged.contentHash !== entry.artifact_hash) { diff --git a/src/core/config/backfill_policy.js b/src/core/config/backfill_policy.js index 0f821d1b..6a83d8ca 100644 --- a/src/core/config/backfill_policy.js +++ b/src/core/config/backfill_policy.js @@ -6,7 +6,7 @@ /** * Read a plugin entry's `backfill` policy block (LLP 0037) as a - * tri-state. The single source of truth for interpreting the block — + * tri-state. The single source of truth for interpreting the block: * shared by the reconciler (`action_backfill.js`, which decides whether to * run an import) and the status surface (`status.js`, which renders * `pending`/`n/a`) so the two can never disagree on what a given block @@ -21,12 +21,12 @@ * as an opt-out (`onJoin: false`), never as "default on". The operator * clearly intended to set the flag, and a potentially months-deep import is * the wrong thing to run on a malformed opt-out. (With the per-plugin - * validator now live — see apply/boot wiring — such a config is rejected at + * validator now live (see apply/boot wiring), such a config is rejected at * apply time anyway; this is the belt-and-braces read both consumers share.) * * @param {PluginConfigInstance | undefined} entry * @returns {{ onJoin: boolean | undefined, windowDays: number | undefined }} - * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [constrained-by] — backfill policy ({ on_join, window_days }) is owned by the plugin; the kernel only reads it + * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [constrained-by]: backfill policy ({ on_join, window_days }) is owned by the plugin; the kernel only reads it */ export function readBackfillPolicy(entry) { const config = entry?.config diff --git a/src/core/config/discover_section_validators.js b/src/core/config/discover_section_validators.js index 3b22e0c5..479739b9 100644 --- a/src/core/config/discover_section_validators.js +++ b/src/core/config/discover_section_validators.js @@ -14,7 +14,7 @@ import { createConfigRegistry } from './schema.js' /** * Discover the per-plugin `config_sections` validators a set of plugins * expose, WITHOUT activating them. A plugin opts in by exporting a - * `configSection` (`{ section, validate }`) from its manifest entrypoint — + * `configSection` (`{ section, validate }`) from its manifest entrypoint: * the same registration it hands `ctx.configRegistry.registerSection` at * activation, surfaced as a side-effect-free export. * @@ -27,18 +27,18 @@ import { createConfigRegistry } from './schema.js' * * Used by the apply path so a central config that *introduces* a * backfill-capable plugin (e.g. `@hypaware/claude`) has its `config.backfill` - * block validated even though that plugin isn't active yet — closing the gap + * block validated even though that plugin isn't active yet, closing the gap * where the live registry only carries validators for already-active plugins * (LLP 0037). * * Best-effort: an entrypoint that can't be imported, or that exports no - * `configSection`, contributes nothing and is logged but never throws — + * `configSection`, contributes nothing and is logged but never throws: * discovery must never fail an apply on its own; real activation at the next * boot remains the backstop. * * @param {{ manifests: LoadedManifest[] }} args * @returns {Promise} - * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements] — discover the owning plugin's `backfill` validator for not-yet-active plugins, side-effect-free + * @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements]: discover the owning plugin's `backfill` validator for not-yet-active plugins, side-effect-free */ export async function discoverConfigSectionValidators({ manifests }) { const registry = createConfigRegistry() diff --git a/src/core/config/schema.js b/src/core/config/schema.js index 2644e833..a0a0f768 100644 --- a/src/core/config/schema.js +++ b/src/core/config/schema.js @@ -40,7 +40,7 @@ export function defaultConfigPath(hypHome) { /** * Guard a write to the user-owned **local** config layer - * (`hypaware-config.json`) so `init` cannot silently clobber it — the + * (`hypaware-config.json`) so `init` cannot silently clobber it: the * remaining, non-destructive half of #111 (`join` no longer writes here; * `init` still can). Behaviour: * @@ -60,7 +60,7 @@ export function defaultConfigPath(hypHome) { * now?: () => number, * }} args * @returns {Promise} - * @ref LLP 0031#local-layer-writers [implements] — init overwrite safety: refuse / --force / backup (non-interactive), prompt (interactive) + * @ref LLP 0031#local-layer-writers [implements]: init overwrite safety: refuse / --force / backup (non-interactive), prompt (interactive) */ export async function prepareLocalConfigWrite({ targetPath, force, confirmOverwrite, now }) { let exists = true @@ -80,7 +80,7 @@ export async function prepareLocalConfigWrite({ targetPath, force, confirmOverwr return { proceed: false, message: - `refusing to overwrite existing config at ${targetPath} — ` + + `refusing to overwrite existing config at ${targetPath} - ` + `pass --force to replace it (a timestamped .bak copy is written first)`, } } @@ -189,7 +189,7 @@ export async function loadConfigFile(configPath) { * * @param {unknown} value * @returns {{ ok: true, config: HypAwareV2Config } | { ok: false, errors: ValidationError[] }} - * @ref LLP 0010#no-mode-field [implements] — v2 shape: version must be 2, explicit plugins[], no mode/role label + * @ref LLP 0010#no-mode-field [implements]: v2 shape: version must be 2, explicit plugins[], no mode/role label */ export function parseConfigShape(value) { /** @type {ValidationError[]} */ @@ -304,7 +304,7 @@ const RECOGNIZED_TOP_KEYS = new Set(['version', 'plugins', 'sinks', 'query', 'di * list: () => ConfigSectionRegistration[], * getDefaults: (plugin: PluginName) => JsonObject|undefined, * }} - * @ref LLP 0010#validation [implements] — each plugin validates its own config section through this registry + * @ref LLP 0010#validation [implements]: each plugin validates its own config section through this registry */ export function createConfigRegistry() { /** @type {Map} */ @@ -342,7 +342,7 @@ export function createConfigRegistry() { const reg = sections.get(pluginName) if (!reg) { // No registered section means the kernel has nothing plugin-specific - // to enforce — return ok so cross-plugin validation can proceed. + // to enforce: return ok so cross-plugin validation can proceed. return { ok: true } } const result = reg.validate(config, { pluginName, pointer: `/plugins/<${pluginName}>/config` }) @@ -419,7 +419,7 @@ function parsePluginEntry(entry, pointer, errors) { * @param {string} pointer * @param {ValidationError[]} errors * @returns {SinkConfigInstance|undefined} - * @ref LLP 0014#config-two-shapes [implements] — blob sink = writer+destination; request sink = one-piece plugin; never both + * @ref LLP 0014#config-two-shapes [implements]: blob sink = writer+destination; request sink = one-piece plugin; never both */ function parseSinkEntry(entry, pointer, errors) { if (!isPlainObject(entry)) { @@ -506,7 +506,7 @@ function parseQueryConfig(obj, pointer, errors) { } } - // remotes{} — named MCP targets for `--remote`. The URL is non-secret and + // remotes{}: named MCP targets for `--remote`. The URL is non-secret and // committable; the token is never config (secrets-never-in-config). if (obj.remotes !== undefined) { if (!isPlainObject(obj.remotes)) { diff --git a/src/core/config/validate.js b/src/core/config/validate.js index feed64f7..0e386641 100644 --- a/src/core/config/validate.js +++ b/src/core/config/validate.js @@ -108,7 +108,7 @@ export function firstPartyPluginMetadata() { * cross-plugin validator so sink-pair and capability-ambiguity checks * work the same way they do for bundled plugins. * - * First-party metadata always wins on collision — the boot path already + * First-party metadata always wins on collision: the boot path already * rejects installed plugins that shadow first-party names, but the * helper is defensive in case it is called outside the boot path (e.g. * `hyp config validate` from a host that has not booted). @@ -129,7 +129,7 @@ export function mergeInstalledManifestsIntoKnown(installedManifests, base) { /** * Derive a `PluginMetadata` snapshot from a plugin manifest. Picks up - * capability `provides` / `requires` only — schema-validated upstream + * capability `provides` / `requires` only, schema-validated upstream * by `validateManifest` so the casts here are safe. * * @param {PluginManifest} manifest @@ -152,14 +152,14 @@ function pluginMetadataFromManifest(manifest) { /** * Run the cross-plugin checks and return the raw error list, without the * `config.validate` span or per-error logging. The synchronous, quiet - * core that `validateConfig` wraps — used by the boot-time layer merge, + * core that `validateConfig` wraps, used by the boot-time layer merge, * which re-validates candidate merges many times and must stay silent and * cheap (no telemetry spam, no async). * * @param {HypAwareV2Config} config * @param {ValidateContext} [ctx] * @returns {ConfigValidationError[]} - * @ref LLP 0010#validation [implements] — the same cross-plugin checks, sans span/logging + * @ref LLP 0010#validation [implements]: the same cross-plugin checks, sans span/logging */ export function collectConfigErrors(config, ctx = {}) { const knownPlugins = ctx.knownPlugins ?? firstPartyPluginMetadata() @@ -212,7 +212,7 @@ export function collectConfigErrors(config, ctx = {}) { * @param {HypAwareV2Config} config * @param {ValidateContext} [ctx] * @returns {Promise} - * @ref LLP 0010#validation [implements] — core's cross-plugin checks run after all manifests are loaded + * @ref LLP 0010#validation [implements]: core's cross-plugin checks run after all manifests are loaded */ export async function validateConfig(config, ctx = {}) { const knownPlugins = ctx.knownPlugins ?? firstPartyPluginMetadata() @@ -340,7 +340,7 @@ function checkSinks(config, knownPlugins, errors) { * @param {BlobSinkConfigInstance} sink * @param {Map} knownPlugins * @param {ConfigValidationError[]} errors - * @ref LLP 0014#bytes-flow-down-semantics-flow-up [implements] — writer requires blob-store; rejects writer paired with an http-endpoint + * @ref LLP 0014#bytes-flow-down-semantics-flow-up [implements]: writer requires blob-store; rejects writer paired with an http-endpoint */ function checkBlobSink(name, sink, knownPlugins, errors) { const pointer = `/sinks/${name}` @@ -378,7 +378,7 @@ function checkBlobSink(name, sink, knownPlugins, errors) { errorKind: 'sink_writer_invalid', message: `sink '${name}': writer '${sink.writer}' provides neither ${CAP_ENCODER} nor ${CAP_TABLE_FORMAT}` + - ` — blob sinks need an encoder or table-format writer`, + ` - blob sinks need an encoder or table-format writer`, }) return } @@ -400,7 +400,7 @@ function checkBlobSink(name, sink, knownPlugins, errors) { if (!destProvidesBlob) { const destProvidesHttp = !!destMeta.provides?.[CAP_HTTP_ENDPOINT] const hint = destProvidesHttp - ? ` (provides ${CAP_HTTP_ENDPOINT} instead — only request sinks accept it)` + ? ` (provides ${CAP_HTTP_ENDPOINT} instead - only request sinks accept it)` : '' errors.push({ pointer: `${pointer}/destination`, @@ -428,7 +428,7 @@ function checkBlobSink(name, sink, knownPlugins, errors) { if (!destProvidesBlob) { const destProvidesHttp = !!destMeta.provides?.[CAP_HTTP_ENDPOINT] const hint = destProvidesHttp - ? ` (provides ${CAP_HTTP_ENDPOINT} instead — only request sinks accept it)` + ? ` (provides ${CAP_HTTP_ENDPOINT} instead - only request sinks accept it)` : '' errors.push({ pointer: `${pointer}/destination`, @@ -499,7 +499,7 @@ function checkRequestSink(name, sink, knownPlugins, errors) { * @param {unknown} schedule * @param {string} pointer * @param {ConfigValidationError[]} errors - * @ref LLP 0014#config-two-shapes [implements] — schedule is a 5-field cron; friendly DSLs (@hourly) are rejected + * @ref LLP 0014#config-two-shapes [implements]: schedule is a 5-field cron; friendly DSLs (@hourly) are rejected */ function checkSchedule(name, schedule, pointer, errors) { if (schedule === undefined) return @@ -681,7 +681,7 @@ function clientDescriptorsWithFallback(clientDescriptors) { * it" lines. * * Client and upstream checks use the plugin catalog's client - * descriptors rather than hardcoded plugin names — any plugin that + * descriptors rather than hardcoded plugin names, any plugin that * declares a `contributes.client` with `required_upstreams` gets the * same diagnostic coverage. * @@ -712,7 +712,7 @@ export function diagnoseV1Config(config, ctx = {}) { kind: 'client_without_gateway', pointer: pluginPointer(config, pluginName), message: - `client plugin '${pluginName}' is enabled but '${AI_GATEWAY_PLUGIN}' is not — ` + + `client plugin '${pluginName}' is enabled but '${AI_GATEWAY_PLUGIN}' is not - ` + `attach commands will fail until the gateway is enabled.`, repair: [ `hyp init --from-file # re-run picker to add the gateway`, @@ -733,7 +733,7 @@ export function diagnoseV1Config(config, ctx = {}) { kind: `gateway_missing_${primaryUpstream}_upstream`, pointer: pluginPointer(config, AI_GATEWAY_PLUGIN), message: - `'${pluginName}' is enabled but the gateway has no ${upstreamList} upstream — ` + + `'${pluginName}' is enabled but the gateway has no ${upstreamList} upstream - ` + `${clientName} requests will have nowhere to forward.`, repair: [ `hyp init --from-file # re-run picker to add the upstream`, @@ -759,7 +759,7 @@ export function diagnoseV1Config(config, ctx = {}) { pointer: `/sinks/${name}`, message: `sink '${name}' targets '${destination}' but no encoder plugin ` + - `(${[...encoderPlugins].join(' or ')}) is enabled — local export will produce no files.`, + `(${[...encoderPlugins].join(' or ')}) is enabled - local export will produce no files.`, repair: [ `hyp init --from-file # re-run picker and pick "local Parquet export"`, ], diff --git a/src/core/daemon/linux.js b/src/core/daemon/linux.js index b7ed6c04..a264c677 100644 --- a/src/core/daemon/linux.js +++ b/src/core/daemon/linux.js @@ -175,7 +175,7 @@ export function unitPathFor(unitDir, label = SYSTEMD_UNIT_BASE) { * * @param {PlanSystemdInstallOptions} options * @returns {SystemdInstallPlan} - * @ref LLP 0017#install-global-package-then-service-manager [implements] — systemd user unit pointed at the stable global binary, never an npx path + * @ref LLP 0017#install-global-package-then-service-manager [implements]: systemd user unit pointed at the stable global binary, never an npx path */ export function planSystemdInstall(options) { const label = options.label ?? SYSTEMD_UNIT_BASE @@ -265,7 +265,7 @@ export async function installSystemdUnit(options) { /** * Stop, disable, and remove a HypAware systemd unit. Tolerates * already-stopped / already-disabled state and a missing unit file. - * Removes only the service artifact — config, recordings, and logs are + * Removes only the service artifact. Config, recordings, and logs are * left untouched. * * @param {{ label?: string, unitDir?: string, homeDir?: string, systemctl?: SystemctlAdapter }} options diff --git a/src/core/daemon/logs.js b/src/core/daemon/logs.js index 13b4fa1d..902c3e48 100644 --- a/src/core/daemon/logs.js +++ b/src/core/daemon/logs.js @@ -5,7 +5,7 @@ import path from 'node:path' /** * Resolve the directory the daemon writes its dispatch / lifecycle - * log to — `/hypaware/logs`. OTel logs continue to land in + * log to: `/hypaware/logs`. OTel logs continue to land in * `dev-telemetry/` (when `HYP_DEV_TELEMETRY=1`) or be exported over * OTLP; this file is the line-oriented sidecar a human inspects with * `tail -f` after `daemon run`. @@ -54,7 +54,7 @@ export function openDaemonLog({ stateRoot, runId, mode }) { try { stream.write(JSON.stringify(record) + '\n') } catch { - // best-effort — daemon must not crash because the log device is + // best-effort. Daemon must not crash because the log device is // full or detached. } } @@ -68,7 +68,7 @@ export function openDaemonLog({ stateRoot, runId, mode }) { // Resolve only once the stream has flushed every buffered line to // disk. The daemon awaits this before resolving `done` / exiting, so a // boot health event or shutdown line written moments earlier is never - // lost to a half-flushed buffer — under load `stream.end()` alone + // lost to a half-flushed buffer. Under load, `stream.end()` alone // returns before the writes land, which intermittently dropped the // `daemon.degraded` line the #138 regression reads back. return new Promise((resolve) => { diff --git a/src/core/daemon/macos.js b/src/core/daemon/macos.js index 15a54095..84ab0d3b 100644 --- a/src/core/daemon/macos.js +++ b/src/core/daemon/macos.js @@ -187,7 +187,7 @@ function defaultUserDomain(uid) { * * @param {PlanLaunchAgentInstallOptions} options * @returns {LaunchAgentInstallPlan} - * @ref LLP 0017#install-global-package-then-service-manager [implements] — launchd LaunchAgent pointed at the stable global binary, never an npx path + * @ref LLP 0017#install-global-package-then-service-manager [implements]: launchd LaunchAgent pointed at the stable global binary, never an npx path */ export function planLaunchAgentInstall(options) { const label = options.label ?? LAUNCH_LABEL @@ -272,15 +272,15 @@ function isTransientBootstrapError(res) { /** * Install or refresh a HypAware LaunchAgent. Idempotent: if the agent is - * already loaded it is booted out first — and we wait for launchd to fully - * release it — before the new plist is written and bootstrapped back in. + * already loaded it is booted out first, and we wait for launchd to fully + * release it before the new plist is written and bootstrapped back in. * Bootstrap retries the transient EIO (`error 5`) launchd raises while an * unfinished teardown still holds the label, so a reinstall over a live * agent doesn't fail; genuine load errors still surface immediately. * * @param {PlanLaunchAgentInstallOptions & { launchctl?: LaunchctlAdapter, userDomain?: string, sleep?: (ms: number) => Promise }} options * @returns {Promise} - * @ref LLP 0017#reinstall-waits-for-launchd-release [implements] — bootout is async; poll until released + bounded EIO retry + * @ref LLP 0017#reinstall-waits-for-launchd-release [implements]: bootout is async; poll until released + bounded EIO retry */ export async function installLaunchAgent(options) { const plan = planLaunchAgentInstall(options) @@ -321,8 +321,8 @@ export async function installLaunchAgent(options) { /** * Boot out and remove a HypAware LaunchAgent. Tolerates already-unloaded - * state and a missing plist file. Removes only the service artifact — - * config, recordings, and logs are left untouched. + * state and a missing plist file. Removes only the service artifact. + * Config, recordings, and logs are left untouched. * * @param {{ label?: string, plistDir?: string, homeDir?: string, launchctl?: LaunchctlAdapter, userDomain?: string }} options * @returns {Promise} diff --git a/src/core/daemon/pid.js b/src/core/daemon/pid.js index c3442f9f..b08c4330 100644 --- a/src/core/daemon/pid.js +++ b/src/core/daemon/pid.js @@ -8,7 +8,7 @@ import path from 'node:path' */ /** - * Resolve the directory the daemon uses for runtime state files — + * Resolve the directory the daemon uses for runtime state files: * `/hypaware/run`. The dispatcher passes `stateRoot` * (`/hypaware`); this returns the `run` child. * @@ -31,8 +31,8 @@ export function pidFilePath(stateRoot) { /** * Write a PID file atomically (write to `.tmp`, then rename). The - * caller is the running daemon, so we crash hard on any I/O failure — - * a daemon that cannot record its PID has nothing for `daemon stop` + * caller is the running daemon, so we crash hard on any I/O failure. + * A daemon that cannot record its PID has nothing for `daemon stop` * to target later. * * @param {string} stateRoot @@ -51,8 +51,8 @@ export function writePidFile(stateRoot, entry) { /** * Read the current PID file, returning `null` when no daemon has - * claimed this `HYP_HOME` (no file). Malformed files raise — the - * caller's job is to surface "daemon state is corrupt" to the user + * claimed this `HYP_HOME` (no file). Malformed files raise errors. + * The caller's job is to surface "daemon state is corrupt" to the user * rather than silently swallowing it. * * @param {string} stateRoot @@ -81,8 +81,8 @@ export function readPidFile(stateRoot) { } /** - * Best-effort delete of the PID file. Silent when nothing is there — - * shutdown should not throw if a parallel `daemon stop` already + * Best-effort delete of the PID file. Silent when nothing is there. + * Shutdown should not throw if a parallel `daemon stop` already * cleaned up. * * @param {string} stateRoot diff --git a/src/core/daemon/platform.js b/src/core/daemon/platform.js index c40baf2d..40cb7c79 100644 --- a/src/core/daemon/platform.js +++ b/src/core/daemon/platform.js @@ -101,7 +101,7 @@ export function daemonKindLabel(platform = process.platform) { /** * @param {NodeJS.Platform} [platform] * @returns {boolean} - * @ref LLP 0017#install-global-package-then-service-manager [constrained-by] — V1 service install targets macOS launchd + Linux systemd only + * @ref LLP 0017#install-global-package-then-service-manager [constrained-by]: V1 service install targets macOS launchd + Linux systemd only */ export function platformIsSupported(platform = process.platform) { return platform === 'darwin' || platform === 'linux' diff --git a/src/core/daemon/runtime.js b/src/core/daemon/runtime.js index be707625..6b7453ef 100644 --- a/src/core/daemon/runtime.js +++ b/src/core/daemon/runtime.js @@ -50,20 +50,20 @@ const MIN_TICK_INTERVAL_MS = 25 /** * Exit code a foreground daemon uses to request its own relaunch after - * a staged config apply or rollback (EX_TEMPFAIL — "try again"). The + * a staged config apply or rollback (EX_TEMPFAIL, "try again"). The * service managers relaunch on any exit (`KeepAlive` / * `Restart=always`); foreground invokers (smoke harness, dev shells) * loop on this specific code. - * @ref LLP 0017#staged-restart-for-config-replacement [implements] — a foreground daemon cannot relaunch itself; the invoker loops on this code + * @ref LLP 0017#staged-restart-for-config-replacement [implements]: a foreground daemon cannot relaunch itself; the invoker loops on this code */ export const DAEMON_RESTART_EXIT_CODE = 75 /** * Boot the kernel, start every configured source, and run sink ticks * on a fixed cadence. Returns a `DaemonHandle` the caller can use to - * `stop()` the daemon or read the latest `snapshot()` — both used by + * `stop()` the daemon or read the latest `snapshot()` (both used by * the smoke flow to drive a deterministic start/stop without sending - * real OS signals into the test process. + * real OS signals into the test process). * * Lifecycle (all under a single `daemon.run` root span): * @@ -76,7 +76,7 @@ export const DAEMON_RESTART_EXIT_CODE = 75 * using the per-plugin activation context captured on the runtime. * 4. Once every configured source returns a `StartedSource`, status * flips to `healthy`. Failures degrade the state to `degraded` - * but do not abort the daemon — operators get a partial system. + * but do not abort the daemon. Operators get a partial system. * 5. A 60s (or `tickIntervalMs`) loop drives the sink driver. Each * tick is a `sink.tick` child span; the bundled sink driver opens * its own `sink.export_batch` spans inside. @@ -94,7 +94,7 @@ export const DAEMON_RESTART_EXIT_CODE = 75 * * @param {RunDaemonOptions} [opts] * @returns {Promise} - * @ref LLP 0017#the-primary-daemon [implements] — boots kernel, starts sources, runs the sink tick loop, reloads on SIGHUP + * @ref LLP 0017#the-primary-daemon [implements]: boots kernel, starts sources, runs the sink tick loop, reloads on SIGHUP */ export async function runDaemon(opts = {}) { const env = opts.env ?? process.env @@ -139,7 +139,7 @@ export async function runDaemon(opts = {}) { // Forward reference to the client-action reconcile scheduler. It can only // be built after `boot` resolves (it needs the effective config + the // kernel backfill registry), but the confirmation-edge hook below is wired - // into `configControl` before boot — so the hook calls through this ref and + // into `configControl` before boot, so the hook calls through this ref and // an edge that fires before the scheduler exists is recovered by the // after-activation already-confirmed pass (mirrors `pendingRestart`). /** @type {((reason: string) => void) | null} */ @@ -164,7 +164,7 @@ export async function runDaemon(opts = {}) { // any plugin activates: a kernel-killing-but-valid config that // crashloops under the service manager may never live long enough // for an in-process timer to fire. The central-layer slots, pointer, - // and join seed all live under `/config-control/` — the + // and join seed all live under `/config-control/`: the // engine derives every path from `stateRoot` and never touches the // user-owned local layer (`hypaware-config.json`). // An apply can land while the daemon is still wiring up (the pull @@ -186,9 +186,9 @@ export async function runDaemon(opts = {}) { // schedule one reconcile pass. The pull loop's immediate pull can race // the tail of runDaemon, so an edge before the scheduler is wired is // dropped here and recovered by the after-activation already-confirmed - // pass (probation is cleared by then) — same race handling as + // pass (probation is cleared by then), same race handling as // `pendingRestart`. - // @ref LLP 0041#when-the-reconciler-runs-lifecycle-integration [implements] — the daemon wires onConfirmed to schedule a reconcile pass per confirmation edge; apply.js stays ignorant of the reconciler + // @ref LLP 0041#when-the-reconciler-runs-lifecycle-integration [implements]: the daemon wires onConfirmed to schedule a reconcile pass per confirmation edge; apply.js stays ignorant of the reconciler onConfirmed: () => { if (scheduleReconcile) scheduleReconcile('confirm-edge') }, @@ -283,7 +283,7 @@ export async function runDaemon(opts = {}) { // registered (e.g. claude/codex `backfill` blocks). Without it the // per-plugin validators are dead in production: a served config with a // malformed `backfill` block would be accepted instead of rolled back. - // @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements] — apply-time validation dispatches to the source plugin's own config-section validator + // @ref LLP 0037#per-plugin-config-kernel-generic-reconciler [implements]: apply-time validation dispatches to the source plugin's own config-section validator configControl.attachApplyDeps( buildConfigApplyDeps({ stateRoot, configRegistry: boot.runtime.configRegistry }) ) @@ -295,7 +295,7 @@ export async function runDaemon(opts = {}) { // boot) never performs a machine effect. v1 ships one handler, the // run-once backfill-on-join. Constructed only after boot because a pass // needs the effective config + the kernel backfill registry. - // @ref LLP 0041#the-reconciler-component [implements] — construct the reconciler with [backfillHandler] in the daemon + // @ref LLP 0041#the-reconciler-component [implements]: construct the reconciler with [backfillHandler] in the daemon const actionReconciler = opts.actionReconciler ?? createActionReconciler({ @@ -306,7 +306,7 @@ export async function runDaemon(opts = {}) { /** * Run one reconcile pass against the effective config + backfill registry. - * Never throws — a failed handler is surfaced as a `failed` marker by the + * Never throws. A failed handler is surfaced as a `failed` marker by the * reconciler, and any unexpected error is logged here, so the single-flight * scheduler's rerun loop is never aborted by a pass. * @param {string} reason @@ -332,7 +332,7 @@ export async function runDaemon(opts = {}) { // hypHome this daemon actually booted against, so a spawned // `hyp backfill` imports into the same cache rather than whatever // process.env.HYP_HOME happened to be (LLP 0041 §Run-once flow). - // @ref LLP 0041#run-once-flow-backfill-handler [implements] — the child runs against the daemon's resolved HYP_HOME, not process.env + // @ref LLP 0041#run-once-flow-backfill-handler [implements]: the child runs against the daemon's resolved HYP_HOME, not process.env env: { ...env, HYP_HOME: hypHome }, }) fileLog.info('daemon.reconcile_pass', { @@ -357,10 +357,10 @@ export async function runDaemon(opts = {}) { // After-activation already-confirmed pass: if a central layer is present // and the running config already cleared probation on a prior boot (no // active probation marker now), run one pass to recover anything missed - // while a previous probation was outstanding. A fresh join — probation - // still active — instead waits for the `confirmPoll` edge above. A + // while a previous probation was outstanding. A fresh join (probation + // still active) instead waits for the `confirmPoll` edge above. A // non-joined host has no central layer, so the reconciler stays a no-op. - // @ref LLP 0041#when-the-reconciler-runs-lifecycle-integration [implements] — after-activation already-confirmed pass, gated on a present central layer and no active probation + // @ref LLP 0041#when-the-reconciler-runs-lifecycle-integration [implements]: after-activation already-confirmed pass, gated on a present central layer and no active probation const bootControlStatus = await configControl.status() if (boot.centralConfigPath != null && !bootControlStatus.probation) { reconcileScheduler.schedule('boot-already-confirmed') @@ -395,10 +395,10 @@ export async function runDaemon(opts = {}) { persist() // Derive the boot health event from the SAME aggregate written to // status.json: a degraded boot (any source failed to start) must not log - // `daemon.healthy` — monitoring keyed off that event would read a false - // positive — and a health event never lists a source that failed to + // `daemon.healthy`. Monitoring keyed off that event would read a false + // positive, and a health event never lists a source that failed to // start; it reports only the sources that actually came up. - // @ref LLP 0017#the-primary-daemon [implements] — the boot health event reports the same state as `hyp daemon status` + // @ref LLP 0017#the-primary-daemon [implements]: the boot health event reports the same state as `hyp daemon status` const startedSourceNames = sourceSnapshots .filter((s) => s.state !== 'failed') .map((s) => s.name) @@ -484,7 +484,7 @@ export async function runDaemon(opts = {}) { cacheRoot: boot.runtime.storage.cacheRoot, budgetMs: mCfg.max_tick_ms, config: mCfg, - // @ref LLP 0027#re-settle-sweep — thread the dataset's + // @ref LLP 0027#re-settle-sweep: thread the dataset's // re-settle hook (same enricher the flush path uses) so // compaction can re-settle committed fallback rows split from // their uuid twin. @@ -525,7 +525,7 @@ export async function runDaemon(opts = {}) { await maintenanceInFlight } // Let any in-flight reconcile pass finish so the daemon never exits - // mid-import — abandoning a pass would orphan the spawned `hyp backfill` + // mid-import. Abandoning a pass would orphan the spawned `hyp backfill` // child and interrupt the marker write. await reconcileScheduler.settle() persist({ state: 'stopping' }) @@ -571,7 +571,7 @@ export async function runDaemon(opts = {}) { if (installSignals) { removeSignalHandlers() } - // @ref LLP 0017#staged-restart-for-config-replacement [implements] — the daemon exits and the service manager (or looping invoker) relaunches it + // @ref LLP 0017#staged-restart-for-config-replacement [implements]: the daemon exits and the service manager (or looping invoker) relaunches it resolveDone?.(reason === 'restart' ? DAEMON_RESTART_EXIT_CODE : 0) return done } @@ -594,12 +594,12 @@ export async function runDaemon(opts = {}) { // central config on a joined host and re-open the #111 footgun this // design closes. The central layer is read-only here; only an // *apply* (which triggers a restart, not a reload) rewrites it. - // @ref LLP 0031#two-layers-merged-at-boot [implements] — reload re-runs the two-layer resolution; reload never sees the local layer alone + // @ref LLP 0031#two-layers-merged-at-boot [implements]: reload re-runs the two-layer resolution; reload never sees the local layer alone const resolved = await resolveLayeredConfigForDaemon({ stateRoot, configPath: boot.configPath ?? null, }) - // A broken/missing local layer is loud but not fatal — keep running + // A broken/missing local layer is loud but not fatal. Keep running // on the already-merged config rather than reload from a degraded // view (the central layer always carries the host). if (boot.configPath && resolved.localLoaded && !resolved.localLoaded.ok) { @@ -684,22 +684,22 @@ export async function runDaemon(opts = {}) { /** * Single-flight scheduler for client-action reconcile passes. * - * Each confirmation edge — and the after-activation already-confirmed check — + * Each confirmation edge (and the after-activation already-confirmed check) * calls `schedule()`, which runs `run()` as its own async task **off the * caller's stack**: `schedule()` returns synchronously, so a reconcile pass * (which may spawn a multi-minute `hyp backfill` import) never delays the * sink tick loop or the apply engine's confirm poll. Only one pass runs at a * time; an edge that arrives while a pass is in flight sets a "re-run when * done" flag, coalescing any number of edges during a pass into exactly one - * more pass. Coalescing is lossless because the reconciler is level-triggered - * — the next pass reads the latest config + markers and converges the gap. + * more pass. Coalescing is lossless because the reconciler is level-triggered, + * so the next pass reads the latest config + markers and converges the gap. * * `settle()` resolves when no pass is in flight; the shutdown path awaits it * so the daemon never exits mid-pass. * * @param {{ run: (reason: string) => Promise, log?: { error(message: string, attributes?: Record): void } }} args * @returns {{ schedule: (reason: string) => void, settle: () => Promise }} - * @ref LLP 0041#when-the-reconciler-runs-lifecycle-integration [implements] — single-flight guard: one pass at a time, an edge during a pass re-runs once when done, and the pass is its own async task off the tick loop + * @ref LLP 0041#when-the-reconciler-runs-lifecycle-integration [implements]: single-flight guard: one pass at a time, an edge during a pass re-runs once when done, and the pass is its own async task off the tick loop */ export function createReconcilePassScheduler({ run, log }) { let running = false @@ -781,8 +781,8 @@ function describeSourceStartError(err, source) { /** * Start every registered source that has not auto-started during - * `activate()`. Returns one snapshot per source — including the - * already-started ones so the status file lists everything the + * `activate()`. Returns one snapshot per source (including the + * already-started ones) so the status file lists everything the * operator expects to see. * * @param {{ runtime: KernelRuntime, log: ReturnType, fileLog: ReturnType, sourcePluginByName: Map }} args @@ -891,8 +891,8 @@ async function stopAllSources({ runtime, fileLog }) { } /** - * Best-effort source `.status()` invocation — failures should not - * abort the daemon's snapshot capture. + * Best-effort source `.status()` invocation (failures should not + * abort the daemon's snapshot capture). * * @param {KernelRuntime} runtime * @param {string} name diff --git a/src/core/daemon/status.js b/src/core/daemon/status.js index cb60508d..45ec54d3 100644 --- a/src/core/daemon/status.js +++ b/src/core/daemon/status.js @@ -53,7 +53,7 @@ export function statusFilePath(stateRoot) { /** * Write a status file atomically (write to `.tmp`, then rename). The * smoke harness asserts against this file directly so it must always - * be either absent or fully formed — partial writes would race the + * be either absent or fully formed. Partial writes would race the * SIGTERM assertion. * * @param {string} stateRoot @@ -69,7 +69,7 @@ export function writeStatusFile(stateRoot, status) { /** * Read the status file. Returns `null` when no daemon has run for - * this `HYP_HOME` yet — `hyp daemon status` surfaces that as + * this `HYP_HOME` yet. `hyp daemon status` surfaces that as * "daemon: not started" rather than an error. * * @param {string} stateRoot @@ -115,9 +115,9 @@ export async function collectHypAwareStatus(opts = {}) { // ----- config (LLP 0031: central ⊕ local) ----- // The user-facing config path is the local layer; the central layer is - // resolved read-only from config-control/ (active slot or join seed) — - // reading it never fires a config poll. What's "running" is the merge. - // @ref LLP 0031#status-provenance [implements] — restore inspectability: provenance tags + dropped-local section over the merged config + // resolved read-only from config-control/ (active slot or join seed). + // Reading it never fires a config poll. What's "running" is the merge. + // @ref LLP 0031#status-provenance [implements]: Restore inspectability: provenance tags + dropped-local section over the merged config const configPath = env.HYP_CONFIG ? path.resolve(env.HYP_CONFIG) : defaultConfigPath(hypHome) @@ -131,7 +131,7 @@ export async function collectHypAwareStatus(opts = {}) { // Build the plugin catalog before the merge so the layer resolution // validates local additions against the same plugin set the daemon - // runs — a local plugin that invalidates the merge (capability tie, + // runs. A local plugin that invalidates the merge (capability tie, // unknown plugin) is dropped here, not surfaced as a config error. /** @type {PluginCatalog | undefined} */ let catalog @@ -151,7 +151,7 @@ export async function collectHypAwareStatus(opts = {}) { catalog = buildPluginCatalog(bundledLoaded, installedLoaded) } catch { /* catalog build failure is non-fatal */ } - // @ref LLP 0031#central-layer-is-sacrosanct [implements] — same merge + validation pruning as boot, so status shows exactly what runs + // @ref LLP 0031#central-layer-is-sacrosanct [implements]: Same merge + validation pruning as boot, so status shows exactly what runs const merged = resolveLayeredConfig({ central: centralConfig, local: localConfig, @@ -179,7 +179,7 @@ export async function collectHypAwareStatus(opts = {}) { // outage. `configExists` tracks whether *anything* is configured. const configExists = config !== null - // Validate the *effective* (merged + pruned) config — that is what runs. + // Validate the *effective* (merged + pruned) config: that is what runs. // After pruning, any error left is the central layer's own (apply-time's // concern); a local entry that lost the merge shows in `layered.drops`, // not here, so it never degrades `overall`. @@ -212,7 +212,7 @@ export async function collectHypAwareStatus(opts = {}) { diagnostics.push({ severity: 'warning', kind: 'config_missing', - message: `no config found — neither a central layer nor ${configPath}`, + message: `no config found - neither a central layer nor ${configPath}`, repair: ['hyp init', 'hyp init --from-file ', 'hyp join '], }) } else { @@ -225,12 +225,12 @@ export async function collectHypAwareStatus(opts = {}) { } } else { // A broken local file with the central layer still carrying the host - // is loud but not an outage — the central layer always boots. + // is loud but not an outage. The central layer always boots. if (!localLoaded.ok && localLoaded.errorKind !== 'config_missing') { diagnostics.push({ severity: 'warning', kind: 'config_local_unreadable', - message: `local config layer is unreadable (${localLoaded.message}) — running on the central layer only`, + message: `local config layer is unreadable (${localLoaded.message}) - running on the central layer only`, repair: ['hyp init --from-file --force'], }) } @@ -310,7 +310,7 @@ export async function collectHypAwareStatus(opts = {}) { } // Fall back to the PID + status files when the installer probe - // didn't already report a live process — covers foreground + // didn't already report a live process. This covers foreground // `hyp daemon run` sessions. if (!daemon.running) { try { @@ -394,7 +394,7 @@ export async function collectHypAwareStatus(opts = {}) { } // Sinks are derived from the loaded config (so the count reflects - // "how many sinks does the user have configured?" — the same number + // "how many sinks does the user have configured?", the same number // a fresh kernel boot or a running daemon would surface). When // matching runtime handles exist on the kernel, layer in the live // instance metadata (plugin / kind) so the report does not lose @@ -449,7 +449,7 @@ export async function collectHypAwareStatus(opts = {}) { diagnostics.push({ severity: 'warning', kind: 'client_attach_missing', - message: `'${descriptor.plugin}' is enabled but ${clientName} settings show no HypAware marker — run 'hyp attach --client ${clientName}'`, + message: `'${descriptor.plugin}' is enabled but ${clientName} settings show no HypAware marker - run 'hyp attach --client ${clientName}'`, repair: [`hyp attach --client ${clientName}`], }) } @@ -478,9 +478,9 @@ export async function collectHypAwareStatus(opts = {}) { // ----- client-action reconciler state (LLP 0036 / 0041) ----- // Read-only marker view; `hyp status` never runs a reconcile pass. A // failed backfill is surfaced here (its own section, below) but is - // deliberately NOT a degrading diagnostic — the gateway runs fine on a + // deliberately NOT a degrading diagnostic. The gateway runs fine on a // valid config (LLP 0041 §failure-is-surfaced-not-fatal). - // @ref LLP 0041#failure-is-surfaced-not-fatal [implements] — surface client-action failure as its own line, never an outage signal + // @ref LLP 0041#failure-is-surfaced-not-fatal [implements]: Surface client-action failure as its own line, never an outage signal /** @type {ClientActionsReport | null} */ let clientActions = null try { @@ -508,11 +508,11 @@ export async function collectHypAwareStatus(opts = {}) { } // Anything that the operator would have to fix to call the install - // "set up" should degrade overall — config errors, v1 inconsistencies, + // "set up" should degrade overall: config errors, v1 inconsistencies, // and the "no config at all yet" case. `client_attach_missing` / // `recent_errors` stay informational so a perfectly-configured-but- // not-yet-attached install can still report healthy. A failed - // client-action (e.g. backfill-on-join) is likewise excluded — it has + // client-action (e.g. backfill-on-join) is likewise excluded. It has // its own status line but never flips `overall` (LLP 0041 // §failure-is-surfaced-not-fatal); note it is not even a diagnostic, so // it cannot reach this computation. @@ -546,25 +546,25 @@ export async function collectHypAwareStatus(opts = {}) { /** * Build the client-action reconciler section for `hyp status` from the * persisted marker store and the effective config. Pure: it reads markers - * and config and never runs a reconcile pass (LLP 0041 — the status surface + * and config and never runs a reconcile pass (LLP 0041, the status surface * "reads the marker file, it never runs a pass"). Returns null when nothing * applies so the V1 status surface is unchanged on an ordinary host. * * Per-provider state: * - `done` / `failed` come straight from a persisted marker (any request * key, even one whose plugin has since left the config). - * - `pending` / `n/a` are derived for *declared* backfill targets — a + * - `pending` / `n/a` are derived for *declared* backfill targets: a * plugin entry carrying its own `config.backfill` block. Backfill * capability is a runtime fact (a registered `BackfillContribution`, * LLP 0041 §per-plugin-capability) the status collector cannot see * without activating plugins, so the declared policy is the honest, - * provider-agnostic signal: `on_join: false` or a non-joined host → - * `n/a` (the reconciler is a no-op); otherwise desired-but-unrun → + * provider-agnostic signal: `on_join: false` or a non-joined host -> + * `n/a` (the reconciler is a no-op); otherwise desired-but-unrun -> * `pending`. * * @param {{ status: ClientActionStatus, config: HypAwareV2Config | null, hasCentral: boolean, backfillPlugins?: Set }} args * @returns {ClientActionsReport | null} - * @ref LLP 0041#idempotency-and-completion-state [implements] — per-provider done/failed/pending/n-a derived from the per-handler/per-request-key marker store, no reconcile pass + * @ref LLP 0041#idempotency-and-completion-state [implements]: Per-provider done/failed/pending/n-a derived from the per-handler/per-request-key marker store, no reconcile pass */ function buildClientActionsReport({ status, config, hasCentral, backfillPlugins }) { /** @type {ClientActionReport[]} */ @@ -573,14 +573,14 @@ function buildClientActionsReport({ status, config, hasCentral, backfillPlugins const backfillCapable = backfillPlugins ?? new Set() // Declared backfill targets: enabled plugin entries that drive - // backfill-on-join (LLP 0037 — policy rides the owning plugin). Two cases: + // backfill-on-join (LLP 0037, policy rides the owning plugin). Two cases: // 1. An explicit `config.backfill` block (any host). - // 2. *Default-on*: a known backfill provider with no explicit block — on + // 2. *Default-on*: a known backfill provider with no explicit block. On // a joined host `backfillHandler.desired()` still emits for it, so it // is a real (pending) target. Status mirrors that here; without this // the default-on case was invisible. It is gated on `hasCentral` so a // non-joined host (where the reconciler never runs) keeps its - // V1-unchanged surface — a bare `claude`/`codex` install shows nothing. + // V1-unchanged surface. A bare `claude`/`codex` install shows nothing. /** @type {Map} */ const declared = new Map() for (const entry of config?.plugins ?? []) { @@ -622,7 +622,7 @@ function buildClientActionsReport({ status, config, hasCentral, backfillPlugins ...(typeof marker.attempts === 'number' ? { attempts: marker.attempts } : {}), }) } else if (marker) { - // `done` (run-once) or `applied` (reversible) — the effect is in place. + // `done` (run-once) or `applied` (reversible), the effect is in place. actions.push({ kind, requestKey, @@ -825,7 +825,7 @@ async function countRecentErrors(telemetryDir) { /** * Map config-validate `error_kind` values to the repair commands * status surfaces alongside them. Returning an empty array is - * acceptable — the renderer just shows the diagnostic without a + * acceptable. The renderer just shows the diagnostic without a * "try this" line. * * @param {ConfigValidationError['errorKind']} kind diff --git a/src/core/iceberg/partition-spec.js b/src/core/iceberg/partition-spec.js index 64ea3295..ba6e5024 100644 --- a/src/core/iceberg/partition-spec.js +++ b/src/core/iceberg/partition-spec.js @@ -1,6 +1,6 @@ // @ts-check -// @ref LLP 0022#shared-core-helpers — partition-spec derivation promoted out of +// @ref LLP 0022#shared-core-helpers: partition-spec derivation promoted out of // the cache; consumed by the cache and the @hypaware/format-iceberg export // alike, so it is core surface, not a cache internal (LLP 0003). [implements] @@ -77,19 +77,19 @@ export function validatePartitionSpecStability(declaration, existingSpec, schema const existing = existingSpec.fields.find(f => f.name === expected.name) if (!existing) { throw new Error( - `cache-iceberg: partition field "${expected.name}" is new — adding a partition field is spec evolution and requires an explicit migration` + `cache-iceberg: partition field "${expected.name}" is new - adding a partition field is spec evolution and requires an explicit migration` ) } if (existing.transform !== expected.transform) { throw new Error( - `cache-iceberg: partition field "${expected.name}" changed transform from ${existing.transform} to ${expected.transform} — partition spec changes require an explicit migration` + `cache-iceberg: partition field "${expected.name}" changed transform from ${existing.transform} to ${expected.transform} - partition spec changes require an explicit migration` ) } } for (const existing of existingSpec.fields) { if (!expectedNames.has(existing.name)) { throw new Error( - `cache-iceberg: partition field "${existing.name}" was removed — removing a partition field is spec evolution and requires an explicit migration` + `cache-iceberg: partition field "${existing.name}" was removed - removing a partition field is spec evolution and requires an explicit migration` ) } } diff --git a/src/core/index.js b/src/core/index.js index a3ae47e0..60f6a37e 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -9,6 +9,6 @@ export * from './observability/index.js' export { createConfigRegistry, defaultConfigPath, loadConfigFile, parseConfigShape, CONFIG_BASENAME } from './config/schema.js' export { validateConfig, firstPartyPluginMetadata, mergeInstalledManifestsIntoKnown, isCronExpression, CAP_ENCODER, CAP_BLOB_STORE, CAP_HTTP_ENDPOINT } from './config/validate.js' export { buildPluginCatalog } from './plugin_catalog.js' -// Iceberg partition-spec helpers — core surface consumed by the cache and the +// Iceberg partition-spec helpers: core surface consumed by the cache and the // @hypaware/format-iceberg export (LLP 0003 / LLP 0022#shared-core-helpers). export { partitionSpecForDeclaration, validatePartitionSpecStability } from './iceberg/partition-spec.js' diff --git a/src/core/manifest.js b/src/core/manifest.js index db54df60..ea8f367a 100644 --- a/src/core/manifest.js +++ b/src/core/manifest.js @@ -105,7 +105,7 @@ export async function loadManifests(rootDirs) { * * @param {unknown} value * @returns {{ ok: true, manifest: PluginManifest } | { ok: false, errorKind: ManifestErrorKind, message: string }} - * @ref LLP 0005#declarative [implements] — one manifest shape declares requires/provides/contributes; category is emergent, not a variant + * @ref LLP 0005#declarative [implements]: one manifest shape declares requires/provides/contributes; category is emergent, not a variant */ export function validateManifest(value) { if (!isPlainObject(value)) { diff --git a/src/core/mcp/client.js b/src/core/mcp/client.js index 676e0f97..2b671bf6 100644 --- a/src/core/mcp/client.js +++ b/src/core/mcp/client.js @@ -1,7 +1,7 @@ // @ts-check /** - * MCP **client** over Streamable HTTP — the consumer half of remote attach + * MCP **client** over Streamable HTTP: the consumer half of remote attach * (LLP 0033). `hyp` is an MCP client, so `hyp --remote ` * runs the verb's operation against the remote tool of the **same** * `inputSchema` instead of locally (LLP 0034 §consumer-side). @@ -12,7 +12,7 @@ * unit-tested through an injectable `fetchImpl`; integration-test it once * the server lands. * - * @ref LLP 0034#pluggable-transport [implements] — the HTTP adapter the fleet server needs, reused on the client side + * @ref LLP 0034#pluggable-transport [implements]: the HTTP adapter the fleet server needs, reused on the client side */ const PROTOCOL_VERSION = '2025-06-18' @@ -67,10 +67,10 @@ export function createHttpMcpClient(opts) { } if (!res.ok) { if (res.status === 401 || res.status === 403) { - throw new Error(`remote rejected the credential (HTTP ${res.status}) — re-run 'hyp remote login'`) + throw new Error(`remote rejected the credential (HTTP ${res.status}) - re-run 'hyp remote login'`) } const text = await safeText(res) - throw new Error(`MCP ${method} failed: HTTP ${res.status}${text ? ` — ${text.slice(0, 200)}` : ''}`) + throw new Error(`MCP ${method} failed: HTTP ${res.status}${text ? ` - ${text.slice(0, 200)}` : ''}`) } const message = await parseRpcResponse(res, id) if (message?.error) { diff --git a/src/core/mcp/jsonrpc.js b/src/core/mcp/jsonrpc.js index 837d1033..f7d2b531 100644 --- a/src/core/mcp/jsonrpc.js +++ b/src/core/mcp/jsonrpc.js @@ -3,11 +3,11 @@ /** * Minimal JSON-RPC 2.0 framing for the hand-rolled MCP host. The kernel * never `npm install`s (LLP 0008), and the protocol surface MCP needs is - * small — `initialize` / `tools/list` / `tools/call` / `resources/*` — so + * small (`initialize` / `tools/list` / `tools/call` / `resources/*`), so * the implementation call here is to hand-roll JSON-RPC rather than add an * SDK dependency (LLP 0034 dependency note). * - * @ref LLP 0034#stdio-stdout-discipline [constrained-by] — JSON-RPC is the stdout channel; helpers here produce only protocol objects + * @ref LLP 0034#stdio-stdout-discipline [constrained-by]: JSON-RPC is the stdout channel; helpers here produce only protocol objects */ export const PARSE_ERROR = -32700 diff --git a/src/core/mcp/proxy.js b/src/core/mcp/proxy.js index 1f6c436e..8f697426 100644 --- a/src/core/mcp/proxy.js +++ b/src/core/mcp/proxy.js @@ -14,7 +14,7 @@ import { serveStdio } from './stdio.js' */ /** - * `hyp mcp --remote ` — the **stdio proxy fallback** (LLP 0034 + * `hyp mcp --remote `: the **stdio proxy fallback** (LLP 0034 * §proxy-fallback). For AI clients that can't add a remote HTTP MCP * directly, this serves a local stdio MCP that transparently forwards each * JSON-RPC message to the remote endpoint, injecting the stored @@ -27,13 +27,13 @@ import { serveStdio } from './stdio.js' * * @param {{ target: string, ctx: CommandRunContext }} args * @returns {Promise} - * @ref LLP 0034#proxy-fallback [implements] — stdio proxy injecting the 0600-stored credential; the fallback, not the primary path + * @ref LLP 0034#proxy-fallback [implements]: stdio proxy injecting the 0600-stored credential; the fallback, not the primary path */ export async function runMcpProxy({ target, ctx }) { const remotes = ctx.config?.query?.remotes ?? {} const entry = remotes[target] if (!entry) { - ctx.stderr.write(`hyp mcp: unknown remote target '${target}' — add it with 'hyp remote add ${target} '\n`) + ctx.stderr.write(`hyp mcp: unknown remote target '${target}' - add it with 'hyp remote add ${target} '\n`) return 2 } const stateDir = readObservabilityEnv(ctx.env).stateDir @@ -52,7 +52,7 @@ export async function runMcpProxy({ target, ctx }) { let sessionId const log = getLogger('mcp') log.info('mcp.proxy_start', { [Attr.COMPONENT]: 'mcp', [Attr.OPERATION]: 'mcp.proxy', target, url: entry.url }) - // Lifecycle line to stderr — stdout is the protocol channel. + // Lifecycle line to stderr: stdout is the protocol channel. ctx.stderr.write(`hyp mcp: proxying stdio → ${entry.url} (target '${target}')\n`) const server = { diff --git a/src/core/mcp/remote_verb.js b/src/core/mcp/remote_verb.js index 379174a2..89f91b34 100644 --- a/src/core/mcp/remote_verb.js +++ b/src/core/mcp/remote_verb.js @@ -16,11 +16,11 @@ import { createHttpMcpClient } from './client.js' * * Surfaces the **server-side cap** (data volume) as a distinct notice line; * the client display budget (context volume) is added separately by the - * renderer — the two truncations of LLP 0033 §two-truncations. + * renderer: the two truncations of LLP 0033 §two-truncations. * * @param {{ verb: VerbRegistration, params: Record, target: string, ctx: CommandRunContext }} args * @returns {Promise<{ ok: true, result: unknown, notices: string[] } | { ok: false, error: string, exitCode?: number }>} - * @ref LLP 0033#two-truncations [implements] — server cap surfaced here as its own line; client cannot lift it + * @ref LLP 0033#two-truncations [implements]: server cap surfaced here as its own line; client cannot lift it */ export async function runRemoteVerb({ verb, params, target, ctx }) { const remotes = ctx.config?.query?.remotes ?? {} @@ -28,7 +28,7 @@ export async function runRemoteVerb({ verb, params, target, ctx }) { if (!entry) { return { ok: false, - error: `unknown remote target '${target}' — add it with 'hyp remote add ${target} '`, + error: `unknown remote target '${target}' - add it with 'hyp remote add ${target} '`, exitCode: 2, } } @@ -70,7 +70,7 @@ function serverCapNotices(structured) { const capPart = cap !== undefined ? ` (server cap rows:${cap})` : '' const shownPart = shown !== undefined ? `showing first ${shown} rows` : 'result truncated' return [ - `remote: ${shownPart}${capPart} — narrow the query, or read the Iceberg archive directly for bulk`, + `remote: ${shownPart}${capPart} - narrow the query, or read the Iceberg archive directly for bulk`, ] } diff --git a/src/core/mcp/server.js b/src/core/mcp/server.js index dfd740d7..26aca83c 100644 --- a/src/core/mcp/server.js +++ b/src/core/mcp/server.js @@ -23,7 +23,7 @@ const SERVER_NAME = 'hypaware' /** * Build the **one server assembly** (verbs → MCP tools, datasets → MCP * resources) the kernel exposes. The transport (stdio here; HTTP later) is - * a thin adapter over this object — the tool list is emergent from the + * a thin adapter over this object: the tool list is emergent from the * verbs the active plugins registered, so a new capability is a new tool * with zero server change (LLP 0034 §pluggable-transport / §tool-exposure-emergent). * @@ -42,7 +42,7 @@ const SERVER_NAME = 'hypaware' * allowOperator?: boolean, * serverVersion?: string, * }} opts - * @ref LLP 0034#tool-auth-class [implements] — read/operator boundary lives on the tool; the credential scope gates it + * @ref LLP 0034#tool-auth-class [implements]: read/operator boundary lives on the tool; the credential scope gates it */ export function createMcpServer(opts) { const { verbs, query, runTool } = opts @@ -78,7 +78,7 @@ export function createMcpServer(opts) { /** * Handle one parsed JSON-RPC message. Returns the response object, or - * `null` for a notification (which gets no reply). Never throws — a + * `null` for a notification (which gets no reply). Never throws: a * tool that throws becomes an `isError` tool result, and an unknown * method a `-32601` error response, so the stream is never corrupted. * @@ -142,7 +142,7 @@ export function createMcpServer(opts) { }) } catch (err) { // A tool execution failure is a tool *result* (isError), not a - // protocol error — the client sees it as a failed call, not a dead + // protocol error: the client sees it as a failed call, not a dead // connection. const text = err instanceof Error ? err.message : String(err) return jsonRpcResult(id, { content: [{ type: 'text', text }], isError: true }) diff --git a/src/core/mcp/stdio.js b/src/core/mcp/stdio.js index 99b119e7..a02ff65a 100644 --- a/src/core/mcp/stdio.js +++ b/src/core/mcp/stdio.js @@ -5,7 +5,7 @@ import readline from 'node:readline' import { PARSE_ERROR, jsonRpcError, parseMessage } from './jsonrpc.js' /** - * Serve an MCP server over a newline-delimited JSON-RPC stdio stream — the + * Serve an MCP server over a newline-delimited JSON-RPC stdio stream: the * default, near-free transport (LLP 0034 §pluggable-transport). Reads one * JSON message per line from `stdin`, dispatches it to `server`, and writes * each response as a single line to `stdout`. @@ -25,7 +25,7 @@ import { PARSE_ERROR, jsonRpcError, parseMessage } from './jsonrpc.js' * onError?: (err: unknown) => void, * }} args * @returns {Promise} - * @ref LLP 0034#stdio-stdout-discipline [implements] — stdout carries only JSON-RPC; one line per message + * @ref LLP 0034#stdio-stdout-discipline [implements]: stdout carries only JSON-RPC; one line per message */ export function serveStdio({ server, stdin, stdout, onError }) { return new Promise((resolve) => { diff --git a/src/core/observability/attrs.js b/src/core/observability/attrs.js index 02ef7a4e..2f3015c4 100644 --- a/src/core/observability/attrs.js +++ b/src/core/observability/attrs.js @@ -55,7 +55,7 @@ function normalizeValue(value) { * * @param {Record} [input] * @returns {Record} - * @ref LLP 0021#the-attribute-contract [implements] — snake_case keys, fixed status set, bounded cardinality + * @ref LLP 0021#the-attribute-contract [implements]: snake_case keys, fixed status set, bounded cardinality */ export function buildAttrs(input) { if (!input || typeof input !== 'object') return {} @@ -80,7 +80,7 @@ export function buildAttrs(input) { * Convenience helpers for the most common attribute keys defined in * the self-instrumentation contract. */ -// @ref LLP 0021#the-attribute-contract — canonical snake_case key vocabulary +// @ref LLP 0021#the-attribute-contract: canonical snake_case key vocabulary export const Attr = Object.freeze({ COMPONENT: 'hyp_component', PLUGIN: 'hyp_plugin', diff --git a/src/core/observability/index.js b/src/core/observability/index.js index a9560059..4accb9c5 100644 --- a/src/core/observability/index.js +++ b/src/core/observability/index.js @@ -18,10 +18,10 @@ let installed = null * Install tracer, logger, and meter providers using a single shared * Resource derived from env. Returns a handle exposing each provider * and a `shutdown()` that flushes and closes exporters in reverse - * order. Idempotent — a second call returns the existing handle. + * order. Idempotent: a second call returns the existing handle. * * @param {{ env?: ObservabilityEnv }} [opts] - * @ref LLP 0021#otel-is-the-substrate [implements] — idempotent install over one shared Resource; safe-by-default tracer + * @ref LLP 0021#otel-is-the-substrate [implements]: idempotent install over one shared Resource; safe-by-default tracer */ export function installObservability(opts = {}) { if (installed) return installed @@ -44,7 +44,7 @@ export function installObservability(opts = {}) { * }} parts */ function buildHandle({ env, resource, tracer, logger, meter }) { - // @ref LLP 0021#shutdown-and-flush [implements] — close exporters reverse order; dev gets 5s budget + forceFlush + // @ref LLP 0021#shutdown-and-flush [implements]: close exporters reverse order; dev gets 5s budget + forceFlush async function shutdown() { const timeoutMs = env.devTelemetry ? 5_000 : 500 for (const reader of meter.readers ?? []) { diff --git a/src/core/observability/jsonl_exporters.js b/src/core/observability/jsonl_exporters.js index 40f53c57..7d56df8b 100644 --- a/src/core/observability/jsonl_exporters.js +++ b/src/core/observability/jsonl_exporters.js @@ -20,7 +20,7 @@ const ExportResultCode = Object.freeze({ * opened lazily on first write so a no-op shutdown doesn't create * empty artifacts. */ -// @ref LLP 0021#shutdown-and-flush [implements] — one file per signal per pid, opened lazily so no-op runs leave none +// @ref LLP 0021#shutdown-and-flush [implements]: one file per signal per pid, opened lazily so no-op runs leave none class JsonlWriter { /** * @param {string} dir @@ -246,7 +246,7 @@ export class JsonlLogRecordExporter { * record per data point, flattened so smoke assertions can query a * single named metric without unpacking the OTel resource metrics tree. */ -// @ref LLP 0021#the-attribute-contract [explains] — flatten to one record per line is why the vocabulary stays bounded +// @ref LLP 0021#the-attribute-contract [explains]: flatten to one record per line is why the vocabulary stays bounded export class JsonlMetricExporter { /** * @param {object} opts diff --git a/src/core/observability/meter.js b/src/core/observability/meter.js index 8ee13dd6..d81d7312 100644 --- a/src/core/observability/meter.js +++ b/src/core/observability/meter.js @@ -21,7 +21,7 @@ const OTLP_EXPORT_TIMEOUT_MS = 1_000 * @param {ObservabilityEnv} args.env * @param {{ attributes: Record }} args.resource * @returns {{ provider: MeterProvider|null, exporters: object[], readers: object[] }} - * @ref LLP 0021#exporter-selection [implements] — mirrors tracer's JSONL/OTLP choice; 250ms dev push for fast smokes + * @ref LLP 0021#exporter-selection [implements]: mirrors tracer's JSONL/OTLP choice; 250ms dev push for fast smokes */ export function installMeterProvider({ env, resource }) { /** @type {object[]} */ @@ -56,7 +56,7 @@ export function installMeterProvider({ env, resource }) { * for things the kernel itself emits. * * @param {{ createCounter(name: string, opts?: object): object, createUpDownCounter(name: string, opts?: object): object, createGauge(name: string, opts?: object): object, createHistogram(name: string, opts?: object): object }} meter - * @ref LLP 0021#summary — kernel pre-declares its own instruments; plugins declare theirs + * @ref LLP 0021#summary: kernel pre-declares its own instruments; plugins declare theirs */ function buildKernelInstruments(meter) { return { @@ -137,7 +137,7 @@ export function getKernelInstruments() { } /** - * Reset the cached instruments (test affordance — kernel boot resets + * Reset the cached instruments (test affordance: kernel boot resets * state when reinitializing the observability layer). */ export function resetKernelInstruments() { diff --git a/src/core/observability/span_helpers.js b/src/core/observability/span_helpers.js index c182fc8c..0984652e 100644 --- a/src/core/observability/span_helpers.js +++ b/src/core/observability/span_helpers.js @@ -22,7 +22,7 @@ import { context, ROOT_CONTEXT, SpanStatusCode } from './runtime.js' * @param {(span: Span) => T|Promise} fn * @param {{ component?: string }} [opts] * @returns {Promise} - * @ref LLP 0021#span-helpers [implements] — inherits active context as parent; records status + error_kind + * @ref LLP 0021#span-helpers [implements]: inherits active context as parent; records status + error_kind */ export async function withSpan(name, attrs, fn, opts = {}) { const tracer = getTracer(opts.component ?? 'kernel') @@ -50,8 +50,8 @@ export async function withSpan(name, attrs, fn, opts = {}) { } /** - * Run `fn` inside a fresh root span — no parent context, no propagated - * trace. Used for unit-of-work that is logically a kernel boot or a + * Run `fn` inside a fresh root span (no parent context, no propagated + * trace). Used for unit-of-work that is logically a kernel boot or a * top-level command invocation. * * @template T @@ -60,7 +60,7 @@ export async function withSpan(name, attrs, fn, opts = {}) { * @param {(span: Span) => T|Promise} fn * @param {{ component?: string }} [opts] * @returns {Promise} - * @ref LLP 0021#span-helpers [implements] — fresh root span (no parent) for boots / top-level commands + * @ref LLP 0021#span-helpers [implements]: fresh root span (no parent) for boots / top-level commands */ export async function runRoot(name, attrs, fn, opts = {}) { const tracer = getTracer(opts.component ?? 'kernel') diff --git a/src/core/observability/tracer.js b/src/core/observability/tracer.js index ab8f54ab..cb9212ba 100644 --- a/src/core/observability/tracer.js +++ b/src/core/observability/tracer.js @@ -28,7 +28,7 @@ const OTLP_EXPORT_TIMEOUT_MS = 1_000 * @param {ObservabilityEnv} args.env * @param {{ attributes: Record }} args.resource * @returns {{ provider: TracerProvider|null, exporters: object[] }} - * @ref LLP 0021#exporter-selection [implements] — three-state JSONL / OTLP / no-op exporter choice + * @ref LLP 0021#exporter-selection [implements]: three-state JSONL / OTLP / no-op exporter choice */ export function installTracerProvider({ env, resource }) { /** @type {object[]} */ @@ -40,7 +40,7 @@ export function installTracerProvider({ env, resource }) { exporters.push(jsonlExporter) } - // @ref LLP 0021#self-loop-guard [constrained-by] — OTLP only when dev telemetry off; never both exporters at once + // @ref LLP 0021#self-loop-guard [constrained-by]: OTLP only when dev telemetry off; never both exporters at once if (!env.devTelemetry && env.otlpEndpoint) { const otlpExporter = new OtlpSpanExporter({ url: env.otlpEndpoint.replace(/\/$/, '') + '/v1/traces', @@ -62,7 +62,7 @@ export function installTracerProvider({ env, resource }) { } /** - * Resolve the active tracer for a component. Always safe to call — + * Resolve the active tracer for a component. Always safe to call: * returns the global no-op tracer when no provider is installed. * @param {string} component */ diff --git a/src/core/plugin_doctor/diagnose.js b/src/core/plugin_doctor/diagnose.js index be0a5007..cf71df0d 100644 --- a/src/core/plugin_doctor/diagnose.js +++ b/src/core/plugin_doctor/diagnose.js @@ -44,7 +44,7 @@ const CONTRIBUTIONS = [ * @param {string} rootDir Absolute path to the plugin directory. * @param {{ knownCapabilities?: Map }} [opts] * `knownCapabilities` maps each capability name any bundled/installed - * plugin provides to the versions it provides — used to resolve + * plugin provides to the versions it provides. This is used to resolve * `requires.capabilities` against their declared semver ranges, and to * pre-seed the dry run. Defaults to empty (every required capability is * then flagged), so callers should pass the catalog map. @@ -151,7 +151,7 @@ async function checkStatic(manifest, rootDir, out) { /** * Categories validated for *shape* only: each entry must be an object - * with a non-empty name field. A superset of CONTRIBUTIONS — it also + * with a non-empty name field. A superset of CONTRIBUTIONS. It also * covers `config_sections` (keyed on `section`), which is excluded from * the declared-vs-registered diff but still must be well-formed, as the * authoring guide promises. @@ -319,11 +319,11 @@ function checkProvidedCapabilities(manifest, registered, out) { } /** - * `requires.capabilities` is a name → semver-range contract, so a + * `requires.capabilities` is a name -> semver-range contract, so a * provider satisfies it only when one of its provided versions falls in * the required range. A name with no provider at all, and a name whose - * providers all fall outside the range, are both `capability_unresolved` - * — runtime resolution would reject either. + * providers all fall outside the range, are both `capability_unresolved`. + * Runtime resolution would reject either. * * @param {PluginManifest} manifest * @param {Map} knownCapabilities diff --git a/src/core/plugin_doctor/dry_run.js b/src/core/plugin_doctor/dry_run.js index 1416d8e7..857c3f19 100644 --- a/src/core/plugin_doctor/dry_run.js +++ b/src/core/plugin_doctor/dry_run.js @@ -32,7 +32,7 @@ const STUB_PROVIDER = '@doctor/stub-provider' * live kernel; * - roots all plugin paths in a fresh `mkdtemp` directory that is * removed before returning, so no state leaks into ``; - * - passes an empty config — a well-behaved plugin registers its + * - passes an empty config. A well-behaved plugin registers its * contributions during `activate()` and defers config reads to * `start()`/`create()`. A plugin that throws on missing config at * activation surfaces as `activate_threw`, which is itself a finding; @@ -52,7 +52,7 @@ const STUB_PROVIDER = '@doctor/stub-provider' * Note: the entrypoint is loaded with dynamic `import()`, which caches * by resolved URL. Each CLI invocation is a fresh process so this never * bites in normal use; within one long-lived process (tests/smokes), - * re-doctoring the *same* path returns the first-loaded module — point + * re-doctoring the *same* path returns the first-loaded module. Point * such callers at distinct directories. * * @param {PluginManifest} manifest @@ -117,7 +117,7 @@ export async function dryRunActivate(manifest, rootDir, opts = {}) { try { await mod.activate(ctx) } catch (err) { - // Snapshot whatever registered before the throw — partial output + // Snapshot whatever registered before the throw. Partial output // still helps locate the failing registration. return { ok: false, @@ -162,13 +162,13 @@ function snapshotRegistry(runtime) { /** * A permissive no-op stand-in for a capability handle that another * plugin would normally provide. Real adapters (e.g. `@hypaware/claude`) - * call methods on the handle *during* `activate()` — - * `gateway.registerUpstreamPreset(...)`, `registerClient(...)`. A plain + * call methods on the handle *during* `activate()`, such as + * `gateway.registerUpstreamPreset(...)` and `registerClient(...)`. A plain * object would throw on the first such call and abort activation before * the plugin registers its own contributions. This Proxy answers every * property access (and call) with itself, so activation runs to * completion and the declared-vs-registered diff stays meaningful. It - * deliberately does NOT model real behavior — the doctor only checks + * deliberately does NOT model real behavior. The doctor only checks * what the plugin registers via `ctx.*`, not what it does with a * capability it requires. * diff --git a/src/core/plugin_doctor/scaffold.js b/src/core/plugin_doctor/scaffold.js index da7a9c7a..430da475 100644 --- a/src/core/plugin_doctor/scaffold.js +++ b/src/core/plugin_doctor/scaffold.js @@ -122,7 +122,7 @@ function sourceActivate(slug) { `/**\n` + ` * Activate the ${slug} plugin.\n` + ` *\n` + - ` * Registers source '${slug}'. The source does not start here —\n` + + ` * Registers source '${slug}'. The source does not start here -\n` + ` * \`start()\` owns the lifecycle and reads config from \`ctx.config\`.\n` + ` *\n` + ` * @param {PluginActivationContext} ctx\n` + @@ -228,7 +228,7 @@ function datasetActivate(slug) { function typesTemplate({ slug, kind }) { return ( `// Shared interfaces for the ${slug} plugin. Define plugin-local\n` + - `// types here and import them with @import JSDoc — do not use\n` + + `// types here and import them with @import JSDoc - do not use\n` + `// @typedef or inline import('...') types (see CLAUDE.md).\n\n` + `export interface ${pascal(slug)}Config {\n` + ` // TODO: fields read from ctx.config for this ${kind}.\n` + @@ -249,7 +249,7 @@ function readmeTemplate({ name, slug, kind }) { `# Validate the plugin (static checks + dry-run activate):\n` + `hyp plugin doctor .\n` + '```\n\n' + - `Edit \`src/index.js\` — the \`activate(ctx)\` function registers the\n` + + `Edit \`src/index.js\` - the \`activate(ctx)\` function registers the\n` + `${kind} '${slug}'. See \`docs/PLUGIN_AUTHORING.md\` in the HypAware repo\n` + `for the full authoring guide.\n` ) diff --git a/src/core/plugin_install/confirm.js b/src/core/plugin_install/confirm.js index 2fbbf63f..d71927d4 100644 --- a/src/core/plugin_install/confirm.js +++ b/src/core/plugin_install/confirm.js @@ -9,7 +9,7 @@ import readline from 'node:readline/promises' /** * Optional warning lines printed to stderr ahead of the prompt. Soft - * warnings only — they never block install on their own. The strings + * warnings only: they never block install on their own. The strings * are short so they grep cleanly in smoke logs. * * @param {StagedArtifact} staged @@ -29,7 +29,7 @@ export function buildWarnings(staged) { if (refIsBranch) { const refText = staged.source.ref ? `'${staged.source.ref}'` : '' warnings.push( - `WARNING: ref ${refText} is an unpinned branch — pin a tag or commit SHA for reproducible installs` + `WARNING: ref ${refText} is an unpinned branch - pin a tag or commit SHA for reproducible installs` ) } return warnings @@ -50,7 +50,7 @@ function isBroadPermission(p) { * A ref counts as an unpinned branch when it is missing entirely (the * fetch picks the remote default branch) or when it does not look like * a tag (`v1.2.3`, semver-like) or a commit SHA. We do not call out to - * `git ls-remote` here — the smoke fixture is local and the warning + * `git ls-remote` here: the smoke fixture is local and the warning * is intentionally heuristic. * * @param {PluginSourceSpec} source diff --git a/src/core/plugin_install/fetch.js b/src/core/plugin_install/fetch.js index c7c5e064..611d3db0 100644 --- a/src/core/plugin_install/fetch.js +++ b/src/core/plugin_install/fetch.js @@ -41,7 +41,7 @@ const SKIPPED_DIR_NAMES = new Set(['node_modules', '.git', '.DS_Store']) * before the artifact rename swap. Ignored for local-dir sources, * which keep their non-interactive behavior by design. * @returns {Promise} - * @ref LLP 0007#install-path-git-artifact-never-npm-install [implements] — copy a prebuilt artifact in; kernel never runs npm install + * @ref LLP 0007#install-path-git-artifact-never-npm-install [implements]: copy a prebuilt artifact in; kernel never runs npm install */ export async function fetchPlugin({ source, stateDir, beforeCommit }) { switch (source.kind) { diff --git a/src/core/plugin_install/git_fetch.js b/src/core/plugin_install/git_fetch.js index cb43338b..2e9bab90 100644 --- a/src/core/plugin_install/git_fetch.js +++ b/src/core/plugin_install/git_fetch.js @@ -44,11 +44,11 @@ const SKIPPED_DIR_NAMES = new Set([ * * Pipeline (each external boundary wrapped in its own span): * - * 1. `plugin.git.clone` — `git clone --filter=blob:none --no-checkout` - * 2. `plugin.git.checkout` — `git checkout ` (default branch HEAD when omitted) - * 3. `plugin.git.resolve_ref` — `git rev-parse HEAD` - * 4. `plugin.artifact.validate` — manifest, entrypoint, symlink-free - * 5. `plugin.artifact.copy` — copy into install dir (atomic swap) + * 1. `plugin.git.clone` - `git clone --filter=blob:none --no-checkout` + * 2. `plugin.git.checkout` - `git checkout ` (default branch HEAD when omitted) + * 3. `plugin.git.resolve_ref` - `git rev-parse HEAD` + * 4. `plugin.artifact.validate` - manifest, entrypoint, symlink-free + * 5. `plugin.artifact.copy` - copy into install dir (atomic swap) * * On any failure after the clone, temp directories are cleaned up and * any prior install is left untouched. diff --git a/src/core/plugin_install/git_source.js b/src/core/plugin_install/git_source.js index e974710b..ac571806 100644 --- a/src/core/plugin_install/git_source.js +++ b/src/core/plugin_install/git_source.js @@ -21,12 +21,12 @@ const PASSTHROUGH_GIT_RE = /^(?:git\+|git:|ssh:|https?:\/\/|gitlab:|bitbucket:|f * * Other git-prefixed URLs (gitlab:, bitbucket:, generic git+/git:/ssh:) * are accepted as-is so plugin authors using non-GitHub hosts still get - * a working install path — owner/repo/host telemetry just stays empty. + * a working install path, so owner/repo/host telemetry just stays empty. * * Returns the normalized HTTPS clone URL, parsed `ref` (URL fragment), * and GitHub owner/repo/host segments when discoverable. Throws when * the token starts with a recognized git prefix but otherwise fails to - * parse — callers funnel the throw into the `resolver_error` span attr. + * parse: callers funnel the throw into the `resolver_error` span attr. * * @param {string} raw * @returns {GitSourceParts} @@ -85,7 +85,7 @@ export function parseGitSource(raw) { } if (PASSTHROUGH_GIT_RE.test(trimmed)) { - // Non-GitHub clone URL — split a `#` fragment if present, then + // Non-GitHub clone URL: split a `#` fragment if present, then // strip any `user:pass@` userinfo from the resulting URL so the // persisted spec never carries credentials. const hashIdx = trimmed.lastIndexOf('#') diff --git a/src/core/plugin_install/install.js b/src/core/plugin_install/install.js index 8b7d496f..311b060b 100644 --- a/src/core/plugin_install/install.js +++ b/src/core/plugin_install/install.js @@ -47,7 +47,7 @@ import { checkForPluginUpdate } from './update_check.js' * - `hyp_plugin` once the manifest is known * - `hyp_source_kind` from the resolved source * - `status` (`ok`/`failed`) - * - `error_kind` (set on failure) — `resolver_error`, + * - `error_kind` (set on failure): `resolver_error`, * `local_dir_missing`, `local_dir_invalid`, `manifest_invalid`, * `manifest_name_mismatch`, `fetch_unsupported`, or * `lock_write_error`. @@ -210,7 +210,7 @@ export async function installPlugin({ rawSource, stateDir, cwd, now, opts, confi } // The fetch already resolved the upstream ref, so the post- - // install probe would just confirm what we already know — and + // install probe would just confirm what we already know, and // would block on a slow or hung remote inside the same span. // Synthesize the "no update available" state directly. const updateState = await checkForPluginUpdate({ @@ -260,7 +260,7 @@ export async function installPlugin({ rawSource, stateDir, cwd, now, opts, confi * - `status` (`ok`/`failed`), `error_kind` (on failure) * * If the user rejects the confirmation prompt the prior install is - * left intact — the artifact swap inside `fetchGitSource` only fires + * left intact: the artifact swap inside `fetchGitSource` only fires * once the `beforeCommit` callback returns `proceed=true`. * * @param {object} args diff --git a/src/core/plugin_install/lock.js b/src/core/plugin_install/lock.js index d0588ea9..ba62d7b8 100644 --- a/src/core/plugin_install/lock.js +++ b/src/core/plugin_install/lock.js @@ -18,7 +18,7 @@ export function emptyLock() { /** * Load `plugin-lock.json` from the state directory. A missing file is - * not an error — callers get an empty lock back. A malformed file is + * not an error: callers get an empty lock back. A malformed file is * an error: we refuse to silently drop entries the user thinks are * installed. * diff --git a/src/core/plugin_install/resolver.js b/src/core/plugin_install/resolver.js index 8cc98e39..15641e09 100644 --- a/src/core/plugin_install/resolver.js +++ b/src/core/plugin_install/resolver.js @@ -25,7 +25,7 @@ const THIRD_PARTY_UNSCOPED_PREFIX = 'hypaware-plugin-' * development against `hypaware-core/plugins-workspace/`. * 2. A token matching a known git prefix (`github:`, `git+https://`, * `https://...git`, `ssh://`, etc.) is a `git` source. The - * resolver does not contact the network — it records the URL on + * resolver does not contact the network: it records the URL on * the spec and lets `fetch` perform the clone. * 3. A scoped name beginning with `@hypaware/` is a first-party * plugin. It maps to `github:hyperparam/hypaware-` per @@ -37,13 +37,13 @@ const THIRD_PARTY_UNSCOPED_PREFIX = 'hypaware-plugin-' * plugin, same registry lookup path. * * Everything else is rejected with a thrown `Error`. The resolver is - * a pure function — no I/O — so the dispatcher can call it inside the + * a pure function (no I/O), so the dispatcher can call it inside the * `plugin.install` span without surprises. * * @param {string} rawSource * @param {{ cwd?: string, ref?: string, subdir?: string }} [opts] * @returns {PluginSourceSpec} - * @ref LLP 0007#single-install-surface [implements] — one fixed resolution precedence across first-party and third-party name shapes + * @ref LLP 0007#single-install-surface [implements]: one fixed resolution precedence across first-party and third-party name shapes */ export function resolveSource(rawSource, opts = {}) { if (typeof rawSource !== 'string' || rawSource.length === 0) { @@ -129,8 +129,8 @@ export function resolveSource(rawSource, opts = {}) { /** * Heuristic for "this looks like a local filesystem path." The CLI - * design treats `./foo`, `/abs/foo`, `../foo`, and `foo/bar` as local - * — anything that isn't a bare identifier and lacks a git protocol + * design treats `./foo`, `/abs/foo`, `../foo`, and `foo/bar` as local: + * anything that isn't a bare identifier and lacks a git protocol * prefix. The resolver does not stat the path; absence is handled by * `fetch.js` so the developer gets a `local_dir_missing` error_kind * instead of a confusing "unknown command" message. diff --git a/src/core/plugin_install/update_check.js b/src/core/plugin_install/update_check.js index 29941885..576e4b9d 100644 --- a/src/core/plugin_install/update_check.js +++ b/src/core/plugin_install/update_check.js @@ -74,7 +74,7 @@ export async function checkForPluginUpdate({ entry, now, freshlyResolved }) { async (s) => { let state if (freshlyResolved) { - // The fetch path resolved the upstream ref a tick ago — re- + // The fetch path resolved the upstream ref a tick ago; re- // querying just risks blocking the install span on a slow // remote. Synthesize the state and mark the span so smokes can // see we deliberately skipped the network probe. @@ -144,7 +144,7 @@ function isRateLimited(entry, now) { * Probe the upstream for a newer version. The Phase 7 implementation * intentionally only handles `local-dir` (no upstream). The other * kinds get a benign "no upstream wired yet" response with - * `available=false` — when Phase 8 brings real fetch online, this + * `available=false` when Phase 8 brings real fetch online, this * function gets the actual git/npm probe without changing the span * contract. * @@ -188,7 +188,7 @@ async function runGitProbe(entry, checkedAt) { // `--` separates options from the URL/ref positionals so a hostile // gitUrl or ref like `--upload-pack=` cannot be parsed as a // git option (CVE-2018-17456 family). The resolver also rejects - // leading-dash inputs upstream — this is the spawn-boundary defense. + // leading-dash inputs upstream: this is the spawn-boundary defense. const probe = await execGit(['ls-remote', '--quiet', '--', gitUrl, ref]) if (probe.code !== 0) { return { @@ -222,7 +222,7 @@ async function runGitProbe(entry, checkedAt) { * matches, formatted `\t`. For annotated tags the * output contains both `refs/tags/` (the tag object) and * `refs/tags/^{}` (the peeled commit). Comparing the tag-object - * SHA against `entry.resolved_ref` — which is always a commit SHA — + * SHA against `entry.resolved_ref`, which is always a commit SHA, * produces a spurious "update available" signal. The peeled line is * the authoritative match. * @@ -248,7 +248,7 @@ export function pickLsRemoteSha(stdout, requestedRef) { } if (rows.length === 0) return undefined - // Prefer a peeled annotated tag (refs/tags/^{}) — its SHA is the + // Prefer a peeled annotated tag (refs/tags/^{}): its SHA is the // commit the tag points at, which is what we compared `resolved_ref` // against when we recorded it. const peeled = rows.find((r) => r.refname.endsWith('^{}')) diff --git a/src/core/query/format.js b/src/core/query/format.js index 46304df6..1b520026 100644 --- a/src/core/query/format.js +++ b/src/core/query/format.js @@ -8,16 +8,16 @@ * Bound a result set's context footprint before it is rendered to a * model or terminal. Two independent axes: * - * 1. per-cell truncation — recursively clip every string value to + * 1. per-cell truncation: recursively clip every string value to * `maxCell` code points, appending a greppable `…(+N)` marker so * the elision is visible and its size known. Only strings shrink; * numbers/booleans/null pass through unchanged, so `--format json` * output stays valid and same-typed. - * 2. row budget — drop trailing rows once the cumulative serialized + * 2. row budget: drop trailing rows once the cumulative serialized * *row-data* size (compact JSON per row, i.e. the jsonl payload) * exceeds `maxBytes`, so a wide or long result cannot flood the - * caller's context. This measures the underlying data — the - * dominant, format-independent context cost — not the final + * caller's context. This measures the underlying data: the + * dominant, format-independent context cost, not the final * rendered output, which adds modest per-format overhead (JSON * array syntax and indentation, table padding, markdown escaping). * At least one row is always kept. @@ -43,7 +43,7 @@ export function applyContextControls(result, controls) { } // Budget: truncate lazily, one source row at a time, and stop as soon as - // the budget is exceeded — so a broad query never pays to truncate rows + // the budget is exceeded, so a broad query never pays to truncate rows // that would be dropped anyway. Always emit at least one row. /** @type {Record[]} */ const kept = [] @@ -255,7 +255,7 @@ function mdEscape(value) { /** * Decide what a SQL query emits for a completed result, without doing any - * IO — so the spill-vs-inline behavior is unit-testable. The caller (the + * IO. So the spill-vs-inline behavior is unit-testable. The caller (the * `query sql` verb's `render`, or `hyp mcp`) performs the actual writes. * * - Spill mode (`output` set): the full, un-capped result is rendered for @@ -271,7 +271,7 @@ function mdEscape(value) { export function buildQuerySqlOutput(full, opts) { if (opts.output) { // Render the file content once and reuse it for both the file and the - // receipt's byte count — large dumps are exactly the `--output` case, + // receipt's byte count: large dumps are exactly the `--output` case, // so a second full serialization is wasted work and peak memory. const content = renderResult(full, opts.format) return { diff --git a/src/core/query/parquet-pushdown.js b/src/core/query/parquet-pushdown.js index ecb276d1..89a364cd 100644 --- a/src/core/query/parquet-pushdown.js +++ b/src/core/query/parquet-pushdown.js @@ -4,7 +4,7 @@ * Convert a squirreling `WHERE` clause AST into a hyparquet * `ParquetQueryFilter` (a MongoDB-style predicate) so the scan can push * the predicate down to the parquet reader. Returns `undefined` whenever - * the expression cannot be fully and faithfully converted — the caller + * the expression cannot be fully and faithfully converted. The caller * must then leave `appliedWhere` false and let the SQL engine filter the * rows itself. * @@ -70,7 +70,7 @@ function convertBinary(node, negate) { } if (op === 'OR') { // `$nor` already expresses NOT(a OR b), so the children are converted - // un-negated and the wrapper carries the negation — propagating + // un-negated and the wrapper carries the negation, propagating // `negate` into them as well would double-negate. const leftFilter = convertExpr(left, false) const rightFilter = convertExpr(right, false) diff --git a/src/core/query/parquet-source.js b/src/core/query/parquet-source.js index cc4a450a..b620d54e 100644 --- a/src/core/query/parquet-source.js +++ b/src/core/query/parquet-source.js @@ -31,7 +31,7 @@ import { whereToParquetFilter } from './parquet-pushdown.js' * filter was active, which can under-return rows when the filter is * selective. Here, when a `WHERE` is present we read every row group * (with the filter applied) and let the engine apply `LIMIT`/`OFFSET` - * over the filtered stream — correct, at the cost of not early-stopping. + * over the filtered stream: correct, at the cost of not early-stopping. * * @param {AsyncBuffer} file * @param {FileMetaData} metadata @@ -76,7 +76,7 @@ export function parquetDataSource(file, metadata) { safeOffset = Math.min(rowCount, hints.offset - groupStart) } safeLimit = Math.min(rowCount - safeOffset, remainingLimit) - // Past the requested window — nothing further to read. + // Past the requested window: nothing further to read. if (safeLimit <= 0 && safeOffset < rowCount) break } // Whole row group skipped by OFFSET. diff --git a/src/core/query/schema.js b/src/core/query/schema.js index 11fe85de..4334806d 100644 --- a/src/core/query/schema.js +++ b/src/core/query/schema.js @@ -6,8 +6,8 @@ /** * Resolve the schema for a dataset by name. The kernel does not hard - * code dataset names — the registry holds whatever the active plugin - * set contributed — so `query schema ` simply asks the registry. + * code dataset names; the registry holds whatever the active plugin + * set contributed; so `query schema ` simply asks the registry. * * Returns `undefined` if the dataset is not registered; callers * surface that as a user-facing error. diff --git a/src/core/query/sql.js b/src/core/query/sql.js index c5d92277..0e79afae 100644 --- a/src/core/query/sql.js +++ b/src/core/query/sql.js @@ -16,7 +16,7 @@ import { QUERY_FLUSH_DEBOUNCE_MS } from '../cache/spool.js' * Run a read-only SELECT against the kernel's dataset registry. The * caller (the `hyp query sql` command or a future server endpoint) * supplies the registry and storage; this function never reads from - * disk directly — every byte of cache IO goes through the storage + * disk directly; every byte of cache IO goes through the storage * service so spans and metrics are attributed correctly. * * Wraps the entire run in a `query.execute_sql` span and emits one @@ -25,7 +25,7 @@ import { QUERY_FLUSH_DEBOUNCE_MS } from '../cache/spool.js' * * @param {ExecuteSqlOptions} args * @returns {Promise} - * @ref LLP 0015#query-is-intrinsic [implements] — core-owned read-only SQL over the registry; IO only via the storage service + * @ref LLP 0015#query-is-intrinsic [implements]: core-owned read-only SQL over the registry; IO only via the storage service */ export async function executeQuerySql(args) { const { query, registry, storage } = args diff --git a/src/core/query/union-source.js b/src/core/query/union-source.js index cdc66fb1..ce80f207 100644 --- a/src/core/query/union-source.js +++ b/src/core/query/union-source.js @@ -10,7 +10,7 @@ * * The union reports `appliedWhere: false` and `appliedLimitOffset: false`, * so the SQL engine re-applies both over the merged stream. `limit`/`offset` - * are stripped from the sub-scans — they are not distributive across a + * are stripped from the sub-scans. They are not distributive across a * concatenation; a sub-source that honors limit/offset pushdown (e.g. an * Iceberg partition) would otherwise drop its first `offset` rows per * partition and the engine would skip the offset again on the joined stream, @@ -24,11 +24,11 @@ * reading the column as null. When a partition can't satisfy the predicate we * drop `where` for it and let the engine filter the concatenated stream (it * already owns the filter via `appliedWhere: false`). `columns` is always - * forwarded — projecting an absent column reads as null, never throws. + * forwarded: projecting an absent column reads as null, never throws. * * @param {AsyncDataSource[]} sources * @returns {AsyncDataSource} - * @ref LLP 0015#multi-partition-union [constrained-by] — the union must not forward limit/offset or offsets apply twice, nor push a filter a partition can't satisfy + * @ref LLP 0015#multi-partition-union [constrained-by]: the union must not forward limit/offset or offsets apply twice, nor push a filter a partition can't satisfy */ export function unionSources(sources) { /** @type {Set} */ @@ -90,7 +90,7 @@ function canPushWhere(source, predicateColumns) { * Collect the column names a `where` predicate references. Returns null when * the predicate contains a construct whose column set can't be safely * enumerated locally (a qualified identifier, subquery, or correlated - * reference) — the caller then declines to push the predicate, which is always + * reference). The caller then declines to push the predicate, which is always * safe because the engine re-applies it. * * @param {ExprNode | undefined} where @@ -148,7 +148,7 @@ function whereColumns(where) { walk(node.elseResult) return default: - // subquery / in / exists / not exists / anything new — bail. + // subquery / in / exists / not exists / anything new: bail. enumerable = false } } diff --git a/src/core/query/verb.js b/src/core/query/verb.js index c7891938..ce22dee9 100644 --- a/src/core/query/verb.js +++ b/src/core/query/verb.js @@ -11,12 +11,12 @@ import { executeQuerySql } from './sql.js' /** * The intrinsic `query_sql` verb. SQL/dataset query is **intrinsic** (LLP * 0003), so core contributes this verb for free on every host with a - * registered dataset — it projects the `query sql` CLI command and the + * registered dataset; it projects the `query sql` CLI command and the * `query_sql` MCP tool from one declaration. Read-class: reachable by a * query-scoped credential. * * @type {VerbRegistration} - * @ref LLP 0034#maps-onto-the-intrinsicplugin-boundary-llp-0003 [implements] — SQL surface is intrinsic → core's own verb, a tool on every host + * @ref LLP 0034#maps-onto-the-intrinsicplugin-boundary-llp-0003 [implements]: SQL surface is intrinsic → core's own verb, a tool on every host */ export const querySqlVerb = { name: 'query sql', diff --git a/src/core/registry/backfills.js b/src/core/registry/backfills.js index 830a6ed7..d26208f8 100644 --- a/src/core/registry/backfills.js +++ b/src/core/registry/backfills.js @@ -11,7 +11,7 @@ import { Attr, getLogger } from '../observability/index.js' * `register(contribution)` during activation; `hyp backfill list`, * `hyp backfill plan`, and `hyp backfill ` enumerate * providers through `list()` / `get()`. The registry is intentionally - * narrow — the runner owns lifecycle and telemetry; the contribution's + * narrow. The runner owns lifecycle and telemetry; the contribution's * `plan()` / `run()` own native discovery. * * @returns {BackfillRegistry} diff --git a/src/core/registry/capabilities.js b/src/core/registry/capabilities.js index 3d099da3..019483a3 100644 --- a/src/core/registry/capabilities.js +++ b/src/core/registry/capabilities.js @@ -13,7 +13,7 @@ import { matchesSemverRange } from '../semver.js' * contract: `cap.provide`, `cap.require_satisfied`, and * `cap.require_missing` logs, plus the `hyp_capabilities_provided` * UpDownCounter on each provide. Duplicate-provider arbitration is - * intentionally not handled here — dep_graph inspects `list()` after + * intentionally not handled here; dep_graph inspects `list()` after * provides and emits the `cap_version_clash` rejection. * * @returns {CapabilityRegistryHandle} @@ -48,7 +48,7 @@ export function createCapabilityRegistry() { * @param {string} name * @param {string} [range] * @returns {T} - * @ref LLP 0006#resolution-rules [implements] — the single sanctioned cross-plugin channel; missing capability fails early + * @ref LLP 0006#resolution-rules [implements]: the single sanctioned cross-plugin channel; missing capability fails early */ function requireCapability(requester, name, range) { const matches = findMatches(registrations, name, range) @@ -128,7 +128,7 @@ export function createCapabilityRegistry() { * @param {InternalRegistration[]} registrations * @param {string} name * @param {string} [range] - * @ref LLP 0006#two-kinds-of-dependency [implements] — version range travels with the require, never baked into the capability name + * @ref LLP 0006#two-kinds-of-dependency [implements]: version range travels with the require, never baked into the capability name */ function findMatches(registrations, name, range) { return registrations.filter((r) => r.name === name && matchesSemverRange(r.version, range)) diff --git a/src/core/registry/commands.js b/src/core/registry/commands.js index d3b1a2eb..a24f9e60 100644 --- a/src/core/registry/commands.js +++ b/src/core/registry/commands.js @@ -25,7 +25,7 @@ * has: (name: string) => boolean, * size: () => number, * }} - * @ref LLP 0009#core-owns-dispatch [implements] — core routes argv to the owning command; plugins only register + * @ref LLP 0009#core-owns-dispatch [implements]: core routes argv to the owning command; plugins only register */ export function createCommandRegistry() { /** @type {Map} */ diff --git a/src/core/registry/datasets.js b/src/core/registry/datasets.js index f6c720ae..4bbee58c 100644 --- a/src/core/registry/datasets.js +++ b/src/core/registry/datasets.js @@ -39,7 +39,7 @@ function validateCachePartitioning(decl, schema, datasetName) { * activation context and as `kernel.query` for the dispatcher. * * @returns {QueryRegistry} - * @ref LLP 0015#query-is-intrinsic [implements] — core hard-codes no dataset names; plugins register every one + * @ref LLP 0015#query-is-intrinsic [implements]: core hard-codes no dataset names; plugins register every one */ export function createQueryRegistry() { /** @type {Map} */ diff --git a/src/core/registry/sinks.js b/src/core/registry/sinks.js index e6c1f52d..896a1ce5 100644 --- a/src/core/registry/sinks.js +++ b/src/core/registry/sinks.js @@ -20,7 +20,7 @@ import { Attr, getKernelInstruments, getLogger, withSpan } from '../observabilit /** * Build the kernel-side SinkRegistry. The contract surface * (`register`/`get`/`list`) matches `collectivus-plugin-kernel-types.d.ts - * §Sinks` and is what plugins see through `ctx.sinks` — plugins call + * §Sinks` and is what plugins see through `ctx.sinks`: plugins call * `register(contribution)` to declare a sink type (matching their * manifest `contributes.sinks[]` entry). Instance creation (per * `HypAwareV2Config.sinks.`) is driven by the kernel through @@ -30,7 +30,7 @@ import { Attr, getKernelInstruments, getLogger, withSpan } from '../observabilit * `hyp_sinks_registered` counter. * * @returns {ExtendedSinkRegistry} - * @ref LLP 0014#sinks-are-export-targets-not-the-write-path — instances driven from config; sources never reach here + * @ref LLP 0014#sinks-are-export-targets-not-the-write-path: instances driven from config; sources never reach here */ export function createSinkRegistry() { /** @type {Map} */ @@ -115,7 +115,7 @@ export function createSinkRegistry() { * * @param {InstantiateArgs} args * @returns {Promise} - * @ref LLP 0014#bytes-flow-down-semantics-flow-up [implements] — blob / table-format / request shapes keep writer + destination decoupled + * @ref LLP 0014#bytes-flow-down-semantics-flow-up [implements]: blob / table-format / request shapes keep writer + destination decoupled */ async function instantiate(args) { const { instanceName, config } = args @@ -348,7 +348,7 @@ function contributionKey(plugin, name) { * @param {TableFormatProvider} provider * @param {SinkEncoder} encoder * @returns {SinkSupportTag[]} - * @ref LLP 0014#queryable-sinks [implements] — table-format queryable only when provider and encoder both claim it + * @ref LLP 0014#queryable-sinks [implements]: table-format queryable only when provider and encoder both claim it */ function resolveTableFormatSupports(provider, encoder) { /** @type {Set} */ @@ -372,7 +372,7 @@ function resolveTableFormatSupports(provider, encoder) { * @param {SinkContribution} contribution * @param {SinkEncoder | undefined} encoder * @returns {SinkSupportTag[]} - * @ref LLP 0014#queryable-sinks [implements] — queryable is a property of the writer+destination pair, not either alone + * @ref LLP 0014#queryable-sinks [implements]: queryable is a property of the writer+destination pair, not either alone */ function resolveSupports(contribution, encoder) { /** @type {Set} */ diff --git a/src/core/registry/sources.js b/src/core/registry/sources.js index 16c228a0..f8227231 100644 --- a/src/core/registry/sources.js +++ b/src/core/registry/sources.js @@ -18,7 +18,7 @@ import { Attr, getKernelInstruments, getLogger, withSpan } from '../observabilit * * @returns {ExtendedSourceRegistry} */ -// @ref LLP 0012 — Source subsystem: registration + kernel-driven lifecycle +// @ref LLP 0012: Source subsystem: registration + kernel-driven lifecycle export function createSourceRegistry() { /** @type {Map} */ const contributions = new Map() @@ -27,7 +27,7 @@ export function createSourceRegistry() { const log = getLogger('sources') const instruments = getKernelInstruments() - // @ref LLP 0012#contribution-surface [implements] — name/plugin/start required, unique source names + // @ref LLP 0012#contribution-surface [implements]: name/plugin/start required, unique source names /** @param {SourceContribution} contribution */ function register(contribution) { if (!contribution || typeof contribution !== 'object') { @@ -66,7 +66,7 @@ export function createSourceRegistry() { * @param {string} name * @param {PluginActivationContext} ctx * @returns {Promise} - * @ref LLP 0012#observable-lifecycle [implements] — start wraps a source.start span and ticks hyp_sources_started + * @ref LLP 0012#observable-lifecycle [implements]: start wraps a source.start span and ticks hyp_sources_started */ async function start(name, ctx) { const contribution = contributions.get(name) @@ -128,7 +128,7 @@ export function createSourceRegistry() { /** * @param {string} name * @param {PluginActivationContext} ctx - * @ref LLP 0012#reload-context [constrained-by] — reload shares start's ActivationContext; unsupported reload still emits a skipped span + * @ref LLP 0012#reload-context [constrained-by]: reload shares start's ActivationContext; unsupported reload still emits a skipped span */ async function reload(name, ctx) { const handle = started.get(name) @@ -136,7 +136,7 @@ export function createSourceRegistry() { throw new Error(`SourceRegistry.reload: source '${name}' is not started`) } if (typeof handle.reload !== 'function') { - // Sources opt-out by omitting reload — surface a span anyway so the + // Sources opt-out by omitting reload, surface a span anyway so the // operator can grep for "reload requested but not supported." const contribution = contributions.get(name) await withSpan( diff --git a/src/core/registry/verbs.js b/src/core/registry/verbs.js index 1bed8bee..479ebef6 100644 --- a/src/core/registry/verbs.js +++ b/src/core/registry/verbs.js @@ -17,7 +17,7 @@ import { verbToCommand } from '../cli/verb_command.js' * * @param {{ commandRegistry?: CommandRegistry }} [opts] * @returns {VerbRegistry} - * @ref LLP 0034#tool-exposure-emergent [implements] — no central tool gate; the surface is exactly the verbs active plugins register + * @ref LLP 0034#tool-exposure-emergent [implements]: no central tool gate; the surface is exactly the verbs active plugins register */ export function createVerbRegistry(opts = {}) { const commandRegistry = opts.commandRegistry diff --git a/src/core/remote/credentials.js b/src/core/remote/credentials.js index 8bb715df..f529f4c0 100644 --- a/src/core/remote/credentials.js +++ b/src/core/remote/credentials.js @@ -10,7 +10,7 @@ import process from 'node:process' * lives in a single `0600` file, written atomically, mirroring `central`'s * `identity.json` single-file precedent. * - * Stakes are low by scoping — what lands here is the **query-scoped** token + * Stakes are low by scoping: what lands here is the **query-scoped** token * (read/compute tools only; cannot author configs or mint tokens), not the * all-powerful operator token (LLP 0033 §credential-stakes). AI clients that * install the endpoint directly hold their own token in their own MCP @@ -125,7 +125,7 @@ export async function resolveToken({ target, env, stateDir }) { } return { ok: false, - error: `no token for '${target}' — run 'hyp remote login ${target}' (or set ${envName})`, + error: `no token for '${target}' - run 'hyp remote login ${target}' (or set ${envName})`, } } diff --git a/src/core/runtime/activation.js b/src/core/runtime/activation.js index 531ac324..3fc987d1 100644 --- a/src/core/runtime/activation.js +++ b/src/core/runtime/activation.js @@ -47,7 +47,7 @@ import { isSafeContributionName } from './contribution_names.js' * configControl?: ConfigControlFacade, * }} [opts] * @returns {KernelRuntime} - * @ref LLP 0003#intrinsic-not-plugin-provided [implements] — query + storage are wired in as intrinsic services, not plugin contributions + * @ref LLP 0003#intrinsic-not-plugin-provided [implements]: query + storage are wired in as intrinsic services, not plugin contributions */ export function createKernelRuntime(opts = {}) { const cacheRoot = opts.cacheRoot ?? opts.storage?.cacheRoot ?? defaultCacheRoot() @@ -106,7 +106,7 @@ function defaultCacheRoot() { * @param {JsonObject} [args.config] * @param {NodeJS.ProcessEnv} [args.env] * @returns {PluginActivationContext} - * @ref LLP 0004#the-activation-context [implements] — per-plugin ctx: config slice, registry facades, scoped logger, requireCapability + * @ref LLP 0004#the-activation-context [implements]: per-plugin ctx: config slice, registry facades, scoped logger, requireCapability */ export function createActivationContext({ runtime, plugin, paths, config, env }) { const pluginName = plugin.name @@ -135,7 +135,7 @@ export function createActivationContext({ runtime, plugin, paths, config, env }) initPresets: runtime.initPresets, backfills: runtime.backfills, backfillMaterializers: runtime.backfillMaterializers, - // @ref LLP 0025#apply-engine-is-kernel-surface [implements] — plugins reach the apply engine only through this narrow facade; absent outside the daemon + // @ref LLP 0025#apply-engine-is-kernel-surface [implements]: plugins reach the apply engine only through this narrow facade; absent outside the daemon ...(runtime.configControl ? { configControl: runtime.configControl } : {}), /** * @template T @@ -218,7 +218,7 @@ function createPermissionContext(pluginName, granted) { * @param {PluginName} pluginName * @param {ReturnType} registry * @returns {CapabilityRegistry} - * @ref LLP 0006#resolution-rules [constrained-by] — facade pins the activating plugin's identity; can't impersonate a provider + * @ref LLP 0006#resolution-rules [constrained-by]: facade pins the activating plugin's identity; can't impersonate a provider */ function createCapabilitiesFacade(pluginName, registry) { return { @@ -257,7 +257,7 @@ function createPhase2SkillRegistry() { if (!skill || typeof skill.name !== 'string' || skill.name.length === 0) { throw new TypeError('skills.register: name is required') } - // @ref LLP 0003#principle [constrained-by] — name is interpolated into + // @ref LLP 0003#principle [constrained-by]: name is interpolated into // `/`; reject traversal before it reaches the filesystem. if (!isSafeContributionName(skill.name)) { throw new TypeError(`skills.register '${skill.name}': name must be a safe basename (no '/', '\\\\', '..', or absolute path)`) @@ -300,7 +300,7 @@ function createAgentRegistry() { if (!agent || typeof agent.name !== 'string' || agent.name.length === 0) { throw new TypeError('agents.register: name is required') } - // @ref LLP 0003#principle [constrained-by] — name is interpolated into + // @ref LLP 0003#principle [constrained-by]: name is interpolated into // `/.md`; reject traversal before it reaches the filesystem. if (!isSafeContributionName(agent.name)) { throw new TypeError(`agents.register '${agent.name}': name must be a safe basename (no '/', '\\\\', '..', or absolute path)`) @@ -333,7 +333,7 @@ function createAgentRegistry() { * * Promoted from a Phase 2 placeholder in Phase 9 (hy-imw). The * registry is intentionally non-validating beyond the basic shape - * checks — preset authors own their argv parsing and config writing + * checks. Preset authors own their argv parsing and config writing * in `run()`. * * @returns {InitPresetRegistry} diff --git a/src/core/runtime/boot.js b/src/core/runtime/boot.js index 96f5810d..ee433d47 100644 --- a/src/core/runtime/boot.js +++ b/src/core/runtime/boot.js @@ -51,7 +51,7 @@ import { discoverInstalledPlugins } from './installed.js' * caller did not select are logged as `plugin.skipped` with * `status=skipped` and `hyp_reason=not_configured`. * - * `bootKernel` never throws on a missing config file — it activates + * `bootKernel` never throws on a missing config file. It activates * nothing and returns `config=null` so help/init paths keep working. * * @param {BootKernelOptions} [opts] @@ -132,14 +132,14 @@ export async function bootKernel(opts = {}) { // Two-layer config resolution (LLP 0031): the effective config is // the merge of a server-owned **central** layer (authoritative, // locked) and the user-owned **local** layer (`hypaware-config.json`, - // additive-only). Both are read-only here — only the daemon's apply + // additive-only). Both are read-only here. Only the daemon's apply // engine ever writes the central layer. A host that never joined has // no central layer, so `effective = local` (this whole block is a // no-op for it) and behaviour is byte-for-byte what it was before. // The catalog is built from the very manifests this boot discovered // so the merge validates local additions against the same plugin set // it will activate. - // @ref LLP 0031#two-layers-merged-at-boot [implements] — effective = merge(central, local), computed at boot + // @ref LLP 0031#two-layers-merged-at-boot [implements]: effective = merge(central, local), computed at boot const catalog = buildPluginCatalog([...discovered.loaded, ...discovered.excluded], installed.loaded) const merged = await resolveLayeredConfigFromDisk({ stateRoot, @@ -153,9 +153,9 @@ export async function bootKernel(opts = {}) { // The central layer is sacrosanct: a local entry that collides with a // locked central key, or that invalidates the merge, is dropped - // (loudly) — never failing boot. A garbage local edit can never take + // (loudly): never failing boot. A garbage local edit can never take // down a centrally-managed gateway. - // @ref LLP 0031#central-layer-is-sacrosanct [implements] — collisions / invalid additions drop the local entry with a loud log; central always boots + // @ref LLP 0031#central-layer-is-sacrosanct [implements]: collisions / invalid additions drop the local entry with a loud log; central always boots if (centralConfig) { const cfgLog = getLogger('config') for (const drop of merged.drops) { @@ -183,7 +183,7 @@ export async function bootKernel(opts = {}) { // dispatch): V1 allowlist + excluded-from-default set + installed // plugins, with an installed plugin replacing a same-named excluded // bundled skeleton. Excluded plugins are in the pool so they activate - // when named in config or an init preset — the allowlist only governs + // when named in config or an init preset, the allowlist only governs // default activation, not discoverability. const { installedNames, selected, selectedManifests } = selectBootPlugins({ discovered, @@ -300,7 +300,7 @@ export async function bootKernel(opts = {}) { * user-owned **local** layer (`configPath`) and the server-owned * **central** layer (active slot / join seed under `stateRoot`), then * merge + prune via {@link resolveLayeredConfig}. Both layers are read - * read-only — only the daemon's apply engine ever writes the central + * read-only. Only the daemon's apply engine ever writes the central * layer. The single place `bootKernel` and the SIGHUP reload agree on * what "effective" means, so a reload can never silently drop the central * layer. @@ -327,13 +327,13 @@ export async function resolveLayeredConfigFromDisk({ stateRoot, configPath, know central: centralConfig, local: localConfig, // No `configRegistry` here on purpose. This merge-time validation runs - // during config *resolution* — before `activatePlugins`, which is when + // during config *resolution* (before `activatePlugins`), which is when // each plugin registers its `config_sections` validator. At this point in // boot the runtime's `configRegistry` exists but is *empty*, so threading // it would dispatch `runPerPluginSectionValidators` against zero // registered sections: a no-op that gives false confidence. Per-plugin // section validation is enforced where the registry is actually populated - // — the daemon's apply path (`buildConfigApplyDeps`), which also discovers + // in the daemon's apply path (`buildConfigApplyDeps`), which also discovers // validators for plugins a document introduces but that aren't active yet. // Boot's merge stays limited to the cross-plugin/structural checks. validate: (cfg) => collectConfigErrors(cfg, { @@ -355,7 +355,7 @@ export async function resolveLayeredConfigFromDisk({ stateRoot, configPath, know /** * Like {@link resolveLayeredConfigFromDisk}, but discovers the plugin - * catalog itself — for callers outside `bootKernel` that don't already + * catalog itself (for callers outside `bootKernel` that don't already * hold the discovered manifest set (the daemon's SIGHUP reload). The * catalog drives the validation pass, so it must reflect the same * bundled + installed plugin set the kernel runs. @@ -394,7 +394,7 @@ export function resolveConfigPath({ explicit, env, hypHome }) { /** * Detect installed plugins that shadow a bundled first-party plugin by - * name. Pure (manifests only — no I/O, telemetry, or throw). Shared + * name. Pure (manifests only: no I/O, telemetry, or throw). Shared * between `bootKernel`'s hard reject guard and `selectBootPlugins` so the * shadow rule has a single definition. * @@ -415,7 +415,7 @@ export function detectShadowedPlugins({ discovered, installed }) { /** * Compute the boot-equivalent plugin selection from the cheap discovery - * inputs boot already reads — bundled + installed plugin manifests and + * inputs boot already reads (bundled + installed plugin manifests) and * the effective config. Pure: no I/O, no activation, no telemetry, no * throw. * @@ -430,8 +430,8 @@ export function detectShadowedPlugins({ discovered, installed }) { * first-party plugin. Boot rejects on these; help advertises no plugin * commands rather than phantoms that will never dispatch. * - excluded-bundled-vs-installed: an installed plugin replaces a - * same-named excluded bundled skeleton in the pool, so its commands — - * not the skeleton's — are what dispatch sees. + * same-named excluded bundled skeleton in the pool, so its commands + * (not the skeleton's) are what dispatch sees. * * @param {{ * discovered: { loaded: LoadedManifest[], excluded: LoadedManifest[] }, @@ -474,7 +474,7 @@ export function selectBootPlugins({ discovered, installed, config, bootProfile = * - `all-bundled`: only the V1 default surface. Excluded plugins * (`@hypaware/central`, `@hypaware/gascity`) and installed third-party * plugins are intentionally dropped so the walkthrough picker - * doesn't surface them — naming an installed plugin in the config is + * doesn't surface them. Instead, naming an installed plugin in the config is * the only way to activate one. * * - `all-available`: the default bundled surface plus every installed @@ -486,17 +486,17 @@ export function selectBootPlugins({ discovered, installed, config, bootProfile = * the workspace can resolve. Excluded plugins MAY appear here when * a developer-built profile names them; this is the documented * "loadable for developers" escape hatch. Installed plugins may - * appear here too — the daemon path uses this profile and shares + * appear here too, as the daemon path uses this profile and shares * the merged pool. * * - `config` (default): activate the plugins listed in the user's * config (`enabled !== false`). Excluded and installed plugins are - * honoured when they appear in the config — typing the name is the + * honoured when they appear in the config: typing the name is the * explicit opt-in. The installed plugin is never preferred over a * bundled one (shadow collisions are rejected before this point). * * Plugins in the config that aren't bundled (or aren't installed) - * are skipped silently here — the cross-plugin validator surfaces + * are skipped silently here. The cross-plugin validator surfaces * `plugin_unknown` diagnostics for those, separate from boot. * * @param {{ diff --git a/src/core/runtime/bundled.js b/src/core/runtime/bundled.js index 57c39723..1787870a 100644 --- a/src/core/runtime/bundled.js +++ b/src/core/runtime/bundled.js @@ -16,7 +16,7 @@ import { loadManifests } from '../manifest.js' * activated by the default boot profiles (`all-bundled`, * `all-available`). Excluded plugins (`@hypaware/central`, * `@hypaware/gascity`) are still discoverable through the plugin - * catalog — their manifest contributions (datasets, client + * catalog: their manifest contributions (datasets, client * descriptors, capability metadata) are visible to config validation * and the walkthrough. They are activatable via explicit config or * init presets; the allowlist only governs default activation. @@ -43,16 +43,16 @@ export const V1_BUNDLED_PLUGIN_ALLOWLIST = new Set(/** @type {PluginName[]} */ ( * bucket) so the plugin catalog can derive datasets, client * descriptors, and capability metadata for config validation. * Activation requires explicit config (`{ name: '@hypaware/gascity' }`) - * or an init preset — the picker and default boot profiles skip them. + * or an init preset: the picker and default boot profiles skip them. * * The embedder/vector-search pair is excluded because enabling an * API-backed embedder is the explicit opt-in that lets captured text - * leave the machine — it must be a deliberate `plugins[]` decision, + * leave the machine: it must be a deliberate `plugins[]` decision, * never a default. `@hypaware/vector-search` follows it: its manifest * requires `hypaware.embedder`, which no default-activated plugin * provides. * - * The completion providers follow the same rule — enabling a model + * The completion providers follow the same rule: enabling a model * backend lets captured content (prompts built from the graph + source) * leave the machine, so it is an explicit `plugins[]` choice. And * `@hypaware/context-graph-enrich` requires the completion + vector-search @@ -60,7 +60,7 @@ export const V1_BUNDLED_PLUGIN_ALLOWLIST = new Set(/** @type {PluginName[]} */ ( * model calls, so it too activates only via explicit config. * * @type {ReadonlySet} - * @ref LLP 0024#embedding-is-a-separate-capability [constrained-by] — the embedder choice is an explicit plugins[] config decision, so neither plugin default-activates + * @ref LLP 0024#embedding-is-a-separate-capability [constrained-by]: the embedder choice is an explicit plugins[] config decision, so neither plugin default-activates */ export const V1_EXCLUDED_FROM_DEFAULT = new Set(/** @type {PluginName[]} */ ([ '@hypaware/central', @@ -90,14 +90,14 @@ export function defaultBundledWorkspaceDir() { /** * Walk `workspaceDir` and split discovered plugin manifests into: * - * - `loaded` — manifests whose `name` is in the V1 allowlist. - * - `excluded` — manifests whose `name` is in the V1 exclude set + * - `loaded` - manifests whose `name` is in the V1 allowlist. + * - `excluded` - manifests whose `name` is in the V1 exclude set * (`@hypaware/central`, `@hypaware/gascity`). These * are excluded from default activation but their * manifests feed the plugin catalog so datasets, * client descriptors, and capability metadata remain * visible to config validation and the walkthrough. - * - `unknownDirs` — directories that hold a parseable manifest under + * - `unknownDirs` - directories that hold a parseable manifest under * a name the kernel doesn't recognise as bundled. * * Missing workspace directories return an empty result rather than diff --git a/src/core/runtime/contribution_names.js b/src/core/runtime/contribution_names.js index 8ad995b8..1f342d0c 100644 --- a/src/core/runtime/contribution_names.js +++ b/src/core/runtime/contribution_names.js @@ -10,7 +10,7 @@ import path from 'node:path' * client directory. This module centralizes the two guards that keep * those writes contained. * - * @ref LLP 0003#principle — names cross the core/plugin trust boundary, + * @ref LLP 0003#principle: names cross the core/plugin trust boundary, * so core validates them rather than trusting plugins. */ diff --git a/src/core/runtime/installed.js b/src/core/runtime/installed.js index 7299a02f..6f3cbe32 100644 --- a/src/core/runtime/installed.js +++ b/src/core/runtime/installed.js @@ -14,8 +14,8 @@ import { loadManifest } from '../manifest.js' /** * Walk the kernel state's `plugin-lock.json` and load the manifest from * every lock entry's `install_dir`. Mirrors `discoverBundledPlugins` - * but skips the V1 allowlist — anything that landed in the lock is - * user-installed and was reviewed at install time. + * but skips the V1 allowlist (anything that landed in the lock is + * user-installed and was reviewed at install time). * * Missing lock file → empty result. Per-entry manifest failures are * surfaced via `failed[]` (mirroring `loadManifests`) and additionally @@ -50,7 +50,7 @@ export async function discoverInstalledPlugins({ stateDir }) { for (const { entry, result } of results) { if (result.ok) { if (result.manifest.name !== entry.name) { - // Lock entry name and manifest name disagree — boot cannot + // Lock entry name and manifest name disagree. Boot cannot // trust this entry. Surface it as failed so the caller can // decide; we log explicitly because the bare manifest.reject // log does not carry the lock-entry context. diff --git a/src/core/runtime/loader.js b/src/core/runtime/loader.js index 1ff0dd28..42a37a5b 100644 --- a/src/core/runtime/loader.js +++ b/src/core/runtime/loader.js @@ -42,7 +42,7 @@ import { createActivationContext, createKernelRuntime } from './activation.js' * @param {KernelRuntime} [args.runtime] Override the kernel runtime (tests). * @param {string} [args.tmpRoot] Override the OS temp root (tests). * @returns {Promise<{ runtime: KernelRuntime, results: ActivationResult[] }>} - * @ref LLP 0008#consequences [implements] — loads each plugin only through its single manifest entrypoint, never a deep import + * @ref LLP 0008#consequences [implements]: loads each plugin only through its single manifest entrypoint, never a deep import */ export async function activatePlugins({ plugins, stateRoot, runId, runtime, tmpRoot }) { if (!Array.isArray(plugins)) throw new Error('activatePlugins: plugins must be an array') diff --git a/src/core/runtime/paths.js b/src/core/runtime/paths.js index b00721c7..d3891d3b 100644 --- a/src/core/runtime/paths.js +++ b/src/core/runtime/paths.js @@ -15,9 +15,9 @@ import path from 'node:path' * * Per LLP 0004#state-directories: * - `rootDir` the plugin's installed directory (where the manifest lives). - * - `stateDir` `/plugins/` — durable per-plugin state. - * - `cacheDir` `/cache/plugins/` — derivable per-plugin cache. - * - `tempDir` `/-` — ephemeral per-boot scratch. + * - `stateDir` `/plugins/` (durable per-plugin state). + * - `cacheDir` `/cache/plugins/` (derivable per-plugin cache). + * - `tempDir` `/-` (ephemeral per-boot scratch). * * Plugin names may contain a scope (`@hypaware/dummy-a`). The kernel * preserves the literal name in state/cache layouts (matching the npm @@ -38,7 +38,7 @@ import path from 'node:path' * @param {string} args.runId Kernel boot identifier (DEV_RUN_ID or similar). * @param {string} [args.tmpRoot] Override the OS temp root (tests). * @returns {Promise} - * @ref LLP 0004#state-directories [implements] — kernel-owned scoped per-plugin dirs; plugins never reach into each other's + * @ref LLP 0004#state-directories [implements]: kernel-owned scoped per-plugin dirs; plugins never reach into each other's */ export async function createPluginPaths({ pluginName, rootDir, stateRoot, runId, tmpRoot }) { if (!pluginName) throw new Error('createPluginPaths: pluginName is required') diff --git a/src/core/sinks/driver.js b/src/core/sinks/driver.js index b8be9378..bae2d24a 100644 --- a/src/core/sinks/driver.js +++ b/src/core/sinks/driver.js @@ -315,12 +315,12 @@ const FIELD_RANGES = /** @type {const} */ ([ ]) /** - * `cronMatches(expr, now)` — true if `now` (UTC) satisfies the 5-field + * `cronMatches(expr, now)` - true if `now` (UTC) satisfies the 5-field * cron expression. Supports `*`, comma lists, ranges (`1-5`), and * step values like every-N (`STAR/N`) or `0-10/2`. Day-of-month and * day-of-week obey the standard OR-when-both-restricted rule. * - * The kernel only needs evaluation, not iteration — the driver's host + * The kernel only needs evaluation, not iteration: the driver's host * is responsible for ticking on a reasonable cadence (typically once * per minute). The smoke harness calls `tick({ now })` directly with a * `now` that aligns with `"* * * * *"`, so this evaluator is exercised diff --git a/src/core/sinks/encoder.js b/src/core/sinks/encoder.js index f4489e00..a3d61544 100644 --- a/src/core/sinks/encoder.js +++ b/src/core/sinks/encoder.js @@ -10,7 +10,7 @@ import { Attr, withSpan } from '../observability/index.js' * Derive the cluster columns for a dataset from its Iceberg partition fields * (e.g. `conversation_id`/`cwd`/`date`). Blob destinations pass these into the * encode context so columnar encoders can keep each row group low-cardinality - * — which keeps wide, heavily-repeated columns (`tools`, `system_text`) + * which keeps wide, heavily-repeated columns (`tools`, `system_text`) * dictionary-encoded instead of falling back to PLAIN. Returns undefined for * datasets without a partition declaration (encoders then use default row * grouping). Shared by the `local-fs` and `s3` blob destinations. @@ -34,7 +34,7 @@ export function clusterColumnsForDataset(query, dataset) { /** * Wrap a single `encoder.encodePartition` call in a * `sink.encode_partition` span. Blob-sink `Sink.exportBatch` - * implementations use this so the kernel — not each plugin — owns the + * implementations use this so the kernel, not each plugin, owns the * observability contract for encoder calls (format/extension/row_count/ * bytes_written attributes, status, error_kind). * diff --git a/src/core/sinks/materialize.js b/src/core/sinks/materialize.js index b0725933..10b849d5 100644 --- a/src/core/sinks/materialize.js +++ b/src/core/sinks/materialize.js @@ -43,13 +43,13 @@ import { * * Three sink shapes are supported: * - * - **request**: `{ plugin }` — resolves that plugin's registered sink + * - **request**: `{ plugin }`, resolves that plugin's registered sink * contribution. * - **blob**: `{ writer, destination }` where the writer provides - * `hypaware.encoder` — resolves encoder and destination blob-store + * `hypaware.encoder`, resolves encoder and destination blob-store * contributions. * - **table-format**: `{ writer, destination }` where the writer provides - * `hypaware.table-format` — resolves table-format provider, blob-store + * `hypaware.table-format`, resolves table-format provider, blob-store * from destination, inner encoder from `config.encoder` pin or default * `@hypaware/format-parquet`. * @@ -167,7 +167,7 @@ async function materializeRequest(runtime, instanceName, raw, opts) { if (contributions.length > 1) { throw materializeError( 'sink_contribution_ambiguous', - `sink '${instanceName}': plugin '${pluginName}' registered ${contributions.length} sink contributions — cannot select unambiguously` + `sink '${instanceName}': plugin '${pluginName}' registered ${contributions.length} sink contributions - cannot select unambiguously` ) } diff --git a/test/core/action-backfill.test.js b/test/core/action-backfill.test.js index e29234d5..3e210c54 100644 --- a/test/core/action-backfill.test.js +++ b/test/core/action-backfill.test.js @@ -124,8 +124,8 @@ test('desired() does not fail open on a non-boolean on_join (treats it as opt-ou makeCtx({ plugins: [{ name: '@hypaware/claude', enabled: true, config: { backfill: { on_join: 'false' } } }] }) ) assert.deepEqual(stringFalse, [], 'on_join:"false" (string) must not run backfill') - // A truthy non-boolean is equally malformed and equally suppressed — - // only a real boolean true (or an absent flag) runs the import. + // A truthy non-boolean is equally malformed and equally suppressed. + // Only a real boolean true (or an absent flag) runs the import. const numberOne = handler.desired( makeCtx({ plugins: [{ name: '@hypaware/claude', enabled: true, config: { backfill: { on_join: 1 } } }] }) ) @@ -300,7 +300,7 @@ test('driven through the reconciler: a failed perform writes a failed marker, th assert.equal(marker.backfill['@hypaware/claude'].status, 'done') assert.equal(marker.backfill['@hypaware/claude'].rows, 42) - // Run-once: a done marker short-circuits — no third spawn. + // Run-once: a done marker short-circuits. No third spawn. const p3 = await reconciler.reconcile(input) assert.equal(p3.results[0].outcome, 'skipped') assert.equal(calls.length, 2, 'no spawn after the done marker lands') diff --git a/test/core/action-reconciler.test.js b/test/core/action-reconciler.test.js index 536af8cd..be03f3ac 100644 --- a/test/core/action-reconciler.test.js +++ b/test/core/action-reconciler.test.js @@ -35,7 +35,7 @@ async function makeFixture() { return { tmp, stateRoot } } -/** A minimal reconcile input — handlers under test ignore these. */ +/** A minimal reconcile input: handlers under test ignore these. */ const INPUT = { config: /** @type {any} */ ({ version: 2, plugins: [] }), backfills: /** @type {any} */ ({ register() {}, get() { return undefined }, list() { return [] } }), @@ -95,7 +95,7 @@ test('reconcile runs a desired action once and short-circuits on the done marker [['@hypaware/claude', 'done']] ) - // Second pass: the done marker short-circuits — perform is not re-run. + // Second pass: the done marker short-circuits, so perform is not re-run. clock += 1000 const second = await reconciler.reconcile(INPUT) assert.equal(handler.performCalls, 1, 'perform must not run again on a done marker') @@ -118,7 +118,7 @@ test('a missed pass (no marker yet) runs on the next reconcile call', async () = const { tmp, stateRoot } = await makeFixture() try { // Handler wants nothing on the first pass (the join hasn't confirmed), - // then names a unit on the second — the gap is picked up. + // then names a unit on the second pass, and the gap is picked up. let active = false /** @type {ActionHandler & { performCalls: number }} */ const handler = { @@ -159,7 +159,7 @@ test('atomic marker read/write round-trips through readClientActionStatus and re log: NOOP_LOG, }) - // Empty before any pass — both the standalone reader and the handle agree. + // Empty before any pass: both the standalone reader and the handle agree. assert.deepEqual(readClientActionStatus({ stateRoot }), { byKind: {} }) assert.deepEqual(reconciler.readStatus(), { byKind: {} }) @@ -208,7 +208,7 @@ test('a failed perform writes a failed marker (not done) and retries with bumped assert.equal(file.backfill['@hypaware/codex'].reason, 'transcript dir missing') assert.equal(file.backfill['@hypaware/codex'].attempts, 1) - // A failed marker is not terminal — the next pass retries and bumps attempts. + // A failed marker is not terminal: the next pass retries and bumps attempts. const p2 = await reconciler.reconcile(INPUT) assert.equal(handler.performCalls, 2) assert.equal(p2.results[0].outcome, 'failed') @@ -260,7 +260,7 @@ test('a corrupt marker file does not wedge reconcile (treated as empty, pass sti try { // Write garbage where the atomic marker store should be. `hyp status` // already swallows this (readClientActionStatus), but reconcile() read - // it through a bare JSON.parse — a corrupt marker wedged ALL actions + // it through a bare JSON.parse: a corrupt marker wedged ALL actions // while status reported clean. It must now degrade to an empty store. const controlDir = path.join(stateRoot, 'config-control') fs.mkdirSync(controlDir, { recursive: true }) diff --git a/test/core/ai-gateway-dataset.test.js b/test/core/ai-gateway-dataset.test.js index a0123e44..1daa21f7 100644 --- a/test/core/ai-gateway-dataset.test.js +++ b/test/core/ai-gateway-dataset.test.js @@ -42,7 +42,7 @@ test('ai-gateway registers cache partitioning for source columns and iceberg fie assert.ok(dataset.cachePartitioning) assert.deepEqual(dataset.cachePartitioning.source.columns, ['client_name', 'conversation_source', 'provider']) assert.equal(dataset.cachePartitioning.source.fallback, 'unknown') - // @ref LLP 0030#breaking — the required identity partition field is + // @ref LLP 0030#breaking: the required identity partition field is // session_id (always present), not conversation_id (now nullable); // conversation_id rides along as a secondary, non-required field. assert.equal(dataset.cachePartitioning.iceberg.fields.length, 4) @@ -222,11 +222,11 @@ test('ai-gateway createDataSource pads declared schema columns absent from an ol // physically lacks it must surface as a null-valued column, not throw // ColumnNotFoundError. `withSchemaColumns` is the only thing guaranteeing // this, and every other test stages partitions that already carry all - // columns — so without this test a regression dropping the padding would pass + // columns. So without this test a regression dropping the padding would pass // the suite while breaking real queries over old data. @ref LLP 0032#capture const cacheRoot = await makeTmpDir('schema-pad') try { - // Stage a partition with ONLY id/date — no repo-identity columns at all. + // Stage a partition with ONLY id/date: no repo-identity columns at all. await appendRowsToSourceTable( cacheRoot, DATASET_NAME, ['source=claude'], TEST_COLUMNS, [{ id: 1, date: '2026-05-26' }] diff --git a/test/core/blob-store.test.js b/test/core/blob-store.test.js index 08d0970a..107fd8ba 100644 --- a/test/core/blob-store.test.js +++ b/test/core/blob-store.test.js @@ -129,7 +129,7 @@ test('local-fs BlobStore deleteObject removes the key and is idempotent', async assert.ok(store.deleteObject) await store.deleteObject({ key: 'a/one.bin' }) assert.equal(await store.getObject({ key: 'a/one.bin' }), null) - // Idempotent — deleting a missing key is not an error. + // Idempotent: deleting a missing key is not an error. await store.deleteObject({ key: 'a/one.bin' }) } finally { await fs.rm(base, { recursive: true, force: true }) @@ -240,8 +240,8 @@ test('local-fs BlobStore accepts a Readable stream as body', async () => { /** * Build a minimal in-memory `BlobStore` fixture used to exercise the * BlobStore contract from the consumer side (e.g. a future - * format-iceberg plugin). The fixture is intentionally simple — a Map - * keyed by object key — but honours every contract bit the surface + * format-iceberg plugin). The fixture is intentionally simple (a Map + * keyed by object key), but honours every contract bit the surface * needs: kind, ordering, prefix filtering, ifNoneMatch, return shape. * * @returns {BlobStore} diff --git a/test/core/boot-layered-config.test.js b/test/core/boot-layered-config.test.js index 9f832fa0..fb2d351c 100644 --- a/test/core/boot-layered-config.test.js +++ b/test/core/boot-layered-config.test.js @@ -13,7 +13,7 @@ import { centralSeedPath } from '../../src/core/config/apply.js' // The merge wiring lives in boot.js; activate nothing (`{ activate: [] }`) // so these assertions stay about the two-layer resolution, not plugin // activation. boot.config is the effective merge regardless of profile. -// @ref LLP 0031#two-layers-merged-at-boot [tests] +// @ref LLP 0031#two-layers-merged-at-boot [tests]: async function makeHome() { const hypHome = await fs.mkdtemp(path.join(os.tmpdir(), 'hyp-boot-layered-')) @@ -109,7 +109,7 @@ test('a local plugin that invalidates the merge (capability tie) is dropped; cen plugins: [{ name: '@hypaware/central' }, { name: '@hypaware/format-parquet' }], }) + '\n') - // Local adds a second encoder (ties with central — capability_ambiguous, + // Local adds a second encoder (ties with central: capability_ambiguous, // dropped) plus a clean additive client (kept). await fs.writeFile(defaultConfigPath(hypHome), JSON.stringify({ version: 2, diff --git a/test/core/cache-iceberg-schema-evolution.test.js b/test/core/cache-iceberg-schema-evolution.test.js index a2c7dd90..8fe79cf7 100644 --- a/test/core/cache-iceberg-schema-evolution.test.js +++ b/test/core/cache-iceberg-schema-evolution.test.js @@ -1,7 +1,7 @@ // @ts-check // Proves issue #102: additive (nullable) column changes evolve the cache table -// schema IN PLACE — the new column is queryable after a plain append, with no +// schema IN PLACE: the new column is queryable after a plain append, with no // recreate and no backfill. Old rows read the new column as `null`; new rows // populate it. Breaking changes still reject. See LLP 0029. @@ -93,7 +93,7 @@ async function tableSchemaCount(dir) { return metadata.schemas.length } -test('additive nullable column evolves the cache schema in place — new column queryable, no recreate', async () => { +test('additive nullable column evolves the cache schema in place - new column queryable, no recreate', async () => { const dir = await makeTmpDir('additive') try { // 1. Write a row under the V1 schema. @@ -104,7 +104,7 @@ test('additive nullable column evolves the cache schema in place — new column assert.equal(await tableHasColumn(dir, 'agent_id'), false, 'agent_id absent before evolution') // 2. Append a row under the V2 schema (new nullable `agent_id`) into the - // SAME table dir — no recreate, no separate backfill step. + // SAME table dir: no recreate, no separate backfill step. await appendRowsToTable(dir, V2_COLUMNS, [ { conversation_id: 'c2', client_name: 'claude', date: '2026-06-16', message: 'new', agent_id: 'agent-7' }, ], { declaration: DECLARATION }) @@ -126,7 +126,7 @@ test('additive nullable column evolves the cache schema in place — new column } }) -test('backfill works against the evolved schema — repeated V2 appends keep the new column populated', async () => { +test('backfill works against the evolved schema - repeated V2 appends keep the new column populated', async () => { const dir = await makeTmpDir('backfill') try { await appendRowsToTable(dir, V1_COLUMNS, [ @@ -155,7 +155,7 @@ test('backfill works against the evolved schema — repeated V2 appends keep the } }) -test('schema evolution is a no-op when columns are unchanged — only one schema persists', async () => { +test('schema evolution is a no-op when columns are unchanged - only one schema persists', async () => { const dir = await makeTmpDir('noop') try { await appendRowsToTable(dir, V1_COLUMNS, [ @@ -182,7 +182,7 @@ test('breaking changes still reject after the table exists (no in-place evolutio { conversation_id: 'c1', client_name: 'claude', date: '2026-06-16', message: 'm1' }, ], { declaration: DECLARATION }) - // New REQUIRED column — Iceberg can't back-fill it; must reject. + // New REQUIRED column: Iceberg can't back-fill it, must reject. /** @type {ColumnSpec[]} */ const requiredAddition = [...V1_COLUMNS, { name: 'must_have', type: 'STRING', nullable: false }] await assert.rejects( @@ -192,7 +192,7 @@ test('breaking changes still reject after the table exists (no in-place evolutio /new column "must_have" must be nullable/ ) - // Type change on an existing column — must reject. + // Type change on an existing column: must reject. /** @type {ColumnSpec[]} */ const typeChanged = V1_COLUMNS.map(c => c.name === 'message' ? { ...c, type: /** @type {const} */ ('INT64') } : c @@ -222,7 +222,7 @@ const V2_NOTE_WIDENED = [ { name: 'note', type: 'STRING', nullable: true }, ] -test('required→nullable widening evolves the table in place — a later null append lands', async () => { +test('required→nullable widening evolves the table in place - a later null append lands', async () => { // LLP 0029 lists a column that widened required→nullable as additive. The // merge keeps the column's field id, so the old id-set "needs evolution" // check missed it: the table stayed marked required and a null write was a @@ -237,7 +237,7 @@ test('required→nullable widening evolves the table in place — a later null a const before = await tableField(dir, 'note') assert.equal(before?.required, true, 'note starts required') - // Append the widened schema with a NULL note — only valid if the table + // Append the widened schema with a NULL note: only valid if the table // schema actually evolved to mark note nullable. await appendRowsToTable(dir, V2_NOTE_WIDENED, [ { conversation_id: 'c2', date: '2026-06-16', note: null }, @@ -259,7 +259,7 @@ test('required→nullable widening evolves the table in place — a later null a test('a rejected append does not advance the table schema (coerce before any commit)', async () => { // The new nullable column AND a row that violates an existing required // column arrive in the same batch. Coercion runs before evolveSchemaInPlace, - // so the schema commit never happens — the table is unchanged, not left with + // so the schema commit never happens: the table is unchanged, not left with // schema ahead of data. const dir = await makeTmpDir('atomic') try { diff --git a/test/core/cache-resettle-sweep.test.js b/test/core/cache-resettle-sweep.test.js index 45962b11..b73e7172 100644 --- a/test/core/cache-resettle-sweep.test.js +++ b/test/core/cache-resettle-sweep.test.js @@ -15,13 +15,13 @@ import { matchKey } from '../../hypaware-core/plugins-workspace/claude/src/trans /** * Re-settle sweep (LLP 0027 "Re-settle sweep"): the finalize-vs-transcript - * race can commit a fallback-hash row ALONE — its transcript line not yet - * on disk — and the uuid twin lands in a SEPARATE later flush. Flush-time + * race can commit a fallback-hash row ALONE, with its transcript line not yet + * on disk, and the uuid twin lands in a SEPARATE later flush. Flush-time * settlement only collapses twins that co-batch, so this split pair is a * permanent duplicate the flush path can never revisit. The maintenance * sweep re-runs the dataset's own settleBatch over the committed fallback * row during compaction, upgrades it to native identity, and dedupes it - * against the committed uuid twin — collapsing the pair after the fact. + * against the committed uuid twin, collapsing the pair after the fact. * * @import { ColumnSpec } from '../../collectivus-plugin-kernel-types.d.ts' */ @@ -77,7 +77,7 @@ test('re-settle sweep collapses a split fallback/uuid twin pair after the fact', await storage.appendRows(tablePath, COLUMNS, [nativeRow()]) await storage.flushTable(tablePath, { force: true }) - // Pre-condition: flush-time settle could NOT fix the split — both the + // Pre-condition: flush-time settle could NOT fix the split, since both the // fallback and the uuid twin are committed. const before = await readPartIds(storage, tablePath) assert.deepEqual(before.sort(), ['fallbackhash16ab#0', 'u-assist#0'].sort(), @@ -99,7 +99,7 @@ test('re-settle sweep collapses a split fallback/uuid twin pair after the fact', assert.deepEqual(after, ['u-assist#0'], 'the pair collapses to the single native uuid row') - // No fallback marker survives — the surviving row is fully native. + // No fallback marker survives. The surviving row is fully native. const rows = await readRows(storage, tablePath) assert.equal(rows.length, 1) assert.equal(rows[0].message_id, 'u-assist') @@ -131,7 +131,7 @@ test('re-settle sweep upgrades a lone fallback row even when its twin never arri const after = await readPartIds(storage, tablePath) assert.deepEqual(after, ['u-assist#0'], - 'the lone fallback is upgraded to native identity (kept, not dropped — no twin to collapse onto)') + 'the lone fallback is upgraded to native identity (kept, not dropped - no twin to collapse onto)') } finally { await env.cleanup() } @@ -202,7 +202,7 @@ test('a fallback marker auto-triggers compaction without force; a no-marker part } }) -test('an unmatchable fallback does not force a rewrite every tick — only on new data', async () => { +test('an unmatchable fallback does not force a rewrite every tick - only on new data', async () => { // Regression for the forced-rewrite loop: a fallback whose transcript never // lands stays a candidate forever. Without the re-settle baseline it would // force a full rewrite on every maintenance tick. The baseline makes the diff --git a/test/core/cache-retention-maintenance.test.js b/test/core/cache-retention-maintenance.test.js index ee01f8a3..f33bc08c 100644 --- a/test/core/cache-retention-maintenance.test.js +++ b/test/core/cache-retention-maintenance.test.js @@ -537,7 +537,7 @@ test('retention cursor stays accurate after new data arrives between ticks', asy const result1 = await enforcer.tick() assert.equal(result1.sourceTableResults[0].rowsDeleted, 2) - // New data arrives — creates a new snapshot, triggering a re-scan + // New data arrives: creates a new snapshot, triggering a re-scan await appendRowsToSourceTable(cacheRoot, 'test_ds', ['source=claude'], COLUMNS, [ { id: 4, value: 'new-recent', timestamp: recentTimestamp }, ]) @@ -674,7 +674,7 @@ test('maintenance reclaims a stale cursor-orphaned table dir with no .retired ma const stale = new Date(Date.now() - 2 * 60 * 60 * 1000) await fs.utimes(orphan, stale, stale) - // A second generation that is still fresh — must be treated as a + // A second generation that is still fresh: must be treated as a // possibly-in-flight compaction and left alone. const fresh = path.join(sourceDir, 'table-1800000000000') await fs.mkdir(path.join(fresh, 'data'), { recursive: true }) diff --git a/test/core/cache-storage.test.js b/test/core/cache-storage.test.js index 873763ca..66eb2d12 100644 --- a/test/core/cache-storage.test.js +++ b/test/core/cache-storage.test.js @@ -291,7 +291,7 @@ test('readSpooledRows yields unflushed rows and goes empty after flush', async ( { id: 2, value: 'b' }, ]) - // Before flush, the rows live only in the spool — invisible to the + // Before flush, the rows live only in the spool, invisible to the // committed-partition scan but visible to readSpooledRows. const pending = await drain(storage.readSpooledRows('my_ds')) assert.equal(pending.length, 2) @@ -329,8 +329,8 @@ test('readSpooledRows projects to requested columns and filters by dataset', asy test('readSpooledRows skips a parseable envelope missing columns, matching what flush drops', async () => { // A parseable spool line whose envelope lacks `columns` is malformed: // streamFlushFile drops it and never commits its rows. readSpooledRows - // must skip the same rows, or backfill would dedupe against — and so - // refuse to materialize — rows that flush will never commit. + // must skip the same rows, or backfill would dedupe against (and thus + // refuse to materialize) rows that flush will never commit. const cacheRoot = await makeTmpDir('spool-read-malformed') try { const storage = createQueryStorageService({ cacheRoot }) diff --git a/test/core/cli/tui/non-tty.test.js b/test/core/cli/tui/non-tty.test.js index c13f1224..f4c33d1f 100644 --- a/test/core/cli/tui/non-tty.test.js +++ b/test/core/cli/tui/non-tty.test.js @@ -25,7 +25,7 @@ function makeFakeTty() { const stdout = new PassThrough() Object.defineProperty(stdin, 'isTTY', { value: true }) Object.defineProperty(stdout, 'isTTY', { value: true }) - // @ts-expect-error — PassThrough does not declare setRawMode. + // @ts-expect-error: PassThrough does not declare setRawMode. stdin.setRawMode = () => {} return { stdin, stdout } } diff --git a/test/core/cli/tui/package-export.test.js b/test/core/cli/tui/package-export.test.js index 3cc38d12..bb4664c3 100644 --- a/test/core/cli/tui/package-export.test.js +++ b/test/core/cli/tui/package-export.test.js @@ -13,7 +13,7 @@ test('package export "hypaware/tui" resolves to the same module', () => { assert.equal(typeof viaPackage.confirm, 'function') assert.equal(typeof viaPackage.PromptCancelledError, 'function') // The identical function references prove the exports point at the - // very same module instance — a sibling package importing + // very same module instance, a sibling package importing // 'hypaware/tui' gets the same code. assert.strictEqual(viaPackage.multiselect, direct.multiselect) assert.strictEqual(viaPackage.PromptCancelledError, direct.PromptCancelledError) diff --git a/test/core/cli/tui/runtime.test.js b/test/core/cli/tui/runtime.test.js index 4af0f754..429f40e0 100644 --- a/test/core/cli/tui/runtime.test.js +++ b/test/core/cli/tui/runtime.test.js @@ -39,7 +39,7 @@ function makeTty() { Object.defineProperty(stdin, 'isTTY', { value: true }) Object.defineProperty(stdout, 'isTTY', { value: true }) Object.defineProperty(stdin, 'isRaw', { value: false, writable: true }) - // @ts-expect-error — PassThrough does not declare setRawMode but the runtime probes for it. + // @ts-expect-error: PassThrough does not declare setRawMode but the runtime probes for it. stdin.setRawMode = (enabled) => { /** @type {any} */ (stdin).isRaw = enabled } @@ -332,7 +332,7 @@ test('runtime: redraw moves up by physical (wrapped) rows on a narrow terminal', Object.defineProperty(stdout, 'isTTY', { value: true }) Object.defineProperty(stdout, 'columns', { value: 24 }) Object.defineProperty(stdin, 'isRaw', { value: false, writable: true }) - // @ts-expect-error — PassThrough has no setRawMode; the runtime probes for it. + // @ts-expect-error: PassThrough has no setRawMode; the runtime probes for it. stdin.setRawMode = (enabled) => { /** @type {any} */ (stdin).isRaw = enabled } /** @type {string[]} */ const chunks = [] @@ -359,7 +359,7 @@ test('runtime: redraw moves up by physical (wrapped) rows on a narrow terminal', assert.ok(firstFrame, 'first frame not captured') assert.ok(redrawFrame, 'redraw frame not captured') // The redraw's cursor-up count must equal the wrapped row count of the - // first frame — otherwise stale rows are left on screen (duplication). + // first frame, otherwise stale rows are left on screen (duplication). assert.equal(cursorUpCount(redrawFrame), countPhysicalRows(firstFrame, 24)) // And that count must exceed the naive newline count, proving wrapping // actually occurred in this fixture. diff --git a/test/core/command-dispatch.test.js b/test/core/command-dispatch.test.js index 5b98e844..91906b8e 100644 --- a/test/core/command-dispatch.test.js +++ b/test/core/command-dispatch.test.js @@ -186,7 +186,7 @@ test('top-level help lists commands declared by config-active plugins', async () assert.equal(stderr.text(), '') const out = stdout.text() // context-graph is in the default surface; vector-search is excluded - // from default but config-enabled here — both must appear, with the + // from default but config-enabled here: both must appear, with the // summary pulled from the manifest's `contributes.commands`. assert.match(out, /graph project\s+Project every registered source contract/) assert.match(out, /graph neighbors\s+Walk the activity graph/) @@ -197,15 +197,15 @@ test('top-level help lists commands declared by config-active plugins', async () test('top-level help lists a local plugin addition on a fleet-joined host', async () => { // Regression: help must resolve the effective config the same way - // `bootKernel` does — with the discovered plugin catalog. A joined host + // `bootKernel` does: with the discovered plugin catalog. A joined host // has a central layer; the merge validator, run WITHOUT the catalog, // treats every bundled plugin as unknown and drops the local `plugins[]` - // addition (`@hypaware/context-graph`) — so help would hide `graph` + // addition (`@hypaware/context-graph`), so help would hide `graph` // commands that actually dispatch. const hypHome = await fs.mkdtemp(path.join(os.tmpdir(), 'hypaware-help-joined-')) const controlDir = path.join(hypHome, 'hypaware', 'config-control') await fs.mkdir(controlDir, { recursive: true }) - // Central layer (authoritative, fleet-owned) — does NOT include the graph. + // Central layer (authoritative, fleet-owned): does NOT include the graph. await fs.writeFile( path.join(controlDir, 'seed.json'), JSON.stringify({ version: 2, plugins: [{ name: '@hypaware/local-fs' }] }) @@ -350,7 +350,7 @@ test('top-level help lists the installed plugin that replaces an excluded bundle // colliding `gascity attach` summary) are what dispatch would run. assert.match(out, /gascity attach\s+attach \(installed winner\)/) assert.match(out, /gascity real\s+only the installed plugin declares this/) - // The replaced skeleton's commands never dispatch — they must not appear. + // The replaced skeleton's commands never dispatch: they must not appear. assert.equal(out.includes('gascity phantom'), false) assert.equal(out.includes('bundled skeleton'), false) }) @@ -358,7 +358,7 @@ test('top-level help lists the installed plugin that replaces an excluded bundle test('top-level help advertises no commands for an installed plugin that shadows a bundled first-party name', async () => { // Regression: an installed plugin shadowing a bundled first-party plugin // makes real boot reject before any command dispatches. Help must not - // advertise either side's commands — none of them will ever run. + // advertise either side's commands: none of them will ever run. const hypHome = await fs.mkdtemp(path.join(os.tmpdir(), 'hypaware-help-shadow-')) const workspaceDir = path.join(hypHome, 'bundled-workspace') await stageBundledPlugin({ @@ -595,7 +595,7 @@ function fakeClientKernel() { ]) ) - // Fake an `hypaware.ai-gateway@2.0.0` surface — that's the range + // Fake an `hypaware.ai-gateway@2.0.0` surface: that's the range // the CLI dispatcher requires after the phase-1 capability bump, // and the test's `dispatch(['attach', ...])` call resolves against // it before it ever reaches the client hooks above. diff --git a/test/core/config-apply-deps.test.js b/test/core/config-apply-deps.test.js index ef22f541..6707d954 100644 --- a/test/core/config-apply-deps.test.js +++ b/test/core/config-apply-deps.test.js @@ -16,7 +16,7 @@ import { getEntry, writeLock } from '../../src/core/plugin_install/lock.js' */ /** - * Pin enforcement is the apply path's core security property — nothing + * Pin enforcement is the apply path's core security property: nothing * may substitute code after the config was authored (LLP 0025 * install-on-config). The apply-engine tests mock these deps away, so * the real decisions are exercised here against real fixtures: a @@ -81,7 +81,7 @@ test('bundled plugin: matching version pin is satisfied without an install; hash try { const deps = buildConfigApplyDeps({ stateRoot: fx.stateRoot, workspaceDir: fx.workspaceDir }) // The artifact_hash refers to a git release artifact that - // legitimately differs from the npm-bundled tree — a garbage hash + // legitimately differs from the npm-bundled tree, so a garbage hash // must not fail a bundled pin (LLP 0025 bundled-first-party). const result = await deps.installPinnedPlugins([ { name: '@hypaware/otel', version: '9.9.9', artifact_hash: 'f'.repeat(64) }, @@ -193,7 +193,7 @@ test('a fetched artifact failing its hash pin is an artifact_hash_mismatch and n test('a correct hash pin installs, and validation then sees the plugin it could not know before', async () => { // The install-before-validate ordering only works because a fresh - // catalog is discovered per call — this is the integration check for + // catalog is discovered per call: this is the integration check for // a served config naming a not-yet-installed plugin. const fx = await makeFixture() const git = await buildGitPluginFixture() diff --git a/test/core/config-apply-recovery.test.js b/test/core/config-apply-recovery.test.js index 02f0f40e..41313a63 100644 --- a/test/core/config-apply-recovery.test.js +++ b/test/core/config-apply-recovery.test.js @@ -50,7 +50,7 @@ const REMOTE_CONFIG = { }, } -/** A state root with **no** join seed — so a first apply has nowhere to +/** A state root with **no** join seed, so a first apply has nowhere to * roll back to (`previous_slot` is null), exactly the single-usable-slot * case behind #141. */ async function makeSeedlessFixture() { @@ -99,7 +99,7 @@ function writeState(stateRoot, state) { test('an expired first apply with no distinct previous slot does not mark the active etag bad', async () => { // Seedless first apply: commit() lands on slot 'b' with previous_slot=null // (the single usable slot). When probation expires there is nowhere to - // roll back to — the engine must NOT record the still-active etag as + // roll back to: the engine must NOT record the still-active etag as // bad_etag (that is the #141 wedge). const { stateRoot } = await makeSeedlessFixture() const { control } = makeControl({ stateRoot }) diff --git a/test/core/config-apply-section-validators.test.js b/test/core/config-apply-section-validators.test.js index 359f1164..8e28f553 100644 --- a/test/core/config-apply-section-validators.test.js +++ b/test/core/config-apply-section-validators.test.js @@ -19,7 +19,7 @@ import { defaultConfigPath } from '../../src/core/config/schema.js' * claude/codex `backfill` policy) would be accepted instead of rolled back. * * These tests boot the real kernel so the claude validator registers exactly - * the way the daemon registers it — no hand-rolled registry. + * the way the daemon registers it: no hand-rolled registry. */ /** @param {string} hypHome */ @@ -58,7 +58,7 @@ test('apply validation rejects a malformed plugin backfill block via the live se const fx = await bootWithClaude() try { // The claude plugin must have registered its `config_sections` validator - // during activation — that is the registry the apply path now consults. + // during activation: that is the registry the apply path now consults. // (`list()` is on the concrete registry; the runtime types it as the // narrower ConfigRegistry, so reach it through a local cast.) const registry = /** @type {{ list(): Array<{ plugin: string }> }} */ ( @@ -110,7 +110,7 @@ test('apply validates a backfill block for a plugin the document INTRODUCES but // *already-active* plugins. A central config that first introduces a // backfill-capable plugin (the realistic join/fleet path) would skip its // `config.backfill` validation. The apply path now discovers the introduced - // plugin's section validator from disk (side-effect-free — never activates + // plugin's section validator from disk (side-effect-free, never activates // it), so the malformed block is rejected, not silently accepted. // // Boot WITHOUT claude/codex so neither section is in the live registry. @@ -166,7 +166,7 @@ test('apply validates a backfill block for a plugin the document INTRODUCES but test('introduced-plugin discovery rejects a malformed block even without the live registry', async () => { // Even with NO live registry passed (a non-daemon caller), the apply path // discovers the introduced plugin's validator from disk and rejects the bad - // block. (Before round-2 this exact shape silently accepted it — the + // block. (Before round-2 this exact shape silently accepted it: the // per-plugin validator was dead without the live registry.) const fx = await bootWith(['@hypaware/ai-gateway']) try { diff --git a/test/core/config-merge.test.js b/test/core/config-merge.test.js index 5e0b4f98..d7867e65 100644 --- a/test/core/config-merge.test.js +++ b/test/core/config-merge.test.js @@ -89,7 +89,7 @@ test('query is local-only: the local block wins, a central query block is ignore // ---- resolveLayeredConfig: the validation-driven drop pass (LLP 0031 // §central-layer-is-sacrosanct). A fake validator keeps these unit-level: // "two encoders enabled together is a capability tie". -// @ref LLP 0031#central-layer-is-sacrosanct [tests] +// @ref LLP 0031#central-layer-is-sacrosanct [tests]: /** @param {any} cfg */ function fakeValidate(cfg) { @@ -123,7 +123,7 @@ test('resolveLayeredConfig: a valid-in-isolation local addition that invalidates }) test('resolveLayeredConfig: an error the central layer carries alone never drops a local entry', () => { - // Central is already ambiguous on its own — that is apply-time's concern. + // Central is already ambiguous on its own: that is apply-time's concern. // The local layer is blameless, so nothing local is dropped. const central = { version: 2, diff --git a/test/core/daemon-reconcile.test.js b/test/core/daemon-reconcile.test.js index 31d3afa2..169d5a1a 100644 --- a/test/core/daemon-reconcile.test.js +++ b/test/core/daemon-reconcile.test.js @@ -45,7 +45,7 @@ test('createReconcilePassScheduler runs exactly one pass per idle edge', async ( await sched.settle() assert.equal(runs, 1) - // A second edge after the first pass settles runs again — one pass per edge. + // A second edge after the first pass settles runs again: one pass per edge. sched.schedule('edge-2') await sched.settle() assert.equal(runs, 2) @@ -64,7 +64,7 @@ test('createReconcilePassScheduler is single-flight and coalesces concurrent edg const sched = createReconcilePassScheduler({ run }) sched.schedule('edge-1') // starts pass 1, blocks on its release - // schedule() returned while pass 1 is still in flight — proof the pass runs + // schedule() returned while pass 1 is still in flight: proof the pass runs // off the caller's stack (it never blocks the tick loop / confirm poll). assert.equal(runs, 1) @@ -85,7 +85,7 @@ test('createReconcilePassScheduler is single-flight and coalesces concurrent edg test('createReconcilePassScheduler.settle resolves immediately when no pass is in flight', async () => { const sched = createReconcilePassScheduler({ run: async () => {} }) - await sched.settle() // never scheduled — resolves without hanging + await sched.settle() // never scheduled: resolves without hanging assert.ok(true) }) @@ -98,7 +98,7 @@ test('createReconcilePassScheduler keeps scheduling after a pass throws', async sched.schedule('edge-1') await sched.settle() assert.equal(runs, 1) - // A throw must not wedge the guard — the next edge still runs. + // A throw must not wedge the guard: the next edge still runs. sched.schedule('edge-2') await sched.settle() assert.equal(runs, 2) @@ -261,7 +261,7 @@ test('runDaemon does not run the boot pass while probation is still active (fres test('the confirmation edge during active probation drives exactly one reconcile pass (fresh-join path)', async () => { // The primary LLP 0037 path: a fresh join boots under active probation - // (no boot pass — covered above) and the FIRST authenticated config poll + // (no boot pass: covered above) and the FIRST authenticated config poll // clears probation, firing the confirmation edge that schedules backfill. // Previously only the no-fire half was tested; this drives the edge through // the real configControl seam and asserts the pass actually runs once. @@ -322,7 +322,7 @@ test('the confirmation edge during active probation drives exactly one reconcile configControl.confirmPoll() await waitFor(() => calls.length === 1) - // A second confirmPoll is a no-op (probation already cleared) — no extra pass. + // A second confirmPoll is a no-op (probation already cleared): no extra pass. configControl.confirmPoll() await tick() await tick() diff --git a/test/core/daemon.test.js b/test/core/daemon.test.js index 2b39bb84..6b68ba0e 100644 --- a/test/core/daemon.test.js +++ b/test/core/daemon.test.js @@ -175,7 +175,7 @@ test('renderDaemonInstall renders a deterministic LaunchAgent dry-run payload', }) test('installers default to relaunch-on-exit (staged restart requirement, LLP 0017)', () => { - // Defaults — no keepAlive/restart override. The service manager MUST + // Defaults: no keepAlive/restart override. The service manager MUST // relaunch the daemon after a staged config-apply exit. const launchd = renderDaemonInstall({ platform: 'darwin', @@ -262,7 +262,7 @@ test('runDaemon reload refreshes plugin config before source.reload', async () = } }) -test('runDaemon reload re-merges the central layer (does not re-read local alone) — #111 regression', async () => { +test('runDaemon reload re-merges the central layer (does not re-read local alone) - #111 regression', async () => { const hypHome = await fs.mkdtemp(path.join(os.tmpdir(), 'hypaware-daemon-reload-central-')) let handle try { @@ -284,7 +284,7 @@ test('runDaemon reload re-merges the central layer (does not re-read local alone }) // The fixture's config lives ONLY in the central layer (the join - // seed). The local layer exists and loads, but never names it — so a + // seed). The local layer exists and loads, but never names it: so a // reload that re-read the local layer alone would lose the central // config and write `reloaded: undefined`. const seedPath = centralSeedPath(stateRoot) @@ -315,7 +315,7 @@ test('runDaemon reload re-merges the central layer (does not re-read local alone await handle.reload() - // After SIGHUP the merge still carries the central layer's config — + // After SIGHUP the merge still carries the central layer's config: // not `undefined` from a local-only re-read. assert.deepEqual(JSON.parse(await fs.readFile(statePath, 'utf8')), { started: 'central', @@ -385,7 +385,7 @@ test('runDaemon health event derives from aggregate state and excludes failed so .filter(Boolean) .map((line) => JSON.parse(line)) - // (a) The boot health event reflects degraded — NOT daemon.healthy. + // (a) The boot health event reflects degraded: NOT daemon.healthy. assert.equal( events.some((e) => e.event === 'daemon.healthy'), false, diff --git a/test/core/flush-streams.test.js b/test/core/flush-streams.test.js index ae5cbdd9..a72e6636 100644 --- a/test/core/flush-streams.test.js +++ b/test/core/flush-streams.test.js @@ -17,7 +17,7 @@ function fakeStream({ writableLength, mode }) { write(_chunk, cb) { if (mode === 'drain') queueMicrotask(() => cb()) else if (mode === 'error') queueMicrotask(() => handlers.error?.()) - // mode 'hang' never invokes either — used with a timeout race + // mode 'hang' never invokes either. Used with a timeout race }, } } diff --git a/test/core/init-configured-entry.test.js b/test/core/init-configured-entry.test.js index d97d3b12..9e608932 100644 --- a/test/core/init-configured-entry.test.js +++ b/test/core/init-configured-entry.test.js @@ -16,7 +16,7 @@ import { // Re-running `hypaware` on a configured install fronts the first-run // picker with a friendly summary + menu instead of starting fresh. -// @ref LLP 0011#returning-to-a-configured-install [tests] +// @ref LLP 0011#returning-to-a-configured-install [tests]: /** * @import { HypAwareStatusReport } from '../../src/core/daemon/types.d.ts' diff --git a/test/core/init-guard.test.js b/test/core/init-guard.test.js index 9a7a92c9..67a6e5a9 100644 --- a/test/core/init-guard.test.js +++ b/test/core/init-guard.test.js @@ -10,7 +10,7 @@ import { dispatch } from '../../src/core/cli/dispatch.js' import { runPickerWalkthrough } from '../../src/core/cli/walkthrough.js' // `init` writes the user-owned local layer; the overwrite guard is the -// non-destructive half of #111. @ref LLP 0031#local-layer-writers [tests] +// non-destructive half of #111. @ref LLP 0031#local-layer-writers [tests]: function makeBuf() { let value = '' diff --git a/test/core/integration.test.js b/test/core/integration.test.js index 29ea2bba..0f497f8a 100644 --- a/test/core/integration.test.js +++ b/test/core/integration.test.js @@ -236,7 +236,7 @@ test('run is the escape hatch for multi-client attach (every client surfaces)', .map((line) => JSON.parse(line).client) .sort() assert.deepEqual(clients, ['claude', 'codex']) - // run().json keeps only the final line — exactly why attach()/detach() + // run().json keeps only the final line: exactly why attach()/detach() // reject 'all' rather than route the fan-out through that single result. assert.equal(/** @type {{ client: string }} */ (result.json).client, 'codex') }) diff --git a/test/core/join-command.test.js b/test/core/join-command.test.js index 0c6a4dba..b3c136c2 100644 --- a/test/core/join-command.test.js +++ b/test/core/join-command.test.js @@ -58,7 +58,7 @@ test('join writes the central seed (mode 0600) and skips daemon install with --n ) assert.equal(code, 0, stdout.text()) - // The seed is the central layer — written under config-control/, never + // The seed is the central layer: written under config-control/, never // to the user-owned hypaware-config.json (the #111 fix). const seedPath = seedPathFor(hypHome) const stat = await fs.stat(seedPath) @@ -108,7 +108,7 @@ test('join supersedes a stale active slot so the fresh token is honored (#139)', // Simulate a previously-joined host whose applied central config lost // its identity (the JWT broke, prompting a re-join). The active slot's - // central sink carries an empty identity — no bootstrap token — so on + // central sink carries an empty identity: no bootstrap token, so on // its own it can never bootstrap. Before the fix, boot resolution // preferred this slot over the freshly written seed and the new token // was silently ignored. diff --git a/test/core/plugin-install-confirm.test.js b/test/core/plugin-install-confirm.test.js index 7b28fb07..e431be65 100644 --- a/test/core/plugin-install-confirm.test.js +++ b/test/core/plugin-install-confirm.test.js @@ -294,7 +294,7 @@ test('renderConfirmationSummary: update header shows diff arrows when version/re test('installPlugin (git): non-tty without confirm callback succeeds (legacy direct caller)', async () => { // The `confirm` callback is the trust gate. When it is not supplied - // the install proceeds — this preserves the legacy programmatic path + // the install proceeds, which preserves the legacy programmatic path // for callers that already know they want to commit. const { stateDir, sourceUrl, commitSha, cleanup } = await buildGitFixture() try { diff --git a/test/core/query-context-controls.test.js b/test/core/query-context-controls.test.js index dda81260..1ed61a3a 100644 --- a/test/core/query-context-controls.test.js +++ b/test/core/query-context-controls.test.js @@ -75,7 +75,7 @@ test('input result is not mutated', () => { }) test('truncation is lazy: rows past the budget are never touched', () => { - // A row whose field throws on access — clipping or serializing it would + // A row whose field throws on access: clipping or serializing it would // throw. It sits past the cutoff (row 0 fills the budget, row 1 triggers // the break), so a lazy implementation must never reach row 2. const r0 = { a: 'small' } diff --git a/test/core/query-sql-error.test.js b/test/core/query-sql-error.test.js index 28cd3117..65563da3 100644 --- a/test/core/query-sql-error.test.js +++ b/test/core/query-sql-error.test.js @@ -5,7 +5,7 @@ import assert from 'node:assert/strict' import { executeQuerySql } from '../../src/core/query/sql.js' -/** Minimal registry/storage stubs — parse failures fire before either is used. */ +/** Minimal registry/storage stubs: parse failures fire before either is used. */ const registry = { getDataset: () => null, listDatasets: () => [] } const storage = { cacheRoot: '/tmp/hypaware-test', pendingInfo: async () => ({ pending: false }) } diff --git a/test/core/remote-credentials.test.js b/test/core/remote-credentials.test.js index 1075002f..f5969503 100644 --- a/test/core/remote-credentials.test.js +++ b/test/core/remote-credentials.test.js @@ -63,7 +63,7 @@ test('resolveToken errors with guidance when neither env nor file has a token', const dir = await tmpState() const r = await resolveToken({ target: 'prod', env: {}, stateDir: dir }) assert.equal(r.ok, false) - assert.match(/** @type {any} */ (r).error, /no token for 'prod' — run 'hyp remote login prod'/) + assert.match(/** @type {any} */ (r).error, /no token for 'prod' - run 'hyp remote login prod'/) assert.match(/** @type {any} */ (r).error, /HYP_REMOTE_TOKEN_PROD/) }) diff --git a/test/core/sinks-dispatch.test.js b/test/core/sinks-dispatch.test.js index d0f6e8fb..9a91340e 100644 --- a/test/core/sinks-dispatch.test.js +++ b/test/core/sinks-dispatch.test.js @@ -152,7 +152,7 @@ test('instantiate table-format sink wires blobStore + encoder + config into crea test('instantiate table-format sink intersects supports tags between provider and encoder', async () => { const registry = createSinkRegistry() const provider = makeTableFormatProvider({ supports: ['queryable'] }) - // Encoder that does NOT advertise queryable — the resolved sink should + // Encoder that does NOT advertise queryable. The resolved sink should // drop the tag (the queryable-only-when-both-agree rule). const encoder = makeEncoder({ format: 'jsonl', extension: 'jsonl', supports: [] }) const handle = await registry.instantiate({ @@ -279,7 +279,7 @@ test('validateConfig rejects table-format writer without blob-store destination' }) test('validateConfig rejects writer providing neither encoder nor table-format', async () => { - // `@hypaware/ai-gateway` provides hypaware.ai-gateway — neither encoder + // `@hypaware/ai-gateway` provides hypaware.ai-gateway: neither encoder // nor table-format. Using it as a blob-sink writer must surface as // sink_writer_invalid, not the generic sink_pair_incompatible kind. const result = await validateConfig({ diff --git a/test/core/status-client-actions.test.js b/test/core/status-client-actions.test.js index 9ddecf39..61a5b93a 100644 --- a/test/core/status-client-actions.test.js +++ b/test/core/status-client-actions.test.js @@ -15,11 +15,11 @@ import { renderStatusJson, renderStatusText } from '../../src/core/cli/core_comm * @import { ClientActionReport } from '../../src/core/daemon/types.d.ts' */ -// T6 — the client-action reconciler status surface (LLP 0036 / 0041). The +// T6: the client-action reconciler status surface (LLP 0036 / 0041). The // collector reads the marker file (it never runs a pass) and derives a // per-provider done/failed/pending/n-a section; a failed action is loud but // never flips `overall` to `degraded`. -// @ref LLP 0041#failure-is-surfaced-not-fatal [tests] +// @ref LLP 0041#failure-is-surfaced-not-fatal [tests]: async function makeHome() { const hypHome = await fs.mkdtemp(path.join(os.tmpdir(), 'hyp-status-actions-')) @@ -63,7 +63,7 @@ test('mixed done/failed/pending/n-a reads cleanly off the marker store + config' const stateRoot = path.join(hypHome, 'hypaware') // Joined host: central enables the gateway plus two backfill-declaring - // plugins — claude (on_join true → pending until a pass runs) and codex + // plugins: claude (on_join true → pending until a pass runs) and codex // (on_join false → suppressed → n/a). const seedPath = centralSeedPath(stateRoot) await fs.mkdir(path.dirname(seedPath), { recursive: true }) @@ -110,7 +110,7 @@ test('mixed done/failed/pending/n-a reads cleanly off the marker store + config' test('a malformed on_join block renders n/a (not pending) on a joined host', async () => { // Regression (round-2): a *present but malformed* `on_join` (the JSON typo // `on_join: "false"`) is an opt-out, exactly as `backfillHandler.desired()` - // reads it — so the reconciler never writes a marker and the honest state is + // reads it: so the reconciler never writes a marker and the honest state is // `n/a`. Status used to read `on_join !== false` inline, so the string // "false" (!== the boolean false) showed `pending` forever. Both consumers // now share `readBackfillPolicy`, so they agree. @@ -164,7 +164,7 @@ test('a default-on backfill target (enabled client, no explicit block) shows pen const m = byKey(report.clientActions.actions) assert.equal(m.get('@hypaware/claude')?.state, 'pending') assert.equal(m.get('@hypaware/claude')?.kind, 'backfill') - // ai-gateway is enabled but is not a backfill provider — it must not appear. + // ai-gateway is enabled but is not a backfill provider: it must not appear. assert.equal(m.has('@hypaware/ai-gateway'), false) }) @@ -184,7 +184,7 @@ test('a default-on client on a NON-joined host keeps the V1 surface (no spurious test('a failed backfill does not flip overall to degraded', async () => { const hypHome = await makeHome() - // Minimal, otherwise-healthy config (gateway only — no client advisories) + // Minimal, otherwise-healthy config (gateway only: no client advisories) // so the only notable state is the failed action marker. await fs.writeFile(defaultConfigPath(hypHome), JSON.stringify({ version: 2, diff --git a/test/core/status-layered.test.js b/test/core/status-layered.test.js index 71e4baca..a7c99ecf 100644 --- a/test/core/status-layered.test.js +++ b/test/core/status-layered.test.js @@ -13,7 +13,7 @@ import { renderStatusJson, renderStatusText } from '../../src/core/cli/core_comm // `hyp status` on a centrally-managed host must restore inspectability of // the merged config: per-entry provenance + the dropped-local section. -// @ref LLP 0031#status-provenance [tests] +// @ref LLP 0031#status-provenance [tests]: async function makeHome() { const hypHome = await fs.mkdtemp(path.join(os.tmpdir(), 'hyp-status-layered-')) @@ -85,7 +85,7 @@ test('a joined host surfaces provenance and the dropped-local section', async () // rendering that turns it into the user-visible provenance tags, the // dropped-local section (collision *and* invalid-merge), and the JSON // `config_layers` block. Rendering off a collected report avoids booting -// the kernel. @ref LLP 0031#status-provenance [tests] +// the kernel. @ref LLP 0031#status-provenance [tests]: function makeBuf() { let value = '' diff --git a/test/core/verb-registry.test.js b/test/core/verb-registry.test.js index 9b868685..6cfad0ac 100644 --- a/test/core/verb-registry.test.js +++ b/test/core/verb-registry.test.js @@ -49,7 +49,7 @@ test('projection is idempotent when a command of that name already exists', () = const commands = createCommandRegistry() commands.register({ name: 'demo verb', summary: 's', usage: 'u', run: async () => 0 }) const verbs = createVerbRegistry({ commandRegistry: commands }) - // Must not throw on the duplicate command name — the verb still registers. + // Must not throw on the duplicate command name: the verb still registers. assert.doesNotThrow(() => verbs.register(makeVerb())) assert.ok(verbs.getByTool('demo_verb')) }) diff --git a/test/core/verb-remote.test.js b/test/core/verb-remote.test.js index f1120e85..52cc5db4 100644 --- a/test/core/verb-remote.test.js +++ b/test/core/verb-remote.test.js @@ -94,7 +94,7 @@ test('a missing token errors with login guidance', async (t) => { const { ctx, err } = ctxWith({ HYP_HOME: '/tmp/none-missing' }) const code = await cmd.run(['SELECT 1', '--remote', 'prod'], ctx) assert.equal(code, 2) - assert.match(err.join(''), /no token for 'prod' — run 'hyp remote login prod'/) + assert.match(err.join(''), /no token for 'prod' - run 'hyp remote login prod'/) }) test('a remote isError result maps to a nonzero exit with the message', async (t) => { diff --git a/test/core/walkthrough-backfill.test.js b/test/core/walkthrough-backfill.test.js index 0624c52d..1e9c32b1 100644 --- a/test/core/walkthrough-backfill.test.js +++ b/test/core/walkthrough-backfill.test.js @@ -155,7 +155,7 @@ test('--yes mode runs bounded backfill automatically without a consent prompt', assert.equal(backfill.calls[0].retentionDays, 7, 'backfill is bounded by the retention window') }) -test('--no-daemon still backfills — it is a local file import', async () => { +test('--no-daemon still backfills - it is a local file import', async () => { const env = await tmpEnv('hypaware-bf-nodaemon-') const stdout = makeBuf() const stderr = makeBuf() @@ -271,8 +271,8 @@ test('picked clients without a registered backfill provider are skipped', async // The injected runner advertises only `claude`, so a codex pick has no // matching provider: the finale intersects picks with the runner's // `available` set and skips the rest. (In production both claude and - // codex are registered — see the all-available boot test in - // boot-installed.test.js — this exercises the empty-intersection path.) + // codex are registered. See the all-available boot test in + // boot-installed.test.js. This exercises the empty-intersection path.) const backfill = makeBackfill(['claude']) const result = await runPickerWalkthrough({ diff --git a/test/core/walkthrough-detect.test.js b/test/core/walkthrough-detect.test.js index 09e774fe..ed55fd3b 100644 --- a/test/core/walkthrough-detect.test.js +++ b/test/core/walkthrough-detect.test.js @@ -50,7 +50,7 @@ test('picker pre-checks detected sources, labels them, and defaults export to lo assert.notEqual(codex?.checked, true) assert.doesNotMatch(codex?.label ?? '', /detected/) - // The export question is no longer asked — local-parquet is the + // The export question is no longer asked: local-parquet is the // unconditional default. assert.equal(seen.find((q) => q.pickType === 'sinks'), undefined, 'export question must not be asked') diff --git a/test/core/walkthrough-tui-happy.test.js b/test/core/walkthrough-tui-happy.test.js index 95ee2f28..44dd37e8 100644 --- a/test/core/walkthrough-tui-happy.test.js +++ b/test/core/walkthrough-tui-happy.test.js @@ -27,7 +27,7 @@ function makeFakeTty() { const stdout = new PassThrough() Object.defineProperty(stdin, 'isTTY', { value: true }) Object.defineProperty(stdout, 'isTTY', { value: true }) - // @ts-expect-error — PassThrough does not declare setRawMode but the runtime probes for it. + // @ts-expect-error: PassThrough does not declare setRawMode but the runtime probes for it. stdin.setRawMode = () => {} /** @type {string[]} */ const writes = [] @@ -87,7 +87,7 @@ test('runPickerWalkthrough drives the TUI multiselect end-to-end when stdin+stdo await feed(io.stdin, ['\x1b[B', '\x1b[B', ' ', '\r']) // No export prompt: the picker always defaults to local-parquet now. - // Retention prompt — empty buffer + enter accepts the 30-day default. + // Retention prompt: empty buffer + enter accepts the 30-day default. await settle() await feed(io.stdin, ['\r']) @@ -174,13 +174,13 @@ test('runPickerWalkthrough falls back to the legacy numbered prompt under HYP_NO // Mark BOTH ends as TTYs so the only signal that flips the router is // the HYP_NO_TUI escape. This proves the env override wins over the // TTY probe. Answers: source '3' (raw-anthropic), then retention - // default — the export question is no longer asked. + // default: the export question is no longer asked. Object.defineProperty(input, 'isTTY', { value: true }) const stdout = answerDrivenOutput(input, ['3\n', '\n'], true) const stderr = makeBuf() - // HYP_NO_TUI flows through opts.env — the same channel real callers - // use — so this test also exercises the env-threading contract. + // HYP_NO_TUI flows through opts.env: the same channel real callers + // use. So this test also exercises the env-threading contract. const result = await runPickerWalkthrough({ capabilities: /** @type {any} */ ({}), stdout, diff --git a/test/plugins/ai-gateway-backfill-materializer.test.js b/test/plugins/ai-gateway-backfill-materializer.test.js index 1d652c66..0da1e331 100644 --- a/test/plugins/ai-gateway-backfill-materializer.test.js +++ b/test/plugins/ai-gateway-backfill-materializer.test.js @@ -82,7 +82,7 @@ test('materializer parity with live projector: native message ids', async () => /** @type {AiGatewayProjectedExchange} */ const projection = { provider: 'anthropic', - // @ref LLP 0030#decision — Claude carries session_id (conversation_id null). + // @ref LLP 0030#decision: Claude carries session_id (conversation_id null). session_id: 'conv-native', conversation_source: 'claude', client_name: 'claude', @@ -253,7 +253,7 @@ test('backfill dedupe: a clean rerun writes zero new rows', async () => { // Simulate the runner writing + flushing the first run's rows. storage.commit(first) // A second run carries a fresh run id, so it re-scans and observes the - // now-committed part_ids — every row is a duplicate and is skipped. + // now-committed part_ids: every row is a duplicate and is skipped. const second = await m.materialize(item(nativeProjection()), matCtx(storage, 'run-2')) assert.deepEqual(second, []) }) @@ -285,7 +285,7 @@ test('backfill dedupe: matches legacy committed rows that predate part_id via me test('backfill dedupe: a re-yielded item within the same run is skipped without re-committing', async () => { const m = aiGatewayBackfillMaterializer() - const storage = dedupeStorage() // stays empty — nothing is committed between calls + const storage = dedupeStorage() // stays empty. Nothing is committed between calls const first = await m.materialize(item(nativeProjection()), matCtx(storage, 'run-1')) assert.equal(first.length, 2) // Same run id → the in-run memo already holds these part_ids, so a @@ -309,7 +309,7 @@ test('backfill dedupe: an unreadable partition degrades to no dedupe rather than storage.failReads() const rows = await m.materialize(item(nativeProjection()), matCtx(storage, 'run-1')) // The scan throws, the seen-set stays empty, so every row passes through - // — a dedupe miss is recoverable (compaction), dropping rows is not. + // A dedupe miss is recoverable (compaction), dropping rows is not. assert.equal(rows.length, 2) }) @@ -349,7 +349,7 @@ test('backfill dedupe: spool dedupe also matches legacy rows via message_id + pa test('backfill dedupe: committed and spooled part_ids are unioned into one seen-set', async () => { const m = aiGatewayBackfillMaterializer() - // m1 already committed, m2 still in the spool — together they cover the + // m1 already committed, m2 still in the spool. Together they cover the // whole conversation, so a backfill of the same conversation is a no-op. const storage = dedupeStorage([{ part_id: 'm1#0' }]) storage.spool([{ part_id: 'm2#0' }]) @@ -360,7 +360,7 @@ test('backfill dedupe: committed and spooled part_ids are unioned into one seen- test('backfill dedupe: an unreadable spool degrades to committed-only dedupe', async () => { const m = aiGatewayBackfillMaterializer() // m1 committed; the spool read throws. The committed scan still folds in - // m1, and m2 (never seen) passes through — the spool failure is absorbed. + // m1, and m2 (never seen) passes through. The spool failure is absorbed. const storage = dedupeStorage([{ part_id: 'm1#0' }]) storage.failSpoolReads() const rows = await m.materialize(item(nativeProjection()), matCtx(storage, 'run-1')) @@ -373,7 +373,7 @@ test('backfill dedupe: a storage stub without readSpooledRows still dedupes agai // Storage exposes the committed read surface but no spool surface; the // spool scan is feature-detected away and committed dedupe is unaffected. const storage = dedupeStorage([{ part_id: 'm1#0' }]) - // @ts-expect-error — intentionally drop the spool surface for this case + // @ts-expect-error: intentionally drop the spool surface for this case delete storage.readSpooledRows const rows = await m.materialize(item(nativeProjection()), matCtx(storage, 'run-1')) assert.equal(rows.length, 1) diff --git a/test/plugins/ai-gateway-graph-boot.test.js b/test/plugins/ai-gateway-graph-boot.test.js index 87cd9292..64a06967 100644 --- a/test/plugins/ai-gateway-graph-boot.test.js +++ b/test/plugins/ai-gateway-graph-boot.test.js @@ -17,7 +17,7 @@ import { requireGraphRuntime } from '../../hypaware-core/plugins-workspace/conte // *plugin* dependency on @hypaware/context-graph (not just the capability dep) so // the resolver activates the provider first. Capability deps are interchangeable // and do not pin activation order, so a capability-only connector would race ahead -// of the provider and `requireCapability` would throw here — which the stubbed +// of the provider and `requireCapability` would throw here, which the stubbed // activate unit test cannot catch. test('the gateway+graph+connector chain activates in order and the connector registers its contract', async () => { const hypHome = await fs.mkdtemp(path.join(os.tmpdir(), 'hyp-graph-boot-')) diff --git a/test/plugins/ai-gateway-graph-bridge.test.js b/test/plugins/ai-gateway-graph-bridge.test.js index b9279d98..f25851c9 100644 --- a/test/plugins/ai-gateway-graph-bridge.test.js +++ b/test/plugins/ai-gateway-graph-bridge.test.js @@ -8,7 +8,7 @@ import { createAiGatewayGraphContract } from '../../hypaware-core/plugins-worksp // Cross-source convergence guard (LLP 0032). A repo, commit, or file seen by // BOTH @hypaware/github and a recorded Claude/Codex session must land on ONE -// graph node — the whole point of the bridge. Convergence is automatic given a +// graph node: the whole point of the bridge. Convergence is automatic given a // shared natural key + a shared id recipe (LLP 0023 §content-addressed-ids), and // the GitHub plugin imports THIS repo's id recipe, so the only thing that can // break convergence is a key-normalization drift between this connector's @@ -17,7 +17,7 @@ import { createAiGatewayGraphContract } from '../../hypaware-core/plugins-worksp // asserting the HOST bridge mints the same ids from a session's captured // remote/sha/path proves the join fires. If a host key recipe drifts, these // fail; if the GitHub side drifts, its pins change and these must be updated in -// lockstep — either way the break is deliberate and visible. +// lockstep: either way the break is deliberate and visible. const GITHUB_PINS = { repo: 'e1505143b1ca95f6a92c3681', // nodeId('Repo', 'octocat/hello-world') commit: 'c40ec7e789b96f5b036504dd', // nodeId('Commit', '6dcb…db5e') @@ -83,7 +83,7 @@ test('host Commit -in-> Repo edge converges with the GitHub edge', () => { test('worktrees of one repo converge on a single File node', () => { // Same file, two checkouts: the main repo and a linked worktree. Different // absolute paths and repo roots, but the remote and relpath are identical, so - // the bridge key — and therefore the node id — is the same. Absolute-path + // the bridge key (and therefore the node id) is the same. Absolute-path // keying (pre-0032) would have split this into two File nodes. const main = rule('node', 'File').toRow({ tool_name: 'Edit', diff --git a/test/plugins/ai-gateway-graph-contract.test.js b/test/plugins/ai-gateway-graph-contract.test.js index 2d96bcd2..6992ebd1 100644 --- a/test/plugins/ai-gateway-graph-contract.test.js +++ b/test/plugins/ai-gateway-graph-contract.test.js @@ -14,7 +14,7 @@ import { // Build the contract the way the connector's activate() does: from the graph // plugin's generic kit (the bridge-key recipe is the connector's own, imported // inside the contract). The rules' row identity + provenance are therefore the -// real end-to-end ones — these assertions double as the digest-stability guard. +// real end-to-end ones, and these assertions double as the digest-stability guard. const KIT = { nodeId, edgeId, makeRowBuilders } const contract = createAiGatewayGraphContract(KIT) @@ -41,7 +41,7 @@ test('contract carries its source/projector metadata', () => { assert.equal(contract.projectorVersion, PROJECTOR_VERSION) }) -// @ref LLP 0030#decision — the Session node keys on session_id (the +// @ref LLP 0030#decision: the Session node keys on session_id (the // session container, always present); conversation_id is null for Claude. test('Session rule builds a node keyed on session_id with pruned props', () => { const r = rule('node', 'Session') @@ -164,7 +164,7 @@ test('numeric natural keys are stringified', () => { assert.equal(row.node_id, nodeId('Session', '42')) }) -// @ref LLP 0026#decision — Claude harness aux exchanges are retained (tagged +// @ref LLP 0026#decision: Claude harness aux exchanges are retained (tagged // attributes.claude.aux_kind) rather than dropped; the graph contract must // exclude them so security-monitor traffic mints no Session/App/Tool noise. test('every rule selects attributes so the shared aux filter has its input', () => { diff --git a/test/plugins/ai-gateway-message-projector.test.js b/test/plugins/ai-gateway-message-projector.test.js index 43b3f40c..04673192 100644 --- a/test/plugins/ai-gateway-message-projector.test.js +++ b/test/plugins/ai-gateway-message-projector.test.js @@ -269,7 +269,7 @@ test('projector-supplied message_id and previous_message_id are preserved', asyn test('supplied message_id without history gets the immediate predecessor as previous_message_id', async () => { // Adapter projectors (Claude transcripts, Codex native ids) supply - // message_id but never previous_message_id — the gateway fills the + // message_id but never previous_message_id. The gateway fills the // immediate predecessor (0/1-element) so enriched rows match fallback // rows. Full ancestry is the transitive closure of these links. const projector = createAiGatewayMessageProjector({ @@ -405,7 +405,7 @@ test('previous_message_id chains are scoped per (conversation_id ?? session_id, // Main-loop second message chains only on the main-loop first. assert.deepEqual(mainTwo.previous_message_id, [mainOne.message_id]) - // Subagent's first message starts a FRESH chain — it must not include + // Subagent's first message starts a FRESH chain. It must not include // the main-loop ids. assert.deepEqual(agentOne.previous_message_id, []) // Subagent's second chains only on the subagent's first. @@ -495,7 +495,7 @@ test('row output is stripped to the schema (no extra fields leak)', async () => }) test('a multi-block usage-bearing message stamps usage on only the last part', () => { - // @ref LLP 0035#one-carrier — Claude backfill emits multi-block carrier + // @ref LLP 0035#one-carrier: Claude backfill emits multi-block carrier // messages (e.g. reasoning + parallel tool_use under one messageId). Usage is // per-response, so it must ride exactly one row (the last block), not every // block, or a plain SUM(attributes.usage.*) over-counts within the message. @@ -525,8 +525,8 @@ test('a multi-block usage-bearing message stamps usage on only the last part', ( assert.equal(carrier.part_type, 'tool_call') const usage = isPlainObject(carrier.attributes) ? carrier.attributes.usage : undefined assert.deepEqual(usage, { input_tokens: 100, output_tokens: 42, cache_read_tokens: 9 }) - // A plain SUM over the message's rows equals the single response's usage — - // no per-block over-count. + // A plain SUM over the message's rows equals the single response's usage. + // No per-block over-count. const summedOutput = rows.reduce((acc, r) => { const u = isPlainObject(r.attributes) ? r.attributes.usage : undefined return acc + (isPlainObject(u) && typeof u.output_tokens === 'number' ? u.output_tokens : 0) @@ -565,7 +565,7 @@ test('two Codex threads sharing a session_id keep separate start time and tool l }, { gatewayId: 'gw', state }) assert.equal(rowsT1[0].conversation_started_at, '2026-06-01T00:00:00.000Z') - // Thread-2 keeps its OWN start time — it does not inherit thread-1's. + // Thread-2 keeps its OWN start time. It does not inherit thread-1's. assert.equal(rowsT2[0].conversation_started_at, '2026-06-02T00:00:00.000Z') assert.equal(rowsT2[0].session_id, 'sess-shared') assert.equal(rowsT2[0].conversation_id, 'thread-2') @@ -605,7 +605,7 @@ test('restart replay: seeds seen-set from committed part_ids so prior history re // then a fresh post-restart listener replaying the SAME history through // a stub storage that reports those rows as committed. With the seen-set // seeded from committed message_ids, the replay must emit zero rows. - // Seeding scopes on session_id — the partition key (LLP 0030). + // Seeding scopes on session_id: the partition key (LLP 0030). const project = () => ({ provider: 'native', session_id: 'sess-restart', diff --git a/test/plugins/ai-gateway-proxy-routing.test.js b/test/plugins/ai-gateway-proxy-routing.test.js index e24d3f81..397cd26a 100644 --- a/test/plugins/ai-gateway-proxy-routing.test.js +++ b/test/plugins/ai-gateway-proxy-routing.test.js @@ -64,7 +64,7 @@ test('matchUpstream invokes match() and returns the first upstream whose match() assert.deepEqual(calls, ['anthropic:/v1/responses', 'codex:/v1/responses']) }) -test('matchUpstream short-circuits on the first match — lower-priority match() is not called', () => { +test('matchUpstream short-circuits on the first match - lower-priority match() is not called', () => { let lowCalled = false const compiled = compileUpstreams([ { diff --git a/test/plugins/central-config-pull.test.js b/test/plugins/central-config-pull.test.js index ac7e2cfa..fadb4aba 100644 --- a/test/plugins/central-config-pull.test.js +++ b/test/plugins/central-config-pull.test.js @@ -199,7 +199,7 @@ test('the steady timer keeps polling on the configured cadence', async () => { { status: 304 }, { status: 304 }, { status: 304 }, { status: 304 }, ]) // Sub-second cadence is rejected by config validation but accepted - // by the loop itself — that's what makes this test fast. + // by the loop itself. That's what makes this test fast. const { loop } = makeLoop({ configControl: control, fetchFn, pollIntervalSeconds: 0.02 }) loop.start() await new Promise((resolve) => setTimeout(resolve, 120)) @@ -239,7 +239,7 @@ test('an oversized Content-Length is rejected without reading the body', async ( /** @type {typeof fetch} */ const fetchFn = async () => { // A stream that never produces and never closes: only the - // Content-Length pre-reject can finish this poll promptly — the + // Content-Length pre-reject can finish this poll promptly. The // streaming counter would wait on it until the deadline. const stream = new ReadableStream({ pull() {} }) const response = new Response(stream, { status: 200, headers: { etag: 'rev-huge' } }) @@ -252,7 +252,7 @@ test('an oversized Content-Length is rejected without reading the body', async ( assert.deepEqual(control.staged, []) const row = log.rows.find((r) => r.fields.error_kind === 'config_document_too_large') assert.ok(row, 'expected the Content-Length pre-reject to fire') - // body_bytes reports the declared length — the streaming path could + // body_bytes reports the declared length. The streaming path could // never have observed this number from an empty stream. assert.equal(row?.fields.body_bytes, MAX_CONFIG_DOCUMENT_BYTES + 1) }) diff --git a/test/plugins/central-forward-chunking.test.js b/test/plugins/central-forward-chunking.test.js index c46151d0..1c9d0646 100644 --- a/test/plugins/central-forward-chunking.test.js +++ b/test/plugins/central-forward-chunking.test.js @@ -17,7 +17,7 @@ function makeLog() { } /** - * Storage whose table yields `count` rows one at a time — a stand-in for + * Storage whose table yields `count` rows one at a time: a stand-in for * the streaming Iceberg scan. Never builds an array of all rows, so the * test mirrors the memory-bounded production path. `rowFactory` lets a * test shape the rows (wide payloads, byte-identical rows); the default @@ -47,7 +47,7 @@ function makeStorage(tablePath, count, rowFactory) { /** * A query registry whose dataset resolves to `signal`. Pass `null` to - * model a dataset with **no** `sourceSignal` — the failure mode bug #2 + * model a dataset with **no** `sourceSignal`: the failure mode bug #2 * fixed, where the sink falls back to the (unknown) dataset name. * * @param {string | null} signal @@ -77,7 +77,7 @@ function makeFetch(responder) { /** @type {Array<{ url: string, batchId: string, lines: string[], rowCount: number }>} */ const calls = [] // Count of response bodies the sink cancelled (drained) before parking on - // backpressure — proves it releases the socket rather than leaking it. + // backpressure: proves it releases the socket rather than leaking it. let bodyCancels = 0 /** @type {typeof fetch} */ const fn = /** @type {any} */ (async (url, init) => { @@ -222,7 +222,7 @@ test('a dataset with no sourceSignal fails the partition for retry (unknown sign assert.equal(result.partitionsExported, 0) assert.equal(result.retryPartitions?.length, 1) assert.match(String(result.error), /unknown signal/) - // it never reached the wire — the signal is rejected before streaming + // it never reached the wire: the signal is rejected before streaming assert.equal(calls.length, 0) }) @@ -233,7 +233,7 @@ const MAX_CHUNK_BYTES = 4 * 1024 * 1024 test('the byte budget splits wide rows even when the row count is tiny', async () => { // 10 rows of ~1 MiB each: MAX_CHUNK_ROWS (5000) never trips, so only // the byte budget governs. This is the bound that actually prevents - // the OOM/oversized-body for wide `content_text` — the row-count tests + // the OOM/oversized-body for wide `content_text`: the row-count tests // above never exercise it. const wide = 'x'.repeat(1 << 20) const { sink, calls } = buildSink({ @@ -357,7 +357,7 @@ test('repeated 429s walk the ladder before succeeding', async () => { test('backpressure beyond the inline budget fails the partition for retry', async () => { // Persistent 429 with Retry-After: 120. The inline budget is 5 min, so - // two waits (240s) fit and the third would cross it — the chunk throws + // two waits (240s) fit and the third would cross it: the chunk throws // and the partition is handed back for the next tick (cheap: the server // dedupes the delivered prefix). const { sink, calls, sleeps } = buildSink({ diff --git a/test/plugins/central-identity-rejoin.test.js b/test/plugins/central-identity-rejoin.test.js index 770ed959..3fa65628 100644 --- a/test/plugins/central-identity-rejoin.test.js +++ b/test/plugins/central-identity-rejoin.test.js @@ -15,7 +15,7 @@ const NOW_SEC = 1_900_000_000 const now = () => NOW_SEC * 1000 /** - * Minimal unsigned JWT carrying a `sub` claim — the client decodes (does + * Minimal unsigned JWT carrying a `sub` claim: the client decodes (does * not verify) it to recover the gateway id. * @param {string} sub */ diff --git a/test/plugins/claude-backfill.test.js b/test/plugins/claude-backfill.test.js index 5455947d..1e7982b3 100644 --- a/test/plugins/claude-backfill.test.js +++ b/test/plugins/claude-backfill.test.js @@ -225,7 +225,7 @@ test('fixture transcript projects into canonical ai_gateway_messages rows', asyn transcript_path: filePath, cwd: '/work/repo-a', git_branch: 'feature/x', - // @ref LLP 0032#capture — the hook also captures repo identity; backfill + // @ref LLP 0032#capture: the hook also captures repo identity; backfill // must replay all three or re-imported Claude sessions drop out of the join. git_remote: 'git@github.com:acme/repo-a.git', head_sha: '0123456789abcdef0123456789abcdef01234567', @@ -248,7 +248,7 @@ test('fixture transcript projects into canonical ai_gateway_messages rows', asyn assert.equal(item.provenance?.native_id, 'sess-1') // Projection carries the bead-mandated conversation envelope. - // @ref LLP 0030#decision — the Claude session id is the session_id + // @ref LLP 0030#decision: the Claude session id is the session_id // partition key; conversation_id is null (no per-thread id). const exchange = value(item) assert.equal(exchange.provider, 'anthropic') @@ -258,7 +258,7 @@ test('fixture transcript projects into canonical ai_gateway_messages rows', asyn assert.equal(exchange.client_name, 'claude') assert.equal(exchange.cwd, '/work/repo-a') assert.equal(exchange.git_branch, 'feature/x') - // @ref LLP 0032#capture — repo identity rides the same record as cwd; the + // @ref LLP 0032#capture: repo identity rides the same record as cwd; the // live projector stamps these too, so backfilled rows converge with live. assert.equal(exchange.git_remote, 'git@github.com:acme/repo-a.git') assert.equal(exchange.head_sha, '0123456789abcdef0123456789abcdef01234567') @@ -365,7 +365,7 @@ test('assistant token usage is folded into attributes.usage like live capture', } }) -test('usage lands once — on the last block of a split assistant API message', async () => { +test('usage lands once - on the last block of a split assistant API message', async () => { const env = await stageEnv() try { // Claude Code writes one transcript line per content block; both lines of @@ -451,7 +451,7 @@ test('assistant model is surfaced per message, switches mid-session, and drops < parentUuid: 'u-asst-2', type: 'assistant', // `` is a sentinel for locally-generated assistant lines - // that never hit a model — it must not land in the model column. + // that never hit a model: it must not land in the model column. message: { role: 'assistant', content: [{ type: 'text', text: '[interrupted]' }], model: '' }, timestamp: '2026-05-20T10:00:07.000Z', }, @@ -569,7 +569,7 @@ test('native DAG identity is preserved verbatim', async () => { } }) -test('raw_frame is minimized — never a full transcript copy', async () => { +test('raw_frame is minimized - never a full transcript copy', async () => { const env = await stageEnv() try { const secret = 'SENSITIVE-PROMPT-BODY-should-not-be-copied' @@ -817,7 +817,7 @@ test('missing transcript root yields nothing without throwing', async () => { /** * A one-turn session whose transcript line carries a `cwd` (as Claude Code - * stamps it) but for which no session-context record exists — the shape of a + * stamps it) but for which no session-context record exists: the shape of a * session recorded before the hook captured git identity. * * @param {string} sessionId @@ -840,7 +840,7 @@ function rowsWithCwd(sessionId, cwd) { test('recovers git_remote/repo_root from the transcript cwd when the record predates git capture', async () => { const env = await stageEnv() try { - // No session-context record at all — the historical shape. + // No session-context record at all: the historical shape. await writeTranscript(env, '-Users-phil-workspace-repo-z', 'sess-z', rowsWithCwd('sess-z', '/Users/phil/workspace/repo-z')) /** @type {string[]} */ @@ -863,7 +863,7 @@ test('recovers git_remote/repo_root from the transcript cwd when the record pred assert.equal(exchange.cwd, '/Users/phil/workspace/repo-z') assert.equal(exchange.git_remote, 'git@github.com:acme/repo-z.git') assert.equal(exchange.repo_root, '/Users/phil/workspace/repo-z') - // head_sha is NEVER derived — current HEAD ≠ the session's HEAD. + // head_sha is NEVER derived: current HEAD ≠ the session's HEAD. assert.equal(exchange.head_sha, undefined) assert.deepEqual(derivedFor, ['/Users/phil/workspace/repo-z']) @@ -949,7 +949,7 @@ test('recovers git_remote from the record cwd when the record predates git captu const env = await stageEnv() try { // The canonical pre-0032 shape (LLP 0032): a session-context record EXISTS - // — cwd and git_branch were captured — but predates git-remote capture, so + // (cwd and git_branch were captured) but predates git-remote capture, so // it carries no git_remote/head_sha/repo_root. The record's cwd differs // from the transcript line's cwd to prove derivation keys on `record.cwd` // (the `record?.cwd ?? transcriptCwd` precedence), not the transcript line. @@ -991,7 +991,7 @@ test('recovers git_remote from the record cwd when the record predates git captu assert.equal(exchange.git_branch, 'feature/recover') assert.equal(exchange.git_remote, 'git@github.com:acme/repo-canon.git') assert.equal(exchange.repo_root, '/Users/phil/workspace/repo-canon') - // head_sha is NEVER derived — current HEAD ≠ the session's HEAD. + // head_sha is NEVER derived: current HEAD ≠ the session's HEAD. assert.equal(exchange.head_sha, undefined) // The recovered identity survives materialization into canonical rows. @@ -1012,7 +1012,7 @@ test('record repo_root is preserved when only the remote is derived', async () = // A partial pre-0032 record: the hook captured an authoritative repo_root // (`git rev-parse --show-toplevel`) but no git_remote. Derivation must // recover the remote WITHOUT clobbering the record's repo_root, even when - // the probe — run later / in a shifted worktree — reports a different + // the probe (run later / in a shifted worktree) reports a different // toplevel. This guards the `&& !exchange.repo_root` clause: drop it and // the derived repo_root would overwrite the record's authoritative value. const filePath = await writeTranscript(env, 'repo-partial', 'sess-partial', rowsWithCwd('sess-partial', '/transcript/line/cwd')) diff --git a/test/plugins/claude-git-repo.test.js b/test/plugins/claude-git-repo.test.js index b26c80ba..497df007 100644 --- a/test/plugins/claude-git-repo.test.js +++ b/test/plugins/claude-git-repo.test.js @@ -7,7 +7,7 @@ import { deriveRepoFromCwd } from '../../hypaware-core/plugins-workspace/claude/ /** * Unit tests for the backfill-time repo recovery helper. The git runner is - * injected so the derivation is hermetic — no real repo on disk. + * injected so the derivation is hermetic. No real repo on disk. */ /** @@ -45,7 +45,7 @@ test('derives redacted remote and repo_root, and never asks for HEAD', async () assert.equal(out.repo_root, '/Users/phil/workspace/repo') assert.equal(/** @type {any} */ (out).head_sha, undefined) - // It must never run `rev-parse HEAD` — a backfilled head_sha would be + // It must never run `rev-parse HEAD`. A backfilled head_sha would be // anachronistic. Only remote + toplevel are probed. const subcommands = calls.map((a) => (a[2] === 'rev-parse' ? `rev-parse ${a[3]}` : a[2])) assert.deepEqual(subcommands.sort(), ['config', 'rev-parse --show-toplevel']) diff --git a/test/plugins/claude-projector-identity.test.js b/test/plugins/claude-projector-identity.test.js index 2c951484..9bdb7f6a 100644 --- a/test/plugins/claude-projector-identity.test.js +++ b/test/plugins/claude-projector-identity.test.js @@ -13,7 +13,7 @@ import { createClaudeExchangeProjector } from '../../hypaware-core/plugins-works * End-to-end identity tests for the Claude exchange projector. Each * test wires the Claude projector through the gateway core's * dispatcher (with no other projector registered) so the assertions - * cover the same path that runs in production — including the + * cover the same path that runs in production: including the * gateway's fallback hash identity stamp. */ @@ -71,7 +71,7 @@ test('native DAG identity: uuid from JSONL transcript becomes message_id and pro assert.equal(assistantRows[0].parent_uuid, 'u-user-1') // Gateway must NOT stamp identity_source when the projector - // supplied message_id — the assertion guards the projector against + // supplied message_id. The assertion guards the projector against // a regression that drops `message_id` and silently falls back. for (const row of rows) { const claude = readAttrPath(row, ['attributes', 'claude']) @@ -303,8 +303,8 @@ test('subagent transcript under /subagents supplies sidechain identit test('transcript_path from session context also loads sibling subagent files', async () => { const env = await stageClaudeEnv() try { - // Non-standard location only reachable through transcript_path — - // the projects-dir scan can never find it, so a uuid match proves + // Non-standard location only reachable through transcript_path. + // The projects-dir scan can never find it, so a uuid match proves // the sibling / directory walk ran. const altDir = path.join(env.homeDir, 'alt-transcripts') const transcriptPath = path.join(altDir, 'sess-hook.jsonl') @@ -525,8 +525,8 @@ test('multi-block assistant turn splits into per-line uuid messages (LLP 0023)', }, }) - // One row per transcript line: user, assistant text, assistant tool_use — - // each its own message with a single part. + // One row per transcript line: user, assistant text, assistant tool_use. + // Each its own message with a single part. assert.equal(rows.length, 3) const textRow = rows.find((r) => r.message_id === 'u-s-text') const toolRow = rows.find((r) => r.message_id === 'u-s-tool') @@ -538,7 +538,7 @@ test('multi-block assistant turn splits into per-line uuid messages (LLP 0023)', assert.equal(toolRow.tool_name, 'Bash') // The native chain rides parent_uuid; previous_message_id is // gateway-owned (immediate predecessor) for enriched and fallback - // rows alike — here the text block that precedes this tool block. + // rows alike. Here the text block that precedes this tool block. assert.equal(toolRow.parent_uuid, 'u-s-text') assert.ok(Array.isArray(toolRow.previous_message_id)) assert.ok(/** @type {string[]} */ (toolRow.previous_message_id).includes('u-s-text')) @@ -776,7 +776,7 @@ test('missing transcript → gateway fallback identity + claude.identity_source assert.equal(row.provider_uuid, undefined, 'no transcript means no provider_uuid') } // The gateway stamps its own fallback marker AND the Claude - // projector stamps its own — both must be present so the row is + // projector stamps its own. Both must be present so the row is // unambiguous to operators querying by either marker. for (const row of rows) { const claude = readAttrPath(row, ['attributes', 'claude']) diff --git a/test/plugins/claude-session-context-hook.test.js b/test/plugins/claude-session-context-hook.test.js index 5e57beeb..2a6bc473 100644 --- a/test/plugins/claude-session-context-hook.test.js +++ b/test/plugins/claude-session-context-hook.test.js @@ -21,7 +21,7 @@ import { redactRemoteUserinfo, runClaudeSessionContextHook } from '../../hypawar import { appendSessionContext, defaultSessionContextFile, pickLatestMatching, readSessionContext } from '../../hypaware-core/plugins-workspace/claude/src/session_context.js' /** - * Roundtrip test for the phase-2 session-context channel — the path + * Roundtrip test for the phase-2 session-context channel: the path * that used to be `POST /_hypaware/session-context` on the gateway * and now lives entirely on disk: * @@ -83,7 +83,7 @@ test('hook → state file → projector roundtrip writes cwd onto the row', asyn // Stage 3: the projector dispatches an Anthropic exchange and // returns rows carrying the state-file cwd. No transcript on disk // for this session, so identity falls back to the gateway hash - // path — that's expected and exactly what the smoke for missing + // path. That's expected and exactly what the smoke for missing // transcripts asserts as well. const projector = createClaudeExchangeProjector({ homeDir: env.homeDir, @@ -109,7 +109,7 @@ test('hook → state file → projector roundtrip writes cwd onto the row', asyn } }) -// @ref LLP 0032#capture — the Claude hook captures repo identity (remote, full +// @ref LLP 0032#capture: the Claude hook captures repo identity (remote, full // HEAD sha, repo root) for the graph bridge, and the projector stamps it on // every row, the same way it already does cwd/git_branch. test('hook captures repo identity for a real git repo and the projector stamps it', async () => { @@ -228,7 +228,7 @@ test('redactRemoteUserinfo strips only the credential-bearing URL form', () => { redactRemoteUserinfo('https://x-access-token:ghp_SECRET@github.com/acme/repo.git'), 'https://github.com/acme/repo.git', ) - // scp-like SSH authenticates by key — its git@ user is meaningful, kept intact. + // scp-like SSH authenticates by key. Its git@ user is meaningful, kept intact. assert.equal(redactRemoteUserinfo('git@github.com:acme/repo.git'), 'git@github.com:acme/repo.git') assert.equal(redactRemoteUserinfo('https://github.com/acme/repo.git'), 'https://github.com/acme/repo.git') assert.equal(redactRemoteUserinfo(undefined), undefined) diff --git a/test/plugins/claude-settlement.test.js b/test/plugins/claude-settlement.test.js index 13fbc374..b47600b3 100644 --- a/test/plugins/claude-settlement.test.js +++ b/test/plugins/claude-settlement.test.js @@ -128,7 +128,7 @@ test('settleBatch dispatches to the enricher and dedupes the upgraded row agains // --- helpers --------------------------------------------------------- -// @ref LLP 0030#decision — the settlement enricher groups fallback rows by +// @ref LLP 0030#decision: the settlement enricher groups fallback rows by // session_id (Claude conversation_id is null), so the row fixtures carry the // session in session_id, not conversation_id. /** @param {Partial> & { match_key: string }} f */ diff --git a/test/plugins/codex-backfill.test.js b/test/plugins/codex-backfill.test.js index c52b8f7c..7e4d5f69 100644 --- a/test/plugins/codex-backfill.test.js +++ b/test/plugins/codex-backfill.test.js @@ -284,7 +284,7 @@ test('modern rollout projects into canonical ai_gateway_messages rows', async () assert.equal(row.client_name, 'codex') assert.equal(row.cwd, '/work/repo') // First-class repo-identity columns survive materialization (LLP 0032). - // repo_root is null for Codex (no verified toplevel — §codex-repo-root). + // repo_root is null for Codex (no verified toplevel: see §codex-repo-root). assert.equal(row.git_remote, 'https://github.com/acme/repo.git') assert.equal(row.head_sha, 'abc123def') assert.equal(row.repo_root, undefined) @@ -325,11 +325,11 @@ test('token_count event folds per-turn usage (net of cache) onto the turn assist // turn's token_count event_msg. The per-turn delta is `last_token_usage`; // `total_token_usage` is the session's cumulative running total. They are // set to DIFFERENT (and deliberately larger) values here so a regression - // that read the cumulative total — the multiply-count trap — would fail the + // that read the cumulative total (the multiply-count trap) would fail the // net-input assertion below instead of passing by coincidence. // @ref LLP 0035#per-turn const lastUsage = { - input_tokens: 13761, // gross — includes the 9600 cached + input_tokens: 13761, // gross (includes the 9600 cached) cached_input_tokens: 9600, output_tokens: 484, reasoning_output_tokens: 189, @@ -371,7 +371,7 @@ test('token_count event folds per-turn usage (net of cache) onto the turn assist assert.equal(exchange.messages.length, 5) // Usage lands on the LAST text/tool_use assistant message of the turn (the - // function_call here), one carrier per response — same row Claude uses. + // function_call here), one carrier per response (same row Claude uses). // Derived from `last_token_usage` (NOT the cumulative `total_token_usage`): // NET of cache 13761 − 9600 = 4161; 4161 + 9600 + 484 == 14245 total. Were // the cumulative read instead, input would be 90000 − 50000 = 40000 here. @@ -446,7 +446,7 @@ test('multi-turn token_count: each turn stamps its own per-turn delta on its own { timestamp: '2026-06-24T00:00:08.000Z', payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: 'turn two' }] } }, { timestamp: '2026-06-24T00:00:09.000Z', payload: { type: 'message', role: 'assistant', content: [{ type: 'output_text', text: 'final answer' }] } }, { type: 'event_msg', timestamp: '2026-06-24T00:00:10.000Z', payload: { type: 'token_count', info: { total_token_usage: turn2Total, last_token_usage: turn2Delta } } }, - // Turn 3: reasoning ONLY — no text/tool_use assistant in range, so its + // Turn 3: reasoning ONLY (no text/tool_use assistant in range), so its // usage is DROPPED rather than mis-attributed to an earlier turn's row. { timestamp: '2026-06-24T00:00:11.000Z', payload: { type: 'reasoning', summary: [{ type: 'summary_text', text: 'thinking 3' }] } }, { type: 'event_msg', timestamp: '2026-06-24T00:00:12.000Z', payload: { type: 'token_count', info: { total_token_usage: turn3Total, last_token_usage: turn3Delta } } }, @@ -473,7 +473,7 @@ test('multi-turn token_count: each turn stamps its own per-turn delta on its own usage: { input_tokens: 4161, cache_read_tokens: 9600, output_tokens: 484, reasoning_tokens: 189, total_tokens: 14245 }, }) - // Turn 2's usage rides its own text reply — its own delta, NOT turn 1's and + // Turn 2's usage rides its own text reply: its own delta, NOT turn 1's and // NOT the cumulative total (which would give input 8161, output 684). const turn2Carrier = findMsg((m) => m.role === 'assistant' && m.content[0].type === 'text' && m.content[0].text === 'final answer') assert.deepEqual(turn2Carrier.attributes, { @@ -491,7 +491,7 @@ test('multi-turn token_count: each turn stamps its own per-turn delta on its own // No row anywhere carries turn 3's delta (it was dropped, not mis-stamped). const stamped = exchange.messages.filter((/** @type {any} */ m) => m.attributes?.usage) - assert.equal(stamped.length, 2, 'exactly two carrier rows — turns 1 and 2') + assert.equal(stamped.length, 2, 'exactly two carrier rows - turns 1 and 2') for (const m of stamped) { assert.notEqual(/** @type {any} */ (m.attributes).usage.total_tokens, 7600, 'turn 3 usage never stamped') } diff --git a/test/plugins/codex-exchange-projector.test.js b/test/plugins/codex-exchange-projector.test.js index 714f92d0..e4b05266 100644 --- a/test/plugins/codex-exchange-projector.test.js +++ b/test/plugins/codex-exchange-projector.test.js @@ -173,7 +173,7 @@ test('OpenAI Responses body usage is normalized onto one assistant response item assert.deepEqual(projection.messages.map((/** @type {any} */ m) => m.role), ['user', 'assistant', 'assistant']) // Response-level usage rides the LAST assistant output item (here the - // function_call), not the first — one carrier per response, same row Claude + // function_call), not the first. One carrier per response, same row Claude // uses. @ref LLP 0035#one-carrier assert.equal(projection.messages[1].attributes, undefined) assert.deepEqual(projection.messages[2].attributes, { @@ -431,7 +431,7 @@ test('OpenAI Responses turn-1 response shape matches turn-2 input replay shape ( }), context())) // Turn 1's assistant text + tool_use must match turn 2's replayed input items - // block-for-block — that's what makes content-hash dedupe collapse them. + // block-for-block. That's what makes content-hash dedupe collapse them. assert.deepEqual(turn1.messages[1].content, turn2.messages[1].content) assert.deepEqual(turn1.messages[2].content, turn2.messages[2].content) }) @@ -605,13 +605,13 @@ test('Codex turn metadata + headers project into first-class columns and codex.* // LLP 0032: repo identity promoted to first-class projection fields (still // mirrored in attributes.codex.* for provenance). head_sha carries the raw - // captured value — `abc123` here is abbreviated, so the graph's commitKey + // captured value. `abc123` here is abbreviated, so the graph's commitKey // guard mints no Commit node, but the column stays faithful to capture. assert.equal(projection.git_remote, 'git@github.com:acme/repo.git') assert.equal(projection.head_sha, 'abc123') // repo_root stays null: the workspace path is NOT a verified git toplevel - // (it may be a repo subdir), so Codex File keys must not bridge against it — - // they fall back to absolute. @ref LLP 0032#codex-repo-root + // (it may be a repo subdir), so Codex File keys must not bridge against it. + // They fall back to absolute. @ref LLP 0032#codex-repo-root assert.equal(projection.repo_root, undefined) assert.equal(projection.attributes.codex.thread_id, 'thread-x') diff --git a/test/plugins/codex-git-remote.test.js b/test/plugins/codex-git-remote.test.js index ac4da195..a2ba5653 100644 --- a/test/plugins/codex-git-remote.test.js +++ b/test/plugins/codex-git-remote.test.js @@ -5,7 +5,7 @@ import test from 'node:test' import { redactRemoteUserinfo } from '../../hypaware-core/plugins-workspace/codex/src/git-remote.js' -// @ref LLP 0032#remote-redaction — strip credentials from a remote at ingress. +// @ref LLP 0032#remote-redaction: strip credentials from a remote at ingress. test('strips user:token userinfo from an https remote', () => { assert.equal( diff --git a/test/plugins/context-graph-command.test.js b/test/plugins/context-graph-command.test.js index 94598b4f..7f5208a6 100644 --- a/test/plugins/context-graph-command.test.js +++ b/test/plugins/context-graph-command.test.js @@ -191,7 +191,7 @@ test('an ambiguous seed exits 1 and lists the candidates on stderr', async () => const code = await runGraphNeighbors(['index.js'], ctx) assert.equal(code, 1) const stderr = errs.join('') - assert.match(stderr, /ambiguous seed "index.js" — 2 nodes match by label/) + assert.match(stderr, /ambiguous seed "index.js" - 2 nodes match by label/) // Both colliding Files are listed so the caller can pick the full key. assert.match(stderr, /\/repo\/index\.js/) assert.match(stderr, /\/other\/index\.js/) @@ -216,7 +216,7 @@ test('--limit truncates and reports the true total on stdout (not stderr)', asyn await withGraph(async ({ ctx, out, errs }) => { const code = await runGraphNeighbors(['conv-1', '--direction', 'out', '--limit', '1'], ctx) assert.equal(code, 0) - assert.match(out.join(''), /1 of 4 neighbor\(s\) within 1 hop\(s\) — truncated; raise --limit/) + assert.match(out.join(''), /1 of 4 neighbor\(s\) within 1 hop\(s\) - truncated; raise --limit/) assert.equal(errs.join(''), '', 'truncation is a result, not an error') }) }) @@ -273,7 +273,7 @@ test('TEXT renderer disambiguates two Files sharing a basename into distinct row test('TEXT renderer keeps deep same-suffix Files distinct when the path tail is truncated', () => { // Two checkouts of one tree: same long relative suffix, differing only in the - // root — so a fixed 47-char tail truncation drops the distinguishing prefix. + // root, so a fixed 47-char tail truncation drops the distinguishing prefix. // The rows must still differ (fall back to node_id), not regress to identical. const suffix = '/packages/server/src/components/common/widgets/Button/index.js' const result = { @@ -317,7 +317,7 @@ test('TEXT renderer leaves a non-colliding label readable (no disambiguator)', ( const { stdout } = graphNeighborsVerb.render(/** @type {any} */ (COLLIDING_RESULT), /** @type {any} */ ({ json: false })) const toolRow = String(stdout).split('\n').find((l) => l.includes('Tool')) assert.ok(toolRow, 'tool row present') - assert.match(toolRow, /Bash\s*$/, 'unique label stays bare — no path/id appended') + assert.match(toolRow, /Bash\s*$/, 'unique label stays bare - no path/id appended') }) test('--json output is unchanged by the collision: node.natural_key is the path, labels untouched', () => { diff --git a/test/plugins/context-graph-contract.test.js b/test/plugins/context-graph-contract.test.js index ae61ead0..65fb7df6 100644 --- a/test/plugins/context-graph-contract.test.js +++ b/test/plugins/context-graph-contract.test.js @@ -43,7 +43,7 @@ test('makeRowBuilders normalizes first_seen and prunes empty props to null', () assert.equal(buildNode({ type: 'A', key: 'k', props: {}, firstSeen: TS, sourceKeys: {} }).props, null) }) -test('the id recipe is source-agnostic — same (type, key) converges across sources', () => { +test('the id recipe is source-agnostic - same (type, key) converges across sources', () => { const imsg = makeRowBuilders({ sourceDataset: 'imessage', projector: 'imsg.t0', projectorVersion: 1 }) const aigw = makeRowBuilders({ sourceDataset: 'ai_gateway_messages', projector: 'ai-gateway.t0', projectorVersion: 1 }) const a = imsg.buildNode({ type: 'Actor', key: 'phil', firstSeen: TS, sourceKeys: {} }) @@ -90,7 +90,7 @@ test('contract registry rejects malformed contracts', () => { test('contract registry rejects malformed rules, naming the offending rule index', () => { const reg = createContractRegistry() const ok = { kind: 'node', type: 'T', sql: 'SELECT 1', toRow: () => null } - // A good rule at 0, a bad one at 1 — the error pins which rule and which field, + // A good rule at 0, a bad one at 1: the error pins which rule and which field, // so a connector typo fails at registration with a locatable message rather // than mid-projection (or by silently routing rows into the wrong target map). assert.throws(() => reg.register(sampleContract({ rules: [ok, { ...ok, kind: 'vertex' }] })), /rule 1 kind/) diff --git a/test/plugins/context-graph-enrich-batch.test.js b/test/plugins/context-graph-enrich-batch.test.js index 560daff7..9ce297ee 100644 --- a/test/plugins/context-graph-enrich-batch.test.js +++ b/test/plugins/context-graph-enrich-batch.test.js @@ -247,7 +247,7 @@ test('runCurateBatch resumes a pre-persisted backfill job (collects, does not re const r = await runCurateBatch(runtime, { intervalMs: 1 }) assert.equal(r.batched, true) - assert.equal(batch._submitted.length, 0, 'resume collects the existing job — no second (re-billed) submit') + assert.equal(batch._submitted.length, 0, 'resume collects the existing job - no second (re-billed) submit') assert.equal(r.pending, 1, 'pending recomputed from the scoped pool, not a placeholder 0') assert.equal(r.processed, 1) assert.equal(tables.enrichment_committed.length, 1, 'the resumed job committed its result') diff --git a/test/plugins/context-graph-enrich-config.test.js b/test/plugins/context-graph-enrich-config.test.js index 774807c6..bf5b59fb 100644 --- a/test/plugins/context-graph-enrich-config.test.js +++ b/test/plugins/context-graph-enrich-config.test.js @@ -131,7 +131,7 @@ test('validateEnrichConfig gates the default tool_result filter to the default s assert.equal(dflt.config.require_text, true) // ...but a custom source defaults to no part-type filter (it may lack a - // `part_type` column), while require_text — which only reads text_column — + // `part_type` column), while require_text (which only reads text_column) // stays on. const custom = validateEnrichConfig({ source_dataset: 'my_logs', text_column: 'body' }) assert.equal(custom.ok, true) diff --git a/test/plugins/context-graph-enrich-curate.test.js b/test/plugins/context-graph-enrich-curate.test.js index a8fd873b..a6348ef5 100644 --- a/test/plugins/context-graph-enrich-curate.test.js +++ b/test/plugins/context-graph-enrich-curate.test.js @@ -66,7 +66,7 @@ test('routeDecision deepen also commits an item', () => { assert.equal(r.resolution?.decision, 'deepen') }) -test('routeDecision reject commits nothing — a rejected prospect never reaches the graph', () => { +test('routeDecision reject commits nothing - a rejected prospect never reaches the graph', () => { const r = routeDecision(prospect(), VIEW, { index: 1, decision: 'reject', note: 'noise' }, AT) assert.equal(r.rejected, true) assert.equal(r.committed, null) @@ -98,7 +98,7 @@ test('routeDecision merge writes a committed row under the canonical key with th assert.deepEqual(r.resolution?.committed_ids, ['redis-decision']) }) -test('routeDecision leaves an under-specified merge pending (no commit, no resolution) — avoids mis-routing the produced edge', () => { +test('routeDecision leaves an under-specified merge pending (no commit, no resolution) - avoids mis-routing the produced edge', () => { // merge_into present but item_type missing: the canonical node type is unknown, // so committing would derive the content-addressed id from the PROSPECT's own // type and attach the produced edge to the wrong node. Leave it pending. @@ -194,7 +194,7 @@ function fakeQuery(query, tables) { * Fake EnrichRuntime backed by in-memory tables + injected execSql, a stub * completion returning a fixed `curate_decisions` call, a stub vector search, * and a stub embedder returning identical vectors (so the no-recall remainder - * collapses into ONE cluster — deterministic). `appendRows` mutates `tables`. + * collapses into ONE cluster, deterministic). `appendRows` mutates `tables`. * * @param {{ cfg: EnrichConfig, prospects: Record[], resolutions?: Record[], decisions: Array>, vectorHits?: Array<{ id: string, score: number }>, providerThrows?: boolean }} args */ @@ -298,7 +298,7 @@ test('runCurateTick curates pending prospects, writes committed + resolution row assert.equal(tables.enrichment_resolutions.length, 3, 'p1 + p2 resolutions added to the pre-existing p3') }) -test('runCurateTick merges cross-session duplicates — each contributing session gets a committed row (produced edge)', async () => { +test('runCurateTick merges cross-session duplicates - each contributing session gets a committed row (produced edge)', async () => { // Two sessions propose the same thing; the curator commits one and merges the // other into its key. Both write a committed row under the canonical item_id // (the node dedups by content-addressed id), each carrying its own session @@ -399,7 +399,7 @@ test('runCurateTick leaves a cluster pending (no resolution) when the curator re }) test('runCurateTick derefs the source with the shared content filter (T1/T2 parity)', async () => { - // @ref LLP 0028#row-selection — safeDeref must AND the same content filter as + // @ref LLP 0028#row-selection: safeDeref must AND the same content filter as // the T1 scan into the deref WHERE, so an excluded part (e.g. tool_result) // sharing a message_id with a kept text part is not re-admitted into the // curator excerpt. The fakeQuery ignores WHERE, so assert on the SQL itself. diff --git a/test/plugins/context-graph-enrich-propose.test.js b/test/plugins/context-graph-enrich-propose.test.js index 5fcaae62..5e972724 100644 --- a/test/plugins/context-graph-enrich-propose.test.js +++ b/test/plugins/context-graph-enrich-propose.test.js @@ -34,7 +34,7 @@ function cfg(overrides = {}) { * Source row in the default `ai_gateway_messages` shape (part-level). `ts` is * epoch millis here (the engine surfaces TIMESTAMP as a Date, which the propose * helpers coerce to millis); part_id is the row-unique tiebreak; session_id is - * the anchor (@ref LLP 0030#decision — the Session anchor moved off + * the anchor (@ref LLP 0030#decision: the Session anchor moved off * conversation_id, which is null for Claude). * @param {{ ts: number, part: string, sid: string, msg?: string, text?: string }} r */ @@ -50,7 +50,7 @@ test('buildSessionAggregateQuery ranks the precise latest (ts, tiebreak) per ses assert.match(sql, /SELECT session_id, last_ts, last_id FROM \(/) assert.match(sql, /message_created_at AS last_ts/) assert.match(sql, /part_id AS last_id/) - // ROW_NUMBER over (ts DESC, tiebreak DESC) — a plain MAX(ts) can't pick the + // ROW_NUMBER over (ts DESC, tiebreak DESC): a plain MAX(ts) can't pick the // winning tiebreak among same-ts parts. assert.match(sql, /ROW_NUMBER\(\) OVER \(PARTITION BY session_id ORDER BY message_created_at DESC, part_id DESC\) AS rn/) assert.match(sql, /FROM ai_gateway_messages WHERE/) @@ -74,7 +74,7 @@ test('buildSessionPartsQuery selects all transcript columns for one session, wit assert.match(sql, /WHERE session_id = 'sess-1' AND/) assert.match(sql, /content_text IS NOT NULL AND content_text <> ''/) assert.match(sql, /part_type NOT IN \('tool_result'\)/) - assert.doesNotMatch(sql, /LIMIT/) // full session — the whole point + assert.doesNotMatch(sql, /LIMIT/) // full session. The whole point }) test("buildSessionPartsQuery escapes a single quote in the session id (no injection surface)", () => { @@ -109,7 +109,7 @@ test('orderSessionParts sorts by (timestamp, tiebreak) and coerces Date/ISO time test('buildTranscript stitches ordered text and dedups provenance ids, skipping empties', () => { const ordered = [ srow({ ts: 1, part: 'p1', sid: 'A', msg: 'm1', text: 'one' }), - srow({ ts: 2, part: 'p2', sid: 'A', msg: 'm1', text: '' }), // empty — skipped + srow({ ts: 2, part: 'p2', sid: 'A', msg: 'm1', text: '' }), // empty. Skipped srow({ ts: 3, part: 'p3', sid: 'A', msg: 'm2', text: 'two' }), ] const { text, keys } = buildTranscript(ordered, cfg()) @@ -129,7 +129,7 @@ test('sessionMark returns the latest ordered part tuple', () => { /** * A runtime whose `execSql` returns a fixed aggregate result (one row per - * session: { session_id, last_ts, last_id }) — the shape + * session: { session_id, last_ts, last_id }): the shape * buildSessionAggregateQuery yields (the precise latest part tuple). Lets us * drive selectSessions without a SQL engine. * @param {{ cfg: EnrichConfig, aggregate: Array<{ session_id: string, last_ts: number, last_id?: string }> }} args @@ -146,7 +146,7 @@ const HOUR = 60 * 60_000 test('selectSessions ongoing keeps only settled, past-watermark sessions, oldest first, capped', () => { const aggregate = [ - { session_id: 'fresh', last_ts: NOW - 5 * 60_000, last_id: 'p1' }, // 5m old — not settled + { session_id: 'fresh', last_ts: NOW - 5 * 60_000, last_id: 'p1' }, // 5m old. Not settled { session_id: 'old1', last_ts: NOW - 3 * HOUR, last_id: 'p1' }, // settled { session_id: 'old2', last_ts: NOW - 2 * HOUR, last_id: 'p1' }, // settled { session_id: 'done', last_ts: NOW - 4 * HOUR, last_id: 'p1' }, // settled but already enriched-through @@ -160,10 +160,10 @@ test('selectSessions ongoing keeps only settled, past-watermark sessions, oldest }) test('selectSessions ongoing reselects a same-timestamp session whose latest part advanced past the mark (tiebreak)', async () => { - // Mark {ts, id:'p1'}; the session's latest part is now {ts, id:'p2'} — same + // Mark {ts, id:'p1'}; the session's latest part is now {ts, id:'p2'}. Same // wall-clock millisecond, higher tiebreak (parts of one message share a ts). // The exact (ts, id) compare must reselect it; a timestamp-only check would - // silently drop the new part's text — the defect Codex #1 flagged. + // silently drop the new part's text. The defect Codex #1 flagged. const settledTs = NOW - 3 * HOUR const aggregate = [{ session_id: 'S', last_ts: settledTs, last_id: 'p2' }] const runtime = selectorRuntime({ cfg: cfg(), aggregate }) @@ -230,7 +230,7 @@ test('collectProspectRows keeps the same label under different sessions as disti /** * Minimal SQL stand-in: resolves the first `FROM `, then honors a single - * `= ''` equality (the per-session parts query) — enough for the tick + * `= ''` equality (the per-session parts query): enough for the tick * to read one session's parts and the idempotency filter to read prospects. * * @param {string} query @@ -255,8 +255,8 @@ function fakeQuery(query, tables) { * `appendRows` mutates `tables`, so the cross-tick idempotency filter sees prior * writes. * - * `onComplete` (optional) runs inside `complete()` — i.e. during the tick's - * await window — to simulate a concurrent source mutating the sidecar mid-tick. + * `onComplete` (optional) runs inside `complete()`: i.e. during the tick's + * await window: to simulate a concurrent source mutating the sidecar mid-tick. * * @param {{ cfg: EnrichConfig, stateDir: string, source: Record[], prospects?: Record[], candidates: Array>, onComplete?: () => void | Promise }} args */ @@ -323,7 +323,7 @@ test('runProposeTick extracts a whole session in one call, appends prospects, an } }) -test('runProposeTick is idempotent across ticks — re-extracting the same session appends no duplicate', async () => { +test('runProposeTick is idempotent across ticks: re-extracting the same session appends no duplicate', async () => { const stateDir = tmpStateDir() try { const source = [srow({ ts: 1000, part: 'p1', sid: 'A', msg: 'm1', text: 'use redis' })] diff --git a/test/plugins/context-graph-enrich-state.test.js b/test/plugins/context-graph-enrich-state.test.js index 5793bb27..5afcdda6 100644 --- a/test/plugins/context-graph-enrich-state.test.js +++ b/test/plugins/context-graph-enrich-state.test.js @@ -59,7 +59,7 @@ test('writeState creates the state dir and persists atomically (no leftover temp const dir = path.join(tmpDir(), 'nested', 'state') writeState(dir, { schema_version: 4, session_marks: {}, curate_job: null }) const entries = fs.readdirSync(dir) - assert.deepEqual(entries, [STATE_FILE], 'only the final file remains — temp was renamed') + assert.deepEqual(entries, [STATE_FILE], 'only the final file remains - temp was renamed') }) test('readState falls back to an empty state on malformed JSON', () => { diff --git a/test/plugins/context-graph-ids.test.js b/test/plugins/context-graph-ids.test.js index a8edd7dc..6ddabeaa 100644 --- a/test/plugins/context-graph-ids.test.js +++ b/test/plugins/context-graph-ids.test.js @@ -14,7 +14,7 @@ test('nodeId pins known digests', () => { assert.equal(nodeId('App', 'claude'), '2e9bdd4283a3dfed19a24ddc') // Keys containing spaces must not collide with delimiter boundaries. This is // ALSO an out-of-repo File path: the LLP 0032 File re-key only bridges paths - // INSIDE a captured repo, so an absolute /tmp path keeps its key — this pin is + // INSIDE a captured repo, so an absolute /tmp path keeps its key, and this pin is // deliberately unchanged by the migration. assert.equal(nodeId('File', '/tmp/a b.txt'), 'f089fa67a6b72ea65ff004f9') }) diff --git a/test/plugins/context-graph-maintenance.test.js b/test/plugins/context-graph-maintenance.test.js index f2de042b..54692318 100644 --- a/test/plugins/context-graph-maintenance.test.js +++ b/test/plugins/context-graph-maintenance.test.js @@ -143,7 +143,7 @@ test('compactGraphTables refuses to touch a partition whose cursor is corrupt', const report = await compactGraphTables({ storage: /** @type {any} */ ({ cacheRoot }) }) const nodeReport = report.datasets.find((d) => d.dataset === 'node') - // Partition b was never scanned, so the duplicate is invisible — but + // Partition b was never scanned, so the duplicate is invisible. But // crucially nothing was rewritten or retired from the stale 'table' // dir a synthesized epoch-0 cursor would have pointed at. assert.deepEqual(nodeReport?.partitionsSkipped, [{ path: partB, reason: 'unreadable-cursor' }]) diff --git a/test/plugins/context-graph-project-e2e.test.js b/test/plugins/context-graph-project-e2e.test.js index 1c102595..cd5348ab 100644 --- a/test/plugins/context-graph-project-e2e.test.js +++ b/test/plugins/context-graph-project-e2e.test.js @@ -25,7 +25,7 @@ import { createAiGatewayGraphContract } from '../../hypaware-core/plugins-worksp // Task 3 (the connector's contract), and Task 1 (the kit) together, and is the // automated regression that the relocation changed nothing observable. -// @ref LLP 0030#decision — the ai-gateway contract keys the Session on +// @ref LLP 0030#decision: the ai-gateway contract keys the Session on // session_id now (conversation_id is null for Claude). The fixture carries // both columns: session_id for the contract under test, conversation_id for // the version-bump regression's bespoke session-only contract below. @@ -126,7 +126,7 @@ test('projectGraph excludes retained Claude aux rows from the graph', async () = // The gateway now retains harness aux exchanges (tagged // attributes.claude.aux_kind) instead of dropping them (LLP 0026). They // must not mint graph nodes/edges. Seed the same two real rows plus one - // aux row whose keys are all distinct — counts must stay 5/4. + // aux row whose keys are all distinct. Counts must stay 5/4. await withSeededGateway(async ({ registry, storage }) => { const contract = createAiGatewayGraphContract({ nodeId, edgeId, makeRowBuilders }) const r = await projectGraph({ query: registry, storage, contracts: [contract] }) @@ -180,14 +180,14 @@ function sessionContract(version) { } } -test('bumping projectorVersion does not re-project — committed rows keep their original version', async () => { +test('bumping projectorVersion does not re-project: committed rows keep their original version', async () => { await withSeededGateway(async ({ registry, storage }) => { const v1 = await projectGraph({ query: registry, storage, contracts: [sessionContract(1)] }) assert.equal(v1.nodesWritten, 1, 'one Session node committed at v1') // Same source, same content-addressed ids, projectorVersion bumped to 2. const v2 = await projectGraph({ query: registry, storage, contracts: [sessionContract(2)] }) - assert.equal(v2.nodesWritten, 0, 'a version bump alone rewrites nothing — pre-write dedup skips the committed id') + assert.equal(v2.nodesWritten, 0, 'a version bump alone rewrites nothing - pre-write dedup skips the committed id') // Exactly one Session row survives, still stamped v1: bumping the version // did not re-derive it. (Asserting on the single Session row rather than by diff --git a/test/plugins/context-graph-query.test.js b/test/plugins/context-graph-query.test.js index b37e030c..f9b0eaf7 100644 --- a/test/plugins/context-graph-query.test.js +++ b/test/plugins/context-graph-query.test.js @@ -31,7 +31,7 @@ function e(src_id, dst_id, edge_type) { return { src_id, dst_id, edge_type } } -// A small Session-rooted activity graph. f2 is isolated (no edges) — it exists +// A small Session-rooted activity graph. f2 is isolated (no edges). It exists // only to collide on the `index.js` basename for the ambiguity test. const NODES = [ n('s1', 'Session', 'conv-1', null), @@ -196,7 +196,7 @@ test('queryNeighbors folds pre-compaction duplicate rows so a natural-key seed s registry.registerDataset(graphDatasetRegistration('edge')) // The same Session/Tool/edge committed twice in different source= - // partitions — what a concurrent projection lands before `hyp graph + // partitions: what a concurrent projection lands before `hyp graph // compact` runs. Without the identity fold, the duplicate node rows make // the natural-key seed read as "ambiguous" and the doubled edge inflates // the walk. diff --git a/test/plugins/format-parquet-clustering.test.js b/test/plugins/format-parquet-clustering.test.js index 8671d608..5c1fca4b 100644 --- a/test/plugins/format-parquet-clustering.test.js +++ b/test/plugins/format-parquet-clustering.test.js @@ -34,7 +34,7 @@ const COLUMNS = [ /** * Conversation-contiguous rows (the order `readRows` yields). Each * conversation repeats one wide `tools` blob; the blob differs per - * conversation, so the column has `nConvs` distinct values total — enough + * conversation, so the column has `nConvs` distinct values total: enough * distinct ~20 KB blobs to exceed hyparquet-writer's 1 MiB dictionary cap * when they all land in one row group. * @@ -118,7 +118,7 @@ test('with clustering, the wide column stays dictionary-encoded and the file shr `expected tools to be dictionary-encoded under clustering, got ${[...tools].join(',')}` ) - // Same rows, same codec — the only difference is row-group clustering, which + // Same rows, same codec: the only difference is row-group clustering, which // keeps the repeated blob stored once per group instead of once per row. assert.ok( clustered.bytesWritten * 3 < plain.bytesWritten, @@ -159,7 +159,7 @@ test('a fat row flushes the group before it is added, so no group overshoots the // one fits. The byte check must run *before* the row is added: otherwise the // group accumulates one row, fails the `groupBytes >= cap` test (it is still // under), then takes a second fat row to ~36 MB before flushing on the next - // iteration — overshooting the heap bound by a whole row. With the pre-add + // iteration, overshooting the heap bound by a whole row. With the pre-add // check, every fat row lands in its own row group. const encoder = await makeEncoder() const ROWS = 4 @@ -193,12 +193,12 @@ test('a fat row flushes the group before it is added, so no group overshoots the test('JSON object columns are interned so they dictionary-encode AND round-trip as objects', async () => { // The cache stores JSON columns as Iceberg `variant`, so the reader hands - // them back as parsed objects — a fresh object reference per row even when + // them back as parsed objects: a fresh object reference per row even when // the content repeats. The writer keys its dictionary by reference, so // without help it sees every row as distinct and bails to PLAIN (this is // what kept the real `tools` column at >1 GB even with clustering active). // Interning identical-content objects to one shared reference lets them - // dictionary-encode WITHOUT stringifying — so the JSON logical type still + // dictionary-encode WITHOUT stringifying, so the JSON logical type still // round-trips the original object to readers (no double-encoding). const encoder = await makeEncoder() const cols = [ @@ -209,7 +209,7 @@ test('JSON object columns are interned so they dictionary-encode AND round-trip async function* rows() { for (let c = 0; c < 40; c++) { const conversation_id = `conv-${c}` - // Fresh object reference each row, identical content — mirrors the reader. + // Fresh object reference each row, identical content: mirrors the reader. for (let r = 0; r < 10; r++) yield { conversation_id, tools: content(conversation_id) } } } diff --git a/test/plugins/iceberg-commit.test.js b/test/plugins/iceberg-commit.test.js index 4a277a60..30c0eda3 100644 --- a/test/plugins/iceberg-commit.test.js +++ b/test/plugins/iceberg-commit.test.js @@ -25,7 +25,7 @@ import { createLocalFsBlobStore } from '../../hypaware-core/plugins-workspace/lo /** * Build a real `@hypaware/local-fs` BlobStore over a fresh temp dir. * The commit module runs through icebird, which needs real bytes on - * disk — an in-memory shim doesn't exercise the metadata read/write + * disk: an in-memory shim doesn't exercise the metadata read/write * cycle, so the test pins the contract by writing to disk. * * @returns {Promise<{ blobStore: BlobStore, baseDir: string, cleanup: () => Promise }>} @@ -143,8 +143,8 @@ test('commitBatch retries past a transient metadata precondition collision', asy // Wrap the underlying blob-store so the FIRST conditional metadata // put surfaces blob_precondition_failed (a 412 the writer maps to - // iceberg_commit_conflict). The retry attempt is allowed through — - // icebird should reload metadata, re-stage, and succeed. + // iceberg_commit_conflict). The retry attempt is allowed through. + // Icebird should reload metadata, re-stage, and succeed. /** @type {{ key: string, ifNoneMatch?: string }[]} */ const puts = [] let firstConditionalMetadataAttempt = true @@ -392,7 +392,7 @@ test('probeTable propagates transient metadata read failures instead of masking // to fetch it. Raise a non-fatal transient error: not in the // FATAL_KINDS set, no `code='ENOENT'`. The reader will wrap // this as iceberg_metadata_read_failed without an ENOENT - // marker — exactly the shape the previous probeTable masked. + // marker, exactly the shape the previous probeTable masked. const err = /** @type {Error & { errorKind?: string }} */ ( new Error(`simulated transient read failure for ${input.key}`) ) diff --git a/test/plugins/iceberg-maintenance.test.js b/test/plugins/iceberg-maintenance.test.js index 0328f65d..07172ee3 100644 --- a/test/plugins/iceberg-maintenance.test.js +++ b/test/plugins/iceberg-maintenance.test.js @@ -248,7 +248,7 @@ test('compactExportTable cleans up partial output when staging fails mid-flight' const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'hyp-maint-')) const { tableUrl, resolver, lister, rows } = await createTwoFileTable(dir) - // Fail the manifest write — by then the consolidated data file(s) + // Fail the manifest write, by which point the consolidated data file(s) // already landed, so without tracked cleanup they would leak (icebird // only reports writtenFiles on a StagedUpdate that completed). /** @type {string[]} */ diff --git a/test/plugins/iceberg-partitioning.test.js b/test/plugins/iceberg-partitioning.test.js index 03292200..5910271e 100644 --- a/test/plugins/iceberg-partitioning.test.js +++ b/test/plugins/iceberg-partitioning.test.js @@ -89,7 +89,7 @@ test('derivePartitioning builds day(primaryTimestampColumn) + conversation sort test('derivePartitioning day grain is independent of cachePartitioning (does not inherit conversation_id partitioning)', () => { const p = derivePartitioning(AI_GATEWAY_REG, AI_GATEWAY_COLUMNS) assert.ok(p) - // The partition axis must be the day grain only — NOT the cache's + // The partition axis must be the day grain only: NOT the cache's // conversation_id/cwd/date identity partitioning. assert.deepEqual(p.partitionSpec.fields.map((f) => f.name), ['message_created_at']) }) diff --git a/test/plugins/iceberg-plugin.test.js b/test/plugins/iceberg-plugin.test.js index 4eebc1ff..d085a9f9 100644 --- a/test/plugins/iceberg-plugin.test.js +++ b/test/plugins/iceberg-plugin.test.js @@ -55,7 +55,7 @@ test('discoverBundledPlugins surfaces format-iceberg from the workspace', async assert.ok(names.includes('@hypaware/format-iceberg'), `expected loaded names to include @hypaware/format-iceberg, got ${names.join(', ')}`) // The unknown-directory bin must NOT contain our plugin even when the - // allowlist or excludeSet changes — the discovery scan goes through + // allowlist or excludeSet changes, the discovery scan goes through // the allowlist branch. assert.ok( !result.unknownDirs.some((dir) => dir.endsWith('format-iceberg')), diff --git a/test/plugins/iceberg-s3-errors.test.js b/test/plugins/iceberg-s3-errors.test.js index 6aa9a0cf..3278627c 100644 --- a/test/plugins/iceberg-s3-errors.test.js +++ b/test/plugins/iceberg-s3-errors.test.js @@ -167,6 +167,6 @@ test('writer onWrite observer that throws does not break the commit', async () = if (!resolver.writer) throw new Error('writer required') const writer = resolver.writer(`blob://${KEY}`, { ifNoneMatch: '*' }) writer.appendBytes(new Uint8Array([1])) - // Must not throw — observer failures are best-effort. + // Must not throw: observer failures are best-effort. await writer.finish() }) diff --git a/test/plugins/iceberg-state.test.js b/test/plugins/iceberg-state.test.js index bc309a4c..bc76b1c7 100644 --- a/test/plugins/iceberg-state.test.js +++ b/test/plugins/iceberg-state.test.js @@ -143,7 +143,7 @@ test('markerSubsumedBySnapshot accepts a probe-state object and resolves equalit assert.equal( markerSubsumedBySnapshot(markerWithSnapshot('100'), { currentSnapshotId: '200', metadata: null }), false, - 'without metadata, only equality counts — ancestry cannot be proven' + 'without metadata, only equality counts - ancestry cannot be proven' ) }) diff --git a/test/plugins/s3-blob-store.test.js b/test/plugins/s3-blob-store.test.js index 3524da51..5238b1b0 100644 --- a/test/plugins/s3-blob-store.test.js +++ b/test/plugins/s3-blob-store.test.js @@ -119,7 +119,7 @@ test('s3 BlobStore listObjects strips the prefix from emitted keys', async () => assert.deepEqual(seen.sort(), ['a.bin', 'sub/b.bin']) // The fake captured the composed-with-prefix Prefix argument so the - // BlobStore-level prefix actually reached S3 — and it MUST carry a + // BlobStore-level prefix actually reached S3, and it MUST carry a // trailing slash so a sibling namespace like `hyp/exports2/...` does // not match as a string prefix. const listCall = client.calls.find((c) => c.command === 'listObjects') @@ -136,13 +136,13 @@ test('s3 BlobStore listObjects does not leak into sibling-namespace keys (hyp/ex // cannot touch out-of-scope objects. const client = makeFakeS3Client() // Pre-populate the fake bucket directly to bypass composeKey's path - // safety check — we want sibling-namespace keys to exist in the + // safety check: we want sibling-namespace keys to exist in the // backing store so the test can confirm they are NOT surfaced. client.objects.set('hyp/exports/datasets/foo.parquet', { bytes: new Uint8Array([1]), lastModified: new Date(0) }) client.objects.set('hyp/exports/datasets/sub/bar.parquet', { bytes: new Uint8Array([2]), lastModified: new Date(0) }) - // Sibling namespace — must never be reported through this BlobStore. + // Sibling namespace: must never be reported through this BlobStore. client.objects.set('hyp/exports2/datasets/leak.parquet', { bytes: new Uint8Array([9]), lastModified: new Date(0) }) client.objects.set('hyp/exports-other/leak2.parquet', @@ -190,7 +190,7 @@ test('s3 BlobStore listObjects empty-prefix without a configured prefix lists th assert.deepEqual(seen.sort(), ['a.bin', 'nested/b.bin']) const listCall = client.calls.find((c) => c.command === 'listObjects') assert.ok(listCall) - // No Prefix passed to S3 — entire bucket is in scope by design. + // No Prefix passed to S3: entire bucket is in scope by design. assert.equal(listCall.input.Prefix, undefined) }) diff --git a/test/plugins/s3-export-batch.test.js b/test/plugins/s3-export-batch.test.js index b68e5136..73fb18f8 100644 --- a/test/plugins/s3-export-batch.test.js +++ b/test/plugins/s3-export-batch.test.js @@ -108,7 +108,7 @@ test('exportBatch partial failure: retryPartitions has only the failed partition async putObject() { call += 1 if (call === 1) return {} // p1 succeeds - // Non-terminal error — driver expects to retry just p2. + // Non-terminal error. Driver expects to retry just p2. const err = Object.assign(new Error('slow down'), { name: 'SlowDown' }) throw err }, @@ -133,7 +133,7 @@ test('exportBatch partial failure: retryPartitions has only the failed partition test('exportBatch forwards dataset cluster columns to the encoder', async () => { // The s3 sink must derive cluster columns from the dataset's Iceberg - // partition fields and pass them to the encoder — same as local-fs — so the + // partition fields and pass them to the encoder (same as local-fs), so the // Parquet encoder keeps wide repeated columns dictionary-encoded. /** @type {any} */ let registered diff --git a/test/plugins/s3-keys.test.js b/test/plugins/s3-keys.test.js index 49b1a5b0..8ca5c242 100644 --- a/test/plugins/s3-keys.test.js +++ b/test/plugins/s3-keys.test.js @@ -59,7 +59,7 @@ test('renderObjectKey strips path separators from dataset and filename so the ke partition: { dataset: '../escape', partition: {} }, filename: '../../etc/passwd', }) - // Exactly three separators (prefix / dataset / segment / filename) — + // Exactly three separators (prefix / dataset / segment / filename), so // the inputs cannot inject any more. assert.equal((key.match(/\//g) ?? []).length, 3) assert.equal(key.startsWith('acme/'), true) diff --git a/test/plugins/s3-query-activate.test.js b/test/plugins/s3-query-activate.test.js index 65bdfbba..b66f92fe 100644 --- a/test/plugins/s3-query-activate.test.js +++ b/test/plugins/s3-query-activate.test.js @@ -9,8 +9,8 @@ import { activate } from '../../hypaware-core/plugins-workspace/s3/src/index.js' * Build a stub `PluginActivationContext` capturing the datasets the * plugin registers and recording every `listObjects` call the injected * S3 client sees. The recorded `Bucket`/`Prefix` reveal how - * `buildQuerySourceBlobStore` rooted each query source — the - * Iceberg-critical `(bucket, root-prefix)` split — without reaching into + * `buildQuerySourceBlobStore` rooted each query source: the + * Iceberg-critical `(bucket, root-prefix)` split, without reaching into * the BlobStore internals. * * @param {Record} config diff --git a/test/plugins/s3-query-dataset.test.js b/test/plugins/s3-query-dataset.test.js index 5db4852d..96711aa8 100644 --- a/test/plugins/s3-query-dataset.test.js +++ b/test/plugins/s3-query-dataset.test.js @@ -187,7 +187,7 @@ test('parquet discovery bounds the prefix to a directory, excluding sibling name }) test('parquet read rejects when a listed object disappears before read (list→read race)', async () => { - // listObjects advertises a key, but getObject returns null — the + // listObjects advertises a key, but getObject returns null: the // real list→read race. The scan must reject, not silently drop rows. /** @type {BlobStore} */ const racyBlobStore = /** @type {BlobStore} */ ({