Files
LlamaCasty/AGENTS.md
T
gramps c9ac348976 docs(ai): ai.md becomes a router; invariants move to AGENTS.md; budgets gated
ai.md 228 → 64 lines. It now carries only: what the product is, the build
commands, the architecture table, a "where the truth lives" table, and a
pointer to HANDOFF. No narrative, no status, no duplicated invariants.

AGENTS.md (195 lines) is now the single always-read file:
- the 20 invariants that break output, wipe user data, or ship a legal/trust
  breach — one line each, pointing at the doc that explains it (they replace
  110 lines of invariant stubs that duplicated docs/ai/*.md verbatim)
- Response style + No-Fluff Mode moved in from ai.md (rules belong in the rules file)
- onboarding is now: HANDOFF → ai.md router → the ONE docs/ai file → dir index;
  TASKS.md is read when the question is "what's next", not to write code
- memory rides with the change but is no longer a unit of its own; HANDOFF is
  ≤40 lines and overwritten, never appended

Anti-rebloat gate: scripts/verify.sh gains step 1/4, a doc-budget check
(AGENTS ≤210, ai ≤100; HANDOFF/TASKS caps land with the queue trim). A commit
that pads a capped file instead of moving detail fails the gate.

schema.md and docs/index.md corrected for the new roles and reading order.
Docs-only unit: scope-check run, no build.
2026-10-29 11:08:07 -07:00

196 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS — how to work in this repo
Auto-read at the start of every session: the rules, the invariants that break the product, and
the map to everything else. `ai.md` is the router, `docs/ai/*.md` is the subsystem truth,
`HANDOFF.md` is current state.
## ⚠️ CRITICAL: The Good Dog Rule
BEFORE any planning or coding, follow this rule:
> ONE integration test per change.
When the user asks for changes that would involve multiple integration tests:
1. Identify the ONE specific use case/test they want to work on
2. Focus ONLY on the single use case until it's complete and tests pass
> *A good dog learns one trick perfectly before learning the next.*
**No feature branches pre-1.0 (decided 2026-08-24):** solo dev, pre-1.0 — all
work lands directly on `main`, committed per work unit. No `taskNN-*` branches, no PRs. The scope
discipline above is about test focus, not git mechanics.
**Commit / push policy (2026-08-30, user directive):** commit every work unit —
committing is expected and never needs extra permission. Pushing is different:
push ONLY at sub-milestones and milestones. During a multi-commit refactor,
that means commits land locally every partial; `git push` happens at the
session's agreed checkpoint(s), not per-commit. When a milestone warrants a
push the user will say so (or say "push it").
## Onboarding (in order — and no further)
1. [`HANDOFF.md`](HANDOFF.md) — current state: branch, what's in flight, next action, landmines.
**Trust it**; no branch hunting, no file-scanning to re-derive what it already says.
2. [`ai.md`](ai.md) — the router: what the product is, the architecture table, and where each
subsystem's truth lives.
3. **The one** [`docs/ai/`](docs/ai/index.md) file that matches your task — not all of them.
The routing table at the top of `ai.md` picks it.
4. `<dir>/index.md` — the index for any directory you're about to touch.
`TASKS.md` is the queue. Read it when the user asks what's next or status is part of the
question — it is not required reading to write code.
**Why it is this short:** the mandatory read used to be ~56k tokens of map, so sessions paid it
and then skimmed — which is exactly how stale facts survive. Read the one subsystem you touch.
## Reading budget
Enforced by `scripts/verify.sh`; a commit that pads instead of replacing fails the gate.
| File | Cap | Role |
|---|---|---|
| `AGENTS.md` | 210 lines | rules + invariants, always read |
| `ai.md` | 100 lines | router only — no narrative, no status |
| `HANDOFF.md` | 40 lines | current state, overwritten every time |
| `TASKS.md` | 120 lines | the active queue; done work moves to `TASKS/archive.md` |
If a fact does not fit, it belongs in `docs/ai/<subsystem>.md`, `MyMistakes.md`, or `git log`.
## Working rules
- **Never work without the map.** If the map contradicts the code, the code wins and the facts
your change **falsifies** are corrected in the same commit (stale facts are corrected, not
appended). Cosmetic drift — a stale task number, a heading — may wait for a sweep; it is not a
reason to stall a unit.
- **Memory rides with the change, not as a unit of its own.** Correct what the change
falsifies in the same commit; a broad restatement can be batched at a milestone.
- **`HANDOFF.md` is ≤40 lines and is overwritten, never appended:** branch, in flight, dirty
files, next action, new landmines. History belongs to `git log`.
- **The first time a fact costs a hunt, record it where it will be found:** paths / ports /
secrets / build commands → `docs/ai/environment.md`; subsystem traps → that subsystem's
`docs/ai` file; one-off recipes → `MyMistakes.md` → Recipes registry.
- **Derived-solution rule (record once, then grep):** when you work out a reusable solution — a
recipe, workaround, or how-to (e.g. how to shrink and re-encode an image for the README) —
write it into `MyMistakes.md` **in the same session**, and **grep `MyMistakes.md` first** on
any "I've done this before but have to figure it out again" wall. An un-recorded solution
guarantees the user hears it derived again (the image-shrink incident, 2026-08-29).
- **Follow existing conventions** — MVVM, `RelayCommand` for actions,
`ViewModelBase.SetProperty<T>()`, all styles in `Themes/Controls.xaml`
(merged once in `App.xaml`; never duplicate per-window).
- **Do not add comments unless the code needs them; do not expand the task queue
on your own** — work only what the user queues.
- **Derivative work / spin guard.** Every problem here is derivative — OBS, other
overlay tools, and the WPF/WebView2 ecosystems already solved it. Both sides are
mandatory, not advisory:
- **Proactive (new features):** before writing code, do a quick external scan
(websearch: how do OBS/CEV/overlay tools do this?) and cite the reference in the
commit message.
- **Spin guard (objectively triggered):** a SECOND failed remedy for the same
symptom = STOP. No third guess over the same code. Research the established
answer externally, cite its URL in the commit message AND in `MyMistakes.md`,
then code it. Repeated failure is a research trigger, not a persistence trigger.
- **Response style: no default planning template.** Do the work, then report
what changed and what's next. No "Plans & Pitfalls", pros/cons tables, or
step-by-step plans unless the user asks for a plan first. A short diff-style summary beats a
proposal document every time.
- **No-Fluff Mode is available on request.** Invoke with "no-fluff mode" (or similar) for
ruthless review instead of reassurance: strip pleasantries, emojis and padding; treat the
user's input as a draft to deconstruct; give unvarnished truth. Occasional and
explicitly-invoked — never the default.
## Invariants that break the product
Violating any of these breaks output, wipes a user's data, or ships a legal/trust breach. Each
line points at the file that explains it.
- **The Background is one locked layer per scene** — index 0, never addable/removable/reorderable;
Live is `DisplayCapture`, every other canonical scene is static art. Never feed a capture row to
the `ActiveBackgroundImage` overlay. → `docs/ai/capture.md`
- **Every global overlay composites on every render path** — pump, preview/thumbnails, scene dump.
A feature wired into one path is silently absent from the output. → `docs/ai/overlays.md`
- **`VideoFrame.Epoch` bumps on every frame from a recycling producer**, or the pump's paste cache
and full-render signature serve stale pixels. → `docs/ai/frame-pipeline.md`
- **Never demote threads inside `Parallel.For`** — `StripPool` owns four `BelowNormal` threads;
strips below 64 rows run inline. → `docs/ai/frame-pipeline.md`
- **Submit never blocks:** the encoder queue is bounded, the newest frame is dropped and counted,
and `StopAsync` flushes the encoder before awaiting the pump. → `docs/ai/frame-pipeline.md`
- **Audio closes before video:** `StopStream` runs `StopLive` (audio EOF) before stopping the pump;
the reverse order stalls on pipe backpressure. → `docs/ai/output.md`
- **rawvideo is paced by arrival, never by a timestamp** — no `-re`, and `nextTick` is never
rebased to wall-now (that turns a hitch into a silent time-lapse). → `docs/ai/output.md`
- **The encoder is probed, never libx264** — NVENC → QSV → AMF → OpenH264; no static or GPL codec
path. → `docs/ai/output.md`
- **REC and ON-AIR are radio-exclusive** — one ffmpeg, one output, in the UI and the VM.
→ `docs/ai/output.md`
- **ffmpeg is bundled and separate** — never downloaded at runtime, never linked into the app; the
pin stays `lgpl-shared`. → `docs/ai/product.md`
- **A modal's falsy result never falls through into its side effect** — Cancel on the recording
save dialog deletes the temp footage. → `docs/ai/output.md`
- **Any test that constructs `MainWindow` must point `LayoutPathOverride` at a temp DB** and clear
it in `finally`, or it overwrites the user's real layout. → `docs/ai/environment.md`
- **The vertical tier is geometry only**, and the Chat camera is rect-locked in the MODEL
(`VerticalShapeLocked`), not just in the UI. → `docs/ai/capture.md`
- **Never override the user's camera choice** — there is deliberately no fallback camera.
→ `docs/ai/capture.md`
- **Chat is rendered, never stored** — no history, no moderation log, no reward payloads; the
Store 11.12 certification answer depends on it. → `docs/ai/product.md`
- **Full trust only** — a packaged build flipped to `appContainer` silently loses recordings.
→ `docs/ai/product.md`
- **A `PopupRoot` has no visual parent** — ancestor walks must fall back to
`LogicalTreeHelper.GetParent`, or a popup row click reads as an outside click.
→ `docs/ai/ui-surface.md`
- **Licensing: no non-CC0 icon art, no dropped `THIRD-PARTY-NOTICES.txt`, no
licence-incompatible dependency, no unvetted ML model.** → `docs/ai/product.md`
- **The private-only broadcast lock is deliberate** — do not unlock it, do not raise it as a
question; it ends at v1 by the creator's ruling. → `docs/ai/product.md`
- **Read render numbers in Release only** — Debug pixel paths are several× slower.
→ `docs/ai/environment.md`
## Scope Lock
BEFORE editing any file, declare the exact file list for the task. Every file you
touch must be either in that list OR you must state the specific dependency that
requires it (e.g. "method X's signature changed, callers must update"). No "while
I'm here" edits. No refactoring. No style tweaks. If you spot a problem in a file
you're already editing, note it in HANDOFF.md as a follow-up — do not fix it in
this change.
> *The commit `d2114c7` touched 11 files across 4 layers because the AI decided
> to refactor the world. This rule exists because of that incident.*
### Before editing a file — git history scan
```
git log --oneline -5 -- <file>
```
If the file hasn't been touched in many commits and the current task doesn't
directly require changing it, that is a red flag — stop and justify the edit or
don't make it. A stable file touched by an unrelated task is a bug, not a feature.
### Pre-commit audit
One gate: `./scripts/verify.sh "<file1>" "<file2>" ...` with the declared scope. It runs the doc
budgets, a **clean** build (0 warnings), the test suite, and `scope-check.sh`. Docs-only units run
`scope-check.sh` alone.
```bash
./scripts/verify.sh "Models/Scene.cs" "ViewModels/MainViewModel.cs" "MainWindow.xaml"
```
## Build
From WSL, ALWAYS use the Windows dotnet host — Linux `dotnet` re-downloads the
`windowsdesktop.app.*` packs over the slow 9p bridge and re-restores twice (WPF
`_wpftmp`), and `--no-restore` right after an interrupted restore produces bogus
`NETSDK1064` errors. See `docs/ai/environment.md` → Run for the exact commands and why.
```bash
"/mnt/c/Program Files/dotnet/dotnet.exe" build "C:\Users\gramp\Documents\Code\projects\ytLive\ytLive.csproj"
"/mnt/c/Program Files/dotnet/dotnet.exe" vstest "C:\Users\gramp\Documents\Code\projects\ytLive\ytLive.Tests\bin\Debug\net8.0-windows10.0.19041.0\ytLive.Tests.dll"
```
Keep it at **0 warnings**. Running requires Windows. On a silent startup crash,
read `%APPDATA%\ytLlive\startup.log` (`Helpers/AppLog.cs` writes checkpoints and
unhandled exceptions there).