| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 2 | 3 | 0% |
| Commands | 0 | 4 | 0 | 0% |
| Section tags | 0 | 3 | 1 | 0% |
What each file covers
Sections
0 shared · 2 only in A · 3 only in B- − CLAUDE.md
- − Claude Code specifics
- + The trust boundary
- + Rules
- + Changing this area
Commands
0 shared · 4 only in A · 0 only in B- − pnpm -F @claudinho/mcp build
- − pnpm -r typecheck
- − pnpm lint
- − gh run watch <id> --exit-status
Section tags
0 shared · 3 only in A · 1 only in B- − lint-format
- − performance
- − agent-behaviour
- + do-not
Line diff
arturogarrido/claudinho · CLAUDE.md
@@ −1 @@
1# CLAUDE.md
2
3This project uses **AGENTS.md** as the primary agent guide. Read it first:
4
5@AGENTS.md
6
7## Claude Code specifics
8
9- The statusline command must return in **<150ms** and **never** hit the network on the hot path — read from the local micro-cache.
10- Local MCP dev loop:
11 ```bash
12 pnpm -F @claudinho/mcp build
13 claude mcp add claudinho-dev -- node packages/mcp/dist/index.js
14 ```
15- When changing shared types, update `@claudinho/core` and run `pnpm -r typecheck` before committing.
16- Run `pnpm lint` (Biome) before committing; CI gates on it. The setup is lint-only (no formatter) — keep style consistent with the surrounding code.
17- **Before declaring any change "done," run the "Pre-PR self-review" rubric in `AGENTS.md`** — verify external API shapes against a *real* response (fixtures included); apply the change to every surface (CLI text **and** `--json`, MCP `data` **and** text, READMEs); audit against the Hard Constraints (existing code too); do an adversarial failure-mode pass (fail-closed; never cache transient errors); and bound default-on latency. For money/legal/external-API changes, do an independent reviewer pass and self-classify findings **P1/P2/P3**.
18- **After any push to a branch with CI, always watch the run and confirm it's green** (`gh run watch <id> --exit-status`); report the per-job result. Don't consider a push "done" until CI passes.
19
arturogarrido/claudinho · .cursor/rules/trust-boundary.mdc
@@ +1 @@
1---
2description: Where untrusted input becomes a domain type, and the rules that hold there
3globs: packages/core/src/trust/**,packages/core/src/adapters/**,packages/core/src/markets/**,packages/cli/src/statusline.ts,packages/cli/src/marketCache.ts
4alwaysApply: false
5---
6
7# The trust boundary
8
9Feed responses and local cache files are untrusted input. They enter through
10`packages/core/src/trust/` and leave as a domain type or as a stated reason they
11could not. Nothing else builds a `Match` or a `MarketSignal` from raw input.
12
13## Rules
14
15- **An adapter FETCHES; it does not interpret.** Parsing lives in `trust/`
16 (`trust/espn.ts` is the model). The adapter that also parsed is how `id` and
17 `kickoff` ended up copied verbatim while every string beside them was cleaned.
18- **Record refusal is local, not a provider outage.** `ProviderAdapter` returns
19 plain arrays. Omit malformed, duplicate, or truncated records while keeping
20 readable siblings usable and attributed to the provider. Throw into the
21 degraded path only for transport/JSON failure, or an incomplete payload with
22 no usable records. Parser-local `BoundedList.complete` is diagnostic; never
23 promote one refused record into a batch-wide blackout.
24- **Both paths end at the same constructor.** Live and cache reads call
25 `sealMatch` / `sealMarketSignal`. When they had separate rules, each fix landed
26 on one of them; `core/test/trust-parity.test.ts` asserts they agree, as a JSON
27 round trip, because that is literally what the cache file is.
28- **Text has roles.** `humanLabel` is prose — no controls, no format characters,
29 **no emoji**, bounded by display columns *and* code points. `opaqueId` is
30 checked against an exact grammar. `canonicalTimestamp` re-emits one form and
31 refuses a date that does not exist. `productFlag` GENERATES the flag from the
32 nation.
33- **Never accept a product glyph from input.** While flags travelled through the
34 text filter it needed an emoji carve-out, and a carve-out without a grammar is
35 a covert channel: TAG characters, variation selectors and ZWJ each rode through
36 it. A `🏴` plus 42 tag characters is ONE two-column glyph spelling a full
37 instruction sentence — invisible on a terminal, legible to a model reading
38 `--json`.
39- **Say which kind of failure it is.** `ParseResult` = `valid` /
40 `definitive-none` / `malformed` / `ambiguous` / `unresolved`. For per-item
41 market resolution, `valid`, `definitive-none` and `ambiguous` are stable and
42 cacheable; `malformed` and `unresolved` must not become definitive negatives.
43 A successful provider batch with one malformed sibling is still shared for
44 the coalescing TTL: identical bytes will parse identically, and immediate
45 refetches only waste egress and starve the readable records.
46- **Enrichment completeness reaches the renderer.** Market cache/fetch helpers
47 return `{ signals, complete }`; default-on annotations, dedicated tools,
48 share cards, and structured output retain it. Only a complete empty batch may
49 say "no signal"; an incomplete one warns and carries `marketComplete:false`
50 or `complete:false`.
51- **Bound the work, not just the output.** `takeBounded` slices *before* the
52 per-record map. A surface reports `total`/`shown`/`truncated` from one
53 `BoundedList`; no handler recomputes a count. `total` is exact only when
54 `complete` is true, so incomplete hot-path scans use a nonnumeric `more`
55 marker rather than guessing how many valid records remain. MCP response
56 limiting preflights depth, width, entries, containers, and aggregate text
57 before serialization or recursive shrinking; a byte cap alone does not bound
58 the CPU and allocation spent discovering that a payload is too large.
59- **Fail closed on ABSENCE.** A missing field must be at least as rejecting as a
60 wrong one. Five market gates were `x != null && ...`, so the more malformed
61 payload was the more successful one.
62
63## Changing this area
64
65Add the property to `core/test/trust-properties.test.ts` (table-driven off each
66type's declared key list, so a new field fails by default), and **verify it goes
67red** with the rule reverted — a property test you have not made fail is pinning
68nothing. If it is a claim `SECURITY.md` makes, cite the test there;
69`core/test/security-claims.test.ts` fails if a citation rots.
70
@@ −1 +1 @@
1−# CLAUDE.md
1+---
2+description: Where untrusted input becomes a domain type, and the rules that hold there
3+globs: packages/core/src/trust/**,packages/core/src/adapters/**,packages/core/src/markets/**,packages/cli/src/statusline.ts,packages/cli/src/marketCache.ts
4+alwaysApply: false
5+---
26
3−This project uses **AGENTS.md** as the primary agent guide. Read it first:
7+# The trust boundary
48
5−@AGENTS.md
9+Feed responses and local cache files are untrusted input. They enter through
10+`packages/core/src/trust/` and leave as a domain type or as a stated reason they
11+could not. Nothing else builds a `Match` or a `MarketSignal` from raw input.
612
7−## Claude Code specifics
13+## Rules
814
9−- The statusline command must return in **<150ms** and **never** hit the network on the hot path — read from the local micro-cache.
10−- Local MCP dev loop:
11− ```bash
12− pnpm -F @claudinho/mcp build
13− claude mcp add claudinho-dev -- node packages/mcp/dist/index.js
14− ```
15−- When changing shared types, update `@claudinho/core` and run `pnpm -r typecheck` before committing.
16−- Run `pnpm lint` (Biome) before committing; CI gates on it. The setup is lint-only (no formatter) — keep style consistent with the surrounding code.
17−- **Before declaring any change "done," run the "Pre-PR self-review" rubric in `AGENTS.md`** — verify external API shapes against a *real* response (fixtures included); apply the change to every surface (CLI text **and** `--json`, MCP `data` **and** text, READMEs); audit against the Hard Constraints (existing code too); do an adversarial failure-mode pass (fail-closed; never cache transient errors); and bound default-on latency. For money/legal/external-API changes, do an independent reviewer pass and self-classify findings **P1/P2/P3**.
18−- **After any push to a branch with CI, always watch the run and confirm it's green** (`gh run watch <id> --exit-status`); report the per-job result. Don't consider a push "done" until CI passes.
15+- **An adapter FETCHES; it does not interpret.** Parsing lives in `trust/`
16+ (`trust/espn.ts` is the model). The adapter that also parsed is how `id` and
17+ `kickoff` ended up copied verbatim while every string beside them was cleaned.
18+- **Record refusal is local, not a provider outage.** `ProviderAdapter` returns
19+ plain arrays. Omit malformed, duplicate, or truncated records while keeping
20+ readable siblings usable and attributed to the provider. Throw into the
21+ degraded path only for transport/JSON failure, or an incomplete payload with
22+ no usable records. Parser-local `BoundedList.complete` is diagnostic; never
23+ promote one refused record into a batch-wide blackout.
24+- **Both paths end at the same constructor.** Live and cache reads call
25+ `sealMatch` / `sealMarketSignal`. When they had separate rules, each fix landed
26+ on one of them; `core/test/trust-parity.test.ts` asserts they agree, as a JSON
27+ round trip, because that is literally what the cache file is.
28+- **Text has roles.** `humanLabel` is prose — no controls, no format characters,
29+ **no emoji**, bounded by display columns *and* code points. `opaqueId` is
30+ checked against an exact grammar. `canonicalTimestamp` re-emits one form and
31+ refuses a date that does not exist. `productFlag` GENERATES the flag from the
32+ nation.
33+- **Never accept a product glyph from input.** While flags travelled through the
34+ text filter it needed an emoji carve-out, and a carve-out without a grammar is
35+ a covert channel: TAG characters, variation selectors and ZWJ each rode through
36+ it. A `🏴` plus 42 tag characters is ONE two-column glyph spelling a full
37+ instruction sentence — invisible on a terminal, legible to a model reading
38+ `--json`.
39+- **Say which kind of failure it is.** `ParseResult` = `valid` /
40+ `definitive-none` / `malformed` / `ambiguous` / `unresolved`. For per-item
41+ market resolution, `valid`, `definitive-none` and `ambiguous` are stable and
42+ cacheable; `malformed` and `unresolved` must not become definitive negatives.
43+ A successful provider batch with one malformed sibling is still shared for
44+ the coalescing TTL: identical bytes will parse identically, and immediate
45+ refetches only waste egress and starve the readable records.
46+- **Enrichment completeness reaches the renderer.** Market cache/fetch helpers
47+ return `{ signals, complete }`; default-on annotations, dedicated tools,
48+ share cards, and structured output retain it. Only a complete empty batch may
49+ say "no signal"; an incomplete one warns and carries `marketComplete:false`
50+ or `complete:false`.
51+- **Bound the work, not just the output.** `takeBounded` slices *before* the
52+ per-record map. A surface reports `total`/`shown`/`truncated` from one
53+ `BoundedList`; no handler recomputes a count. `total` is exact only when
54+ `complete` is true, so incomplete hot-path scans use a nonnumeric `more`
55+ marker rather than guessing how many valid records remain. MCP response
56+ limiting preflights depth, width, entries, containers, and aggregate text
57+ before serialization or recursive shrinking; a byte cap alone does not bound
58+ the CPU and allocation spent discovering that a payload is too large.
59+- **Fail closed on ABSENCE.** A missing field must be at least as rejecting as a
60+ wrong one. Five market gates were `x != null && ...`, so the more malformed
61+ payload was the more successful one.
62+
63+## Changing this area
64+
65+Add the property to `core/test/trust-properties.test.ts` (table-driven off each
66+type's declared key list, so a new field fails by default), and **verify it goes
67+red** with the rule reverted — a property test you have not made fail is pinning
68+nothing. If it is a claim `SECURITY.md` makes, cite the test there;
69+`core/test/security-claims.test.ts` fails if a citation rots.
1970
