Type: Spec Status: Accepted Systems: Sources, Gateway, CLI Author: Phil / Claude Date: 2026-06-29 Related: LLP 0000, LLP 0009, LLP 0012, LLP 0016, LLP 0050, LLP 0051, LLP 0083
A folder-scoped data-usage policy for HypAware capture. A
.hypignorefile (gitignore-style, ancestor-walked) maps a directory subtree to a usage class. V1 ships exactly one class:ignore(never recorded). Managed by ahyp ignore/hyp unignoreCLI. The where of enforcement is LLP 0050; deferred classes (local-only) and the ephemeral session opt-out are LLP 0051.
Today the @hypaware/claude hypaware-ignore / hypaware-unignore skills
describe two opt-out mechanisms, a per-session in-memory drop
(POST /_hypaware/ignore/session) and a committable .hypignore ancestor file,
but neither is implemented: the endpoint route does not exist and no code
reads a .hypignore. Users have no working way to tell HypAware "do not record
work done in this directory."
This spec defines the missing mechanism, and deliberately frames it as more than
a binary toggle: a .hypignore declares how data from a directory subtree is
used, so the same file format can carry richer classes (e.g. record-locally-but-
never-export) later without a repaint.
The mechanism is a single function: resolve a scope to a usage class.
- A scope is a directory subtree.
- A class says how exchanges originating in that subtree are used.
- Everything else (the file format, the CLI, the matcher) is authored once; only enforcement differs per class.
This unifies "a .hypignore" and "a local-only flag" into one feature with one
authoring surface and a small, extensible class set, rather than two unrelated
features that happen to ship together.
A captured exchange is matched to a scope by its cwd (and, equivalently,
its repo_root). HypAware already associates these with every Claude/Codex
exchange: the client hook records cwd/repo_root/git_remote into
session-context.jsonl and the exchange projector stamps them onto each row
(claude/src/projector.js).
Resolution is gitignore-style ancestor walk: from the exchange's cwd, walk
up the directory tree; the nearest ancestor containing a .hypignore
governs. Because V1 has only ignore and no "un-ignore" directive
(see File format), this collapses to a simple rule:
any
.hypignorefound walking up from an exchange'scwd⇒ the exchange is ignored.
repo_root is the natural place to drop one file to cover a whole repo, but the
mechanism is not repo-bound: a .hypignore anywhere in the ancestor chain
(including outside any git repo) governs its subtree.
Extended-by: LLP 0050 §canonicalization:
"the ancestor chain" is over real paths, not lexical ones. A cwd is
matched over the set of spellings that denote it (as-given plus
symlink-resolved), and the most restrictive verdict any spelling produces wins,
so a .hypignore cannot be escaped by reaching its subtree through a symlink.
Extended-by:
LLP 0050 §normalization:
symlinks are not the only way a filesystem spells one directory several ways.
The ancestor walk is unaffected by the rest (it stats each candidate rather
than comparing two strings), but the machine-local list's membership test
compares a cwd a client reported against a dir a CLI declared, so each of
the spellings above is compared in a folded form (Unicode-normalized, and
case-folded on a volume probed case-insensitive). Like canonicalization, the
fold can only ever make the verdict more restrictive.
| Class | V1 | Meaning |
|---|---|---|
ignore |
shipped | Exchange is never written to the cache. |
local-only |
reserved | Recorded to the local cache, never exported/forwarded. See LLP 0051. |
full |
implicit default | No .hypignore governs → recorded and eligible for export. |
The class set is extensible: adding local-only (or future classes like
redact) is additive to this spec and the file format, not a rewrite.
Extended-by: LLP 0103 -
the ignore class gains a second authoring source, an entry in the
machine-local list (private, per-machine, no repo dotfile), and an explicit
full ("asked; syncs") entry becomes representable there. The .hypignore
dotfile semantics in this spec are unchanged.
A .hypignore is a small text file:
#comments and blank lines are ignored.- An optional class token on its own line names the class. V1 recognizes
ignore. - An empty or comment-only file means
ignore: preserving the existing skill notes' promise that "an empty.hypignoreat the top of the repo" opts the tree out. - Reserved, parsed-but-not-invented in V1: in-file path patterns
(scope-narrowing within a subtree, e.g. ignore only
secrets/) and additional class tokens. V1 ignores anything beyond a recognized class token.
If a .hypignore names a class the running version does not implement (most
importantly local-only before it ships, but any unknown token), the file
resolves to ignore, the most restrictive class, and a warning is logged.
This is a privacy invariant, not a convenience: the safe failure for a privacy control is "suppress more," never "record-and-export something the user flagged." A corollary the design relies on:
Upgrading HypAware can only ever expose less than before for a given
.hypignore. Whenlocal-onlyships, a file that saidlocal-onlymoves from fully-suppressed to locally-recorded, a loosening the user already asked for in writing, never the reverse.
The class determines where it is enforced, riding HypAware's core seam: "sources write only to the cache; the export pipeline reads the cache and pushes to sinks" (LLP 0000):
ignore→ capture seam. The row never enters the cache.local-only→ export seam (future): the row enters the cache but the export driver skips it.
V1 implements only the capture seam, so it touches no cache schema, export driver, LLP 0029, or LLP 0030. The mechanics of capture-seam enforcement (which plugin, how the drop happens) are LLP 0050.
A kernel verb (LLP 0009), since hand-authoring a dotfile should not be the only path:
hyp ignore [path]: write a self-documenting.hypignore(comment header +ignoretoken) at the git repo root ifpath/cwd is in a repo, else at cwd. An explicitpathoverrides.hyp unignore [path]: remove the governing.hypignore.hyp ignore --check [path]: report whether a path is currently ignored, which.hypignoregoverns, and how many already-cached rows from the scope remain (see prospective-only). Keeps the rule debuggable, per the repo's log-driven ethos.
- Raw-proxy / OTEL sources are folder-blind. Folder matching needs a
cwd, which only the Claude/Codex pathways supply; theraw-anthropic/raw-openaiproxy (LLP 0012) and OTEL receiver have none, so a folder rule is a no-op for them. This is structural, not a policy choice: see LLP 0050. A future caller-supplied scope (e.g. anX-Hyp-Cwdheader) is not precluded. Extended-by: LLP 0083, the ChatGPT-subscription Codex route (provider='chatgpt') is not in this folder-blind set: it is a first-class adapter pathway whosecwdis recoverable from the local session rollout, so the Codex live projector now enriches it and R1 coverage is client-independent for Codex. - Prospective-only; no purge. {#prospective-only}
.hypignoregates future live recording and future backfills. Rows already in the cache from before the file existed are left untouched; retroactive deletion is a separate, destructive capability out of V1 scope.--checksurfaces the residual count. Extended-by: LLP 0104 - the deferred destructive capability ships as a standalonehyp purgeverb; the marking verbs here stay non-destructive, exactly as this non-goal holds. - No central/config interaction. A
.hypignoreis a local repo dotfile, honored whenever found. It is not merged with layered config (LLP 0031) or pushed by central (LLP 0036). Org-forced policy is a future concern tied tolocal-only. - Ephemeral per-session opt-out is a separate mechanism, not part of this
folder spec. It is specced in LLP 0066
(session-scoped, in-memory, keyed on
session_id), promoted from the deferred sketch in LLP 0051.
- R1. An exchange whose resolved
cwdhas any ancestor.hypignoreresolving toignoreMUST NOT be written to the cache, for both live capture and backfill. (Extended-by: LLP 0085, whencwdwas unknown at capture (the Claude session-start race projected the row withcwd = null), the guarantee is honored at the capture seam or by a flush-time settlement-drop, before partition write and before export. The literal "never written to the cache" relaxes to "never persisted past flush or forwarded" for that race case; a fail-closed hold is rejected because it would drop legitimate SDK/headless traffic that never gets a hook record.) - R2.
ignoreMUST NOT alter the live LLM call: the gateway is pass-through; only persistence is suppressed. - R3. A
.hypignorenaming an unimplemented class MUST resolve toignore(the fail-safe) and SHOULD warn. - R4. Matching MUST be performed by a single shared resolver, not reimplemented per pathway (see LLP 0050).
- R5.
hyp ignore/hyp unignoreMUST be idempotent: ignoring an already-ignored path or unignoring an unignored path succeeds without error. - R6. Resolution MUST NOT add unbounded filesystem work to the capture hot path: the resolver caches per-cwd results.