| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 3 | 2 | 0% |
| Commands | 0 | 0 | 4 | 0% |
| Section tags | 0 | 1 | 3 | 0% |
What each file covers
Sections
0 shared · 3 only in A · 2 only in B- − The trust boundary
- − Rules
- − Changing this area
- + CLAUDE.md
- + Claude Code specifics
Commands
0 shared · 0 only in A · 4 only in B- + pnpm -F @claudinho/mcp build
- + pnpm -r typecheck
- + pnpm lint
- + gh run watch <id> --exit-status
Section tags
0 shared · 1 only in A · 3 only in B- − do-not
- + lint-format
- + performance
- + agent-behaviour
Line diff
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
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
@@ −1 +1 @@
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−---
1+# CLAUDE.md
62
7−# The trust boundary
3+This project uses **AGENTS.md** as the primary agent guide. Read it first:
84
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.
5+@AGENTS.md
126
13−## Rules
7+## Claude Code specifics
148
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.
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.
7019
