Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
42 changes: 21 additions & 21 deletions .claude/skills/ref-check/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,30 @@ 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 <path>` 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 <path>`: 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:

- `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

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
12 changes: 8 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion bin/hypaware.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <flow>`, which goes through the dispatcher and spawns us
// here with a clean process state.
if (argv[0] === '__smoke_internal') {
Expand Down
34 changes: 17 additions & 17 deletions hypaware-core/plugins-workspace/ai-gateway-graph/src/graph-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -21,23 +21,23 @@ 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
* receives `owner/repo` and repo-relative paths straight from the API): turning
* 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
// ---------------------------------------------------------------------------

/**
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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({
Expand All @@ -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.
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions hypaware-core/plugins-workspace/ai-gateway-graph/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion hypaware-core/plugins-workspace/ai-gateway/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading