diff --git a/.cursor/rules/handoff.mdc b/.cursor/rules/handoff.mdc new file mode 100644 index 0000000..cd241cc --- /dev/null +++ b/.cursor/rules/handoff.mdc @@ -0,0 +1,24 @@ +--- +description: Short-chat handoff to cut token burn; agents cannot open new chats +alwaysApply: true +--- + +# Handoff (token control) + +Agents **cannot** create a new Cursor chat. When a handoff is needed, write `agents-docs/HANDOFF.md` and tell the user to start a **new chat** with `@agents-docs/HANDOFF.md`. + +## When to hand off + +- User says "handoff", "new chat", or "wrap up this session" +- Thread is long (many tool rounds, large pastes, repeated failed approaches) and more major work remains +- Switching to a clearly separate objective + +## What to do + +1. Overwrite `agents-docs/HANDOFF.md` — set `Status: active`; fill Goal, Completed, Changed files, Decisions, Failed approaches, Current issue, Next steps, Commands. +2. Stop major new work in this chat after writing the handoff (unless the user says continue here). +3. Tell the user the one-liner to paste in the new chat. + +## New-chat bootstrap + +If `agents-docs/HANDOFF.md` has `Status: active`, read it first and continue Next steps. Do not reload the whole monorepo or redo Completed work. diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 0000000..1cc2d7e --- /dev/null +++ b/.cursorignore @@ -0,0 +1,27 @@ +# Reduce indexing / accidental bulk reads. Scope policy lives in AGENTS.md. +# Do NOT list electron/, server/, e2e/, website/, or docs-site/ here — +# those stay reachable when the user explicitly expands scope. + +node_modules/ +**/node_modules/ +dist/ +dist-electron/ +dist-server/ +.angular/ +coverage/ +test-results/ +e2e/playwright-report/ +docs-site/.docusaurus/ +docs-site/build/ +*.sqlite +package-lock.json + +# Media / binary bulk +images/ +**/*.png +**/*.jpg +**/*.jpeg +**/*.webp +**/*.gif +**/*.mp4 +**/*.wasm diff --git a/AGENTS.md b/AGENTS.md index 12e639d..54a1dc7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,103 +1,98 @@ # AGENTS.md -Read these files at the start of every session before doing any work: +Keep this file small. Detail lives in linked docs — load those **only when the task needs them**. -1. `agents-docs/AGENT_WORKFLOW.md` — workflow and operating rules -2. `agents-docs/LESSONS.md` — durable rules learned from past corrections; apply any that match this session's work -3. `agents-docs/AGENTS_FEATURES.md` — when and how to update feature docs -4. `agents-docs/FEATURES.md` — feature index -5. `agents-docs/ENGINEERING.md` — engineering standards -6. `agents-docs/CONTEXT-MAP.md` — index of bounded contexts in this repo +## Session handoff (new chats) -Reference on-demand (when the workflow triggers them — see `agents-docs/AGENT_WORKFLOW.md` §§ 4–5): +Agents **cannot** open a new Cursor chat. To reset context: -- `agents-docs/AGENTS_CONTEXT.md` — contract for updating `CONTEXT.md` / `CONTEXT-MAP.md` -- `agents-docs/AGENTS_ADRS.md` — contract for writing architecture decision records -- `agents-docs/BUG_TRACKER.md` — Obsidian bug inbox location, allowed vault edits, and triage workflow +1. Write/update `agents-docs/HANDOFF.md` (`Status: active`) — see `.cursor/rules/handoff.mdc`. +2. Ask the user to start a **new chat** and attach `@agents-docs/HANDOFF.md`. -When working in a subdomain, also read its `CONTEXT.md` first: +**At session start:** if `agents-docs/HANDOFF.md` has `Status: active`, read it first and continue Next steps. Do not redo Completed work. -- Product client (Angular 21): `toju-app/CONTEXT.md` -- Desktop shell (Electron main + preload): `electron/CONTEXT.md` -- Signaling server (Express + WebSocket): `server/CONTEXT.md` -- End-to-end tests (Playwright): `e2e/CONTEXT.md` -- Marketing site (Angular 19): `website/CONTEXT.md` -- Application documentation (Docusaurus): `docs-site/CONTEXT.md` +## Default work scope (token fence) + +Unless the user **explicitly** expands scope, stay inside: + +- `toju-app/` (primary) +- `electron/` — **targeted only** (desktop shell is coupled to the client; see below) +- CI: `.gitea/workflows/` +- Agent/docs as needed: this file, `agents-docs/` (index-first / handoff), `toju-app/CONTEXT.md`; `electron/CONTEXT.md` only when touching Electron + +### Electron — relevant files only + +`electron/` is in default scope because the renderer talks to it via preload/IPC/local DB. Do **not** browse the whole tree. + +When a `toju-app` change needs the desktop bridge: + +1. Start from the renderer call site (`window.api` / Electron bridge usage). +2. Open only the matching surface: usually `electron/preload.ts`, then the specific handler under `electron/ipc/`, `electron/cqrs*`, or the one module/entity/migration involved. +3. Prefer ripgrep with path `electron/` + a concrete symbol over listing directories. +4. Skip unrelated areas (`electron/api/` docs server, `game-detection/`, `update/`, other migrations, etc.) unless the bug points there. + +**Still out of scope by default** (do not search/read/edit unless the user names them): + +- `server/`, `e2e/`, `website/`, `docs-site/` +- Root noise: `dist*/`, `node_modules/`, `images/`, `project-files/`, `test-results/` + +If the root cause looks like `server/` or e2e-only, **ask once** instead of exploring those trees. + +Search with path filters. Prefer `toju-app/src/app/domains//` over repo-wide greps. + +## Session start (cheap bootstrap) + +1. Skim this file. +2. If handoff `Status: active` → read `agents-docs/HANDOFF.md`. +3. Open `agents-docs/LESSONS-INDEX.md` only — match tags; open matching bodies in `LESSONS.md`. +4. Read `toju-app/CONTEXT.md` for client work; `electron/CONTEXT.md` only if this task touches Electron. +5. Other docs **on demand** only. + +**Models:** use the latest problem-solving model the user selected. Save tokens with **scope, handoffs, and short chats** — not by silently downgrading model quality. + +**Do not auto-read:** `ENGINEERING.md`, `AGENTS_FEATURES.md`, `FEATURES.md`, `CONTEXT-MAP.md`, full `AGENT_WORKFLOW.md`, feature docs, ADRs — unless needed. + +On-demand: `agents-docs/AGENT_WORKFLOW.md`, `AGENTS_FEATURES.md`, `FEATURES.md`, `ENGINEERING.md`, `AGENTS_CONTEXT.md`, `AGENTS_ADRS.md`, `BUG_TRACKER.md`. --- -MetoYou (also called Toju) is a desktop-first, P2P Discord-style chat application managed as an npm-workspaces monorepo. It bundles an Angular 21 product client, an Electron 39 desktop shell with TypeORM + sql.js for local persistence, a small Node/TypeScript Express signaling server with WebSocket-based realtime, a Playwright end-to-end suite, an Angular 19 marketing site, and a Docusaurus app/plugin documentation site that ships inside the Electron build. Voice and screen-share are WebRTC, with RNNoise denoising via a WASM audio worklet. +MetoYou / Toju: desktop-first P2P chat. Default surface: Angular client (`toju-app/`) + targeted Electron bridge + CI. -## CRITICAL — Non-negotiable rules for all agents +## CRITICAL — Done means the asked behavior works -### Test-Driven Development (MANDATORY) -**Write tests before implementation code.** +**Unit/spec green is support, not done.** -When creating or changing anything: -1. STOP — do not write implementation first -2. Write failing tests (RED) -3. Run tests and confirm failure (`npm run test` for the product client; `npm run test:e2e` for end-to-end; place spec files colocated with source, suffix `.spec.ts`) -4. Write minimal code to pass tests (GREEN) -5. Refactor while keeping tests green +1. Restate acceptance in one sentence. +2. Prove the behavior (user-visible path, focused test at the right level, or explicit manual check). +3. Prefer a regression that fails if the asked behavior regresses. -This applies to all code — Angular components and services, NgRx effects/reducers, Electron IPC handlers, server CQRS handlers, websocket message handlers, plugin runtime, and domain logic. If the code lives in a package without a configured test runner (server, website, docs-site), surface that gap before adding logic there. +### Test-backed development (balanced) -### Lint correctness (MANDATORY) -Before completing any task: -1. Run `npm run lint` from the repo root (ESLint 9 flat config in `eslint.config.js` covers every package) -2. Fix all errors -3. Do not consider work complete until it exits with code 0 +For domain/logic: failing behavior-level test → minimal fix → green. +Skip full red-green for docs/copy/agent text, formatting, trivial wiring already covered higher up. -### Type / build correctness (MANDATORY) -Type checks live in build scripts: +**Do not:** ship implementation-shaped mocks as the feature; stop at unit-green for product asks; run full monorepo / full e2e on every tiny change — targeted specs first. -- Product client (`toju-app/`): `npm run build` (Angular CLI runs `tsc` with strict settings) -- Electron (`electron/`): `npm run build:electron` (invokes `tsc -p tsconfig.electron.json`) -- Server (`server/`): `cd server && npm run build` (invokes `tsc`) +### Lint / type correctness (scoped) -If your change touches one of these packages, run the corresponding build and ensure it exits 0 before marking work complete. +1. Targeted Vitest under `toju-app/` (and colocated Electron specs only if you changed those files). +2. `npm run lint` — fix errors; do not paste entire lint logs into the chat. +3. `npm run build` when client types/templates could break; `npm run build:electron` only if you changed Electron sources. -## Most important rule +Do **not** run `cd server && npm run build` or `npm run test:e2e` unless scope expanded or the bug is proven there. -After any change that affects API contracts, schemas, invariants, workflows, or major behavior: update the relevant `agents-docs/features/.md` as part of the same task — not as a follow-up. New feature area → create `agents-docs/features/.md` and add an entry to `agents-docs/FEATURES.md` (alphabetical). +### Feature docs -The product client already maintains per-domain READMEs under `toju-app/src/app/domains//README.md`. When the change is fully internal to one of those bounded contexts and its surface stays the same, the domain README is the right place to update; cross-context contracts (websocket envelopes, IPC channels, server routes, plugin manifests) belong in `agents-docs/features/`. - -## Structure of further instructions - -- **Agent workflow & operating rules:** `agents-docs/AGENT_WORKFLOW.md` -- **Agent lessons (durable cross-session rules):** `agents-docs/LESSONS.md` -- **Engineering standards:** `agents-docs/ENGINEERING.md` -- **Feature documentation contract:** `agents-docs/AGENTS_FEATURES.md` -- **CONTEXT documentation contract:** `agents-docs/AGENTS_CONTEXT.md` -- **ADR contract:** `agents-docs/AGENTS_ADRS.md` -- **Feature index:** `agents-docs/FEATURES.md` -- **Feature docs:** `agents-docs/features/` -- **Architecture decisions:** `agents-docs/adr/` -- **Context map:** `agents-docs/CONTEXT-MAP.md` -- **Obsidian bug tracker:** `agents-docs/BUG_TRACKER.md` -- **Product-client domain:** `toju-app/CONTEXT.md` -- **Desktop-shell domain:** `electron/CONTEXT.md` -- **Server domain:** `server/CONTEXT.md` -- **E2E suite domain:** `e2e/CONTEXT.md` -- **Marketing-site domain:** `website/CONTEXT.md` -- **App-docs domain:** `docs-site/CONTEXT.md` - -Keep this file minimal. Do not duplicate detailed rules here. +Internal domain changes → `toju-app/src/app/domains//README.md`. +IPC/preload/WS contract changes → `agents-docs/features/.md` when that contract actually changed. ## Completion checklist -Before marking work complete: - -- [ ] Tests written before implementation -- [ ] All tests passing (`npm run test`, plus `npm run test:e2e` if behavior is user-visible) -- [ ] `npm run lint` passes -- [ ] Affected package builds: `npm run build` / `npm run build:electron` / `cd server && npm run build` -- [ ] Naming conventions followed (kebab-case files; domain `*.rules.ts` / `*.model.ts` / `*.component.ts` suffixes) -- [ ] Errors handled -- [ ] Feature docs updated if contract/schema/invariant changed (see `agents-docs/AGENTS_FEATURES.md`) -- [ ] `CONTEXT.md` updated if a domain term was resolved or introduced (see `agents-docs/AGENTS_CONTEXT.md`) -- [ ] ADR written if a hard-to-reverse decision was made (see `agents-docs/AGENTS_ADRS.md`) -- [ ] Lesson recorded in `agents-docs/LESSONS.md` if this session produced a correction, revert, or hidden constraint (see triggers in `agents-docs/AGENT_WORKFLOW.md`) -- [ ] PR opened with summary and linked issues (`Fixes #` / `Relates to #`) -- [ ] Gitea Workflows checks passing +- [ ] Asked behavior proven (not only unit tests green) +- [ ] Stayed in scope (`toju-app` + targeted `electron` + CI) unless user expanded it +- [ ] Appropriate targeted tests for logic changes +- [ ] Lint/build only for touched packages +- [ ] Docs only if contracts changed +- [ ] Lesson + index entry if corrected this session +- [ ] If the thread is long and work remains: write `HANDOFF.md` and ask user for a new chat +- [ ] PR when requesting merge (`Fixes #` / `Relates to #`) diff --git a/agents-docs/AGENT_WORKFLOW.md b/agents-docs/AGENT_WORKFLOW.md index 9dce6e1..69139ad 100644 --- a/agents-docs/AGENT_WORKFLOW.md +++ b/agents-docs/AGENT_WORKFLOW.md @@ -1,110 +1,74 @@ # Agent Workflow & Operating Instructions -These rules apply to **all AI agents** working on this project, regardless of platform or model. +These rules apply to **all AI agents** working on this project. -Read this file at the start of every session. +**Token budget (mandatory):** + +- Default scope: **`toju-app/` + targeted `electron/` + `.gitea/workflows/`** — see `/AGENTS.md`. No `server/`, `e2e/`, `website/`, `docs-site/` unless the user expands scope. +- Electron: follow the renderer → preload → one handler path; never dump the whole `electron/` tree. +- Prefer path-scoped search; one focused agent; no Bugbot / security / best-of-N unless asked. +- **Handoff > fat chats:** agents cannot open new chats. Write `agents-docs/HANDOFF.md` and ask the user to start a new chat with that file attached. +- **Models:** keep the user’s latest problem-solving model. Cut cost with scope + handoffs, not weaker models. + +Do **not** re-read this whole file every turn after the first skim. --- ## Workflow Orchestration -### 1. Plan Mode Default +### 1. Plan only when ambiguity is real -- Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions) -- If something goes sideways, STOP and re-plan immediately — don't keep pushing -- Use plan mode for verification steps, not just building -- Write detailed specs upfront to reduce ambiguity +- Plan for unclear architecture or when the user asks. +- Skip plan mode for focused, in-scope edits with clear acceptance criteria. -### 2. Subagent Strategy +### 2. Subagents sparingly -- Use subagents liberally to keep the main context window clean -- Offload research, exploration, and parallel analysis to subagents -- For complex problems, throw more compute at it via subagents -- One task per subagent for focused execution +- Default: one agent. +- Subagents only for true parallel search inside allowed paths. +- Never spawn extra review agents unless the user asks. -### 3. Self-Improvement Loop +### 3. Handoff / short sessions -The goal is a small, sharp file of project-specific rules in `agents-docs/LESSONS.md` that future sessions read and apply. The format of a lesson is defined at the top of `agents-docs/LESSONS.md` — read it before writing one. +Triggers: user says handoff / new chat; thread is long with more major work left; switching objectives. -**Read at session start.** Open `agents-docs/LESSONS.md` and apply any rules that match the work you're about to do. This is non-optional; the file exists so the same mistake isn't made twice. +Action: overwrite `agents-docs/HANDOFF.md` with `Status: active` (Goal, Completed, Changed files, Decisions, Failed approaches, Current issue, Next steps, Commands). Then stop major work and ask the user to open a new chat. -**Triggers — record a lesson when any of these happen.** Don't wait for a formal request; these are the signals: +New chat: if handoff is active, read it first; continue Next steps; do not redo Completed work. -- User says "no", "actually", "don't", "stop", "that's wrong", or "instead do X" -- User reverts, rewrites, or asks you to redo your edit -- User re-prompts you with the same or similar instruction (signal that the first attempt missed something) -- User points out a hidden constraint, past incident, or convention you didn't know -- Code review (human or `/review`) surfaces an issue caused by your approach -- You catch yourself about to do the same thing the project has been corrected on before +### 4. Self-Improvement Loop -If unsure whether it's worth recording: write it. Sharper is better than missing, and grooming the file is cheap. +**At session start:** `LESSONS-INDEX.md` only; open matching lesson bodies by tag. -**Write before reporting done.** A session that produced a correction must produce a lesson — record it in the same turn the work is completed, not "later". The `AGENTS.md` completion checklist has a line for this; don't tick the box without it. +Record a lesson + index line when corrected. Prefer fewer sharp rules (~20). -**Groom periodically.** When `agents-docs/LESSONS.md` passes ~20 entries, propose consolidations to the user — merge duplicates, delete rules that no longer apply, shorten anything vague. +### 5. CONTEXT.md upkeep -### 4. CONTEXT.md upkeep +Default: `toju-app/CONTEXT.md`. Read `electron/CONTEXT.md` only when touching Electron. Other packages only when in scope. -Read `CONTEXT.md` (or `agents-docs/CONTEXT-MAP.md` → per-subdomain `CONTEXT.md`) when working in a subdomain. Use its vocabulary verbatim **where defined** in code, tests, issues, and commits. If a needed term isn't in the glossary, treat it as a trigger (see below) rather than silently inventing a synonym; the full contract lives in `agents-docs/AGENTS_CONTEXT.md`. +### 6. ADR upkeep -**Triggers — capture vocabulary in the moment:** +Only when hard-to-reverse + surprising + real trade-offs. Contract: `agents-docs/AGENTS_ADRS.md`. -- A previously-ambiguous domain term gets a clear resolution → add it (one-sentence definition, aliases to avoid). -- User corrects your terminology → record the correct term; mark the wrong one as an alias to avoid. -- A new feature introduces a concept absent from the glossary → add it before claiming the feature done. -- You catch yourself inventing a synonym because the right term isn't there → flag the gap; don't silently coin a new term. +### 7. Verification Before Done (behavior first) -**Write before reporting done.** Update the relevant `CONTEXT.md` in the same turn the trigger fires. Append-only — add new entries, don't reshuffle existing ones. The format is documented at the top of each `CONTEXT.md`. See `agents-docs/AGENTS_CONTEXT.md` for the full contract. +Done = asked functionality works. Unit green ≠ done for product asks. Prefer in-scope proof; don’t pull e2e/server “just to be sure.” -### 5. ADR upkeep +### 8. Demand Elegance (Balanced) -Read `agents-docs/adr/` when about to change anything that crosses an existing decision boundary. If your work would contradict an ADR, surface it explicitly — never silently override. +One pause for non-trivial design; skip for obvious fixes. -**Triggers — write an ADR only when all three apply:** +### 9. Autonomous Bug Fixing -- **Hard to reverse** (schema migration, framework swap, integration redesign). -- **Surprising without context** (future engineers will question the approach). -- **Result of genuine trade-offs** (real alternatives existed and you chose deliberately). - -If all three apply: write the ADR in the same turn as the decision. Next number (4-digit zero-padded), kebab-case slug, Nygard short form — see `agents-docs/adr/0001-record-architectural-decisions.md` for the canonical example and `agents-docs/AGENTS_ADRS.md` for the contract. If any of the three is missing: don't write one. - -**Supersede, don't delete.** Overturned decisions get a new ADR; the old one stays with a `Superseded by ADR-NNNN` note. - -### 6. Verification Before Done - -- Never mark a task complete without proving it works -- Diff behavior between main and your changes when relevant -- Ask yourself: "Would a staff engineer approve this?" -- Run tests, check logs, demonstrate correctness - -### 7. Demand Elegance (Balanced) - -- For non-trivial changes: pause and ask "is there a more elegant way?" -- If a fix feels hacky: "Knowing everything I know now, implement the elegant solution" -- Skip this for simple, obvious fixes — don't over-engineer -- Challenge your own work before presenting it - -### 8. Autonomous Bug Fixing - -- When given a bug report: just fix it. Don't ask for hand-holding -- Point at logs, errors, failing tests — then resolve them -- Zero context switching required from the user +Fix with evidence in default scope. If root cause is clearly `server/` (or similar), say so and ask to expand scope. --- ## Pull Requests -This project hosts at Gitea (`git.azaaxin.com/myxelium/Toju`). Gitea PRs and issues use GitHub-style syntax. - -- Create a feature branch for every change: `/` (e.g. `feat/add-retry-logic`, `fix/null-pointer-webhook`) — `` should match the Conventional Commits prefix (`feat`, `fix`, `chore`, `docs`, `perf`, `refactor`, `test`) -- Open the PR via the Gitea web UI (or `tea pulls create` if `tea` CLI is installed) — include a summary and a test plan -- Link issues in the PR body with `Fixes #` for auto-close or `Relates to #` for reference (Gitea honors the same keywords as GitHub) -- After merge, delete the feature branch +Gitea: `git.azaaxin.com/myxelium/Toju`. Branch `/`; PR with summary + test plan; `Fixes #` / `Relates to #`. --- ## Core Principles -- **Simplicity First:** Make every change as simple as possible. Impact minimal code. -- **No Laziness:** Find root causes. No temporary fixes. Senior developer standards. -- **Minimal Impact:** Changes should only touch what's necessary. Avoid introducing bugs. +- **Simplicity First** · **No Laziness** · **Minimal Impact** · **Cheap Context** · **Default Scope Fence** · **Handoff Beats Fat Context** diff --git a/agents-docs/ENGINEERING.md b/agents-docs/ENGINEERING.md index 0d884a6..3600d78 100644 --- a/agents-docs/ENGINEERING.md +++ b/agents-docs/ENGINEERING.md @@ -45,9 +45,14 @@ The server package does not currently have a test runner script — there is one E2E tests exercise the real Electron app against the real signaling server. The `.agents/skills/playwright-e2e/SKILL.md` describes the convention this repo uses for E2E test design — read it before adding new tests. -### TDD discipline +### Test-backed development (balanced) -Write the failing test first. Run it, watch it fail, then write the smallest code that makes it pass. This rule is non-negotiable (see `/AGENTS.md` § CRITICAL). +See `/AGENTS.md` § CRITICAL for the full policy. Summary: + +- **Done** = the asked behavior works, proven at the right level. Green unit tests alone are not done when the ask was product behavior. +- Prefer red-green for domain/logic and bug invariants; encode the **user-facing or cross-boundary** outcome, not only implementation details. +- Skip full red-green for docs/copy/agent-instruction text, pure formatting, and trivial wiring already covered by a higher-level test you will run. +- Run **targeted** specs while iterating (`cd toju-app && npx vitest run `); broaden to full suite / e2e when risk is cross-cutting or before merge. Integration / cross-package work that needs a real database can rely on Electron's TypeORM + sql.js setup (in-memory by default) — no Testcontainers required. diff --git a/agents-docs/HANDOFF.md b/agents-docs/HANDOFF.md new file mode 100644 index 0000000..639e075 --- /dev/null +++ b/agents-docs/HANDOFF.md @@ -0,0 +1,29 @@ +# Session Handoff + +> **New chat:** attach `@agents-docs/HANDOFF.md` (and only the files listed under Changed files). Say: continue from this handoff; do not redo completed work. Stay in default scope unless Next steps expand it. +> +> **Agents cannot open a new chat** — write/update this file, then ask the user to start one. + +**Status:** none + + + +## Goal + +## Completed + +## Changed files + +## Decisions (this session only) + +## Failed approaches + +## Current issue / blockers + +## Next steps (exact) + +## Commands to continue + +## Scope note + +Default: `toju-app/` + targeted `electron/` + `.gitea/workflows/`. Out of scope unless listed here: `server/`, `e2e/`, `website/`, `docs-site/`. diff --git a/agents-docs/LESSONS-INDEX.md b/agents-docs/LESSONS-INDEX.md new file mode 100644 index 0000000..2577290 --- /dev/null +++ b/agents-docs/LESSONS-INDEX.md @@ -0,0 +1,58 @@ +# Agent Lessons — Index + +**Session start:** read this file only. Match tags to the task. Open the matching lesson body in `agents-docs/LESSONS.md` — do **not** load every lesson. + +When adding a lesson: append the full entry near the top of `LESSONS.md` (under `## Lessons`) **and** add one bullet here. + +Tags help grepping: `rg '\\[attachments\\]' agents-docs/LESSONS-INDEX.md` + +## Index + +- Keep `NgOptimizedImage` off runtime blob and data URLs — `[angular] [images]` +- Read the exact Obsidian bug note before diagnosing a named ticket — `[workflow] [bugs]` +- Run `npm run i18n:sync` after editing any `public/i18n/catalog/*.json` file — `[i18n] [testing]` +- Match direct-call recipients against every local identity alias, exactly like DMs already do — `[direct-call] [identity]` +- Resolve outbound direct-call recipient ids to the peer's connected signal identity — `[direct-call] [identity] [signaling]` +- Canonicalize direct conversation ids across cross-signal actor aliases — `[direct-message] [identity]` +- Decide attachment receive admission once at request time; never re-gate size in the chunk handler — `[attachments]` +- Re-queue attachment auto-downloads on every message/room binding event; never trust one transport's ordering — `[attachments] [realtime]` +- Scope per-user UI state by user id, not by the client database — `[persistence] [multi-user] [custom-emoji]` +- Don't strand signed-out mobile users on a logged-out dashboard — `[auth] [mobile] [routing]` +- "Shared from your device" must gate on local bytes, not uploader user id — `[attachments] [multi-device]` +- Generate Android brand icons from the source mark; guard against stock Capacitor placeholders — `[mobile] [android] [assets]` +- Bind chat attachments to a pre-allocated message id, never by matching content — `[attachments] [chat] [mobile]` +- Attachment file persistence must be platform-agnostic, not Electron-only — `[attachments] [persistence] [mobile]` +- Never count duplicate chunks toward transfer progress, and never finalize on byte counters — `[attachments] [webrtc]` +- Don't bump E2E timeouts for sync flakes - gate on presence and read server logs — `[testing] [realtime]` +- When renaming an Angular route, sweep every navigate/url-match/doc reference — `[routing]` +- Server discovery must fan out across all endpoints and self-heal on 404 — never hardcode a host capability blocklist — `[server-directory]` +- Server registration needs `ownerPublicKey: oderId || id`, and must not be fire-and-forget — `[server-directory] [rooms]` +- Identify must fall back to the legacy session token, not only the new credential store — `[realtime] [authentication]` +- Keep the per-signal-URL identify credential resolvable from the store — `[realtime] [authentication]` +- Store clientInstanceId in sessionStorage not localStorage — `[realtime] [multi-device]` +- Revalidate IndexedDB scope without reinitializing on every read — `[persistence] [performance]` +- Restore local user scope before protected writes — `[authentication] [persistence]` +- Persisted local user state still requires a session token — `[authentication] [signaling]` +- Declare MODIFY_AUDIO_SETTINGS for Android WebRTC mic capture — `[mobile] [android]` +- Do not override Tailwind with box-sizing inherit — `[mobile] [css]` +- Use the app-shell servers rail for mobile discovery pages — `[mobile] [layout]` +- Defer attachment blob hydration on Electron startup — `[attachments] [electron]` +- Lazy-load Capacitor modules on Electron/desktop — `[mobile] [electron]` +- Use the upgrade transaction during IndexedDB schema migrations — `[persistence] [browser]` +- Wait for authenticateUser storage prep before post-login navigation — `[authentication] [browser]` +- Use dense arrays for chunked transfer buffers — `[custom-emoji] [webrtc]` +- Route custom emoji right-click through the native context menu — `[custom-emoji] [ux]` +- Separate known emoji assets from saved library — `[custom-emoji] [ux]` +- Chunk custom emoji assets over data channels — `[custom-emoji] [webrtc]` +- Re-clear visible notification channels after recompute — `[notifications] [startup]` +- Disambiguate nested chat cards — `[chat] [ui]` +- Use terminal Vitest when the test tool hangs — `[testing]` +- Do not add fake chrome around screenshots — `[website] [design]` +- Verify lint exits 0 before claiming done — `[verification]` +- Use blob URLs for inline attachment previews — `[attachments] [electron]` +- Resolve Electron drag-and-drop file paths with webUtils — `[attachments] [electron]` +- Preserve uploader local attachment paths across sync — `[attachments] [persistence]` +- Prove the asked behavior; unit-green is not done — `[verification] [testing] [workflow]` +- Default to `toju-app/` + targeted `electron/` + CI; do not crawl the monorepo — `[workflow] [tokens] [scope]` +- Write HANDOFF.md and ask the user for a new chat — agents cannot open chats — `[workflow] [tokens] [handoff]` + diff --git a/agents-docs/LESSONS.md b/agents-docs/LESSONS.md index 08169db..8105912 100644 --- a/agents-docs/LESSONS.md +++ b/agents-docs/LESSONS.md @@ -1,12 +1,12 @@ # Agent Lessons -Durable rules for AI agents working on this project. Read this file at session start. Append to it when this session produces a correction worth remembering. +Durable rules for AI agents working on this project. ## How to use this file -**At session start:** scan the rules below. If any match the work you're about to do, apply them. +**At session start:** read `agents-docs/LESSONS-INDEX.md` only. Open lesson bodies here **only** for tags that match the task. Do not load this entire file into context by default. -**During the session:** if the user corrects you, reverts your edit, or re-prompts with the same instruction — that is a signal to record a lesson before closing the task. See the trigger list in `agents-docs/AGENT_WORKFLOW.md`. +**During the session:** if the user corrects you, reverts your edit, or re-prompts with the same instruction — record a lesson here **and** add a one-line entry to `LESSONS-INDEX.md` before closing the task. See triggers in `agents-docs/AGENT_WORKFLOW.md`. **Format of a lesson:** every entry uses the four-slot template below. Brevity matters — if you can't state the rule in one sentence, the lesson isn't sharp enough yet. @@ -25,6 +25,27 @@ Durable rules for AI agents working on this project. Read this file at session s ## Lessons +### Default to `toju-app/` + targeted `electron/` + CI; do not crawl the monorepo [workflow] [tokens] [scope] + +- **Trigger:** about to browse all of `electron/`, or to `grep`/`Read` under `server/`, `e2e/`, `website/`, or `docs-site/` on a normal product bug without the user naming those packages. +- **Rule:** stay in `toju-app/`, `.gitea/workflows/`, and **only the Electron files on the renderer→preload→handler path**; if the fix looks like `server/`/e2e, ask once instead of exploring those trees. +- **Why:** monorepo-wide (and whole-`electron/`) exploration multiplies context on expensive problem-solving models without fixing the asked client bug. +- **Example:** attachment disk restore → `toju-app` persistence service + `electron/preload.ts` + the one IPC/file helper involved — not every file under `electron/migrations/` or `electron/api/`. + +### Write HANDOFF.md and ask the user for a new chat — agents cannot open chats [workflow] [tokens] [handoff] + +- **Trigger:** the thread is long, the user says "handoff"/"new chat", or a new major objective starts while more work remains. +- **Rule:** overwrite `agents-docs/HANDOFF.md` with `Status: active` and the eight sections, then ask the user to start a new chat attaching that file; never pretend you opened a fresh chat. +- **Why:** fat chat history dominates token burn on problem-solving models; a 5–10k handoff replaces 100k+ of mixed failure/tool output. +- **Example:** user: "handoff" → write HANDOFF → reply: "Start a new chat and attach `@agents-docs/HANDOFF.md`; say continue from handoff." + +### Prove the asked behavior; unit-green is not done [verification] [testing] [workflow] + +- **Trigger:** about to report a task finished because colocated Vitest specs (or a narrow mocked unit) are green, while the user’s ask was a product behavior, UI flow, or bug they can still reproduce. +- **Rule:** treat acceptance as “the asked functionality works” — prove it with a user-visible path, focused e2e, or an explicit manual check; keep unit tests as support, never as the sole done signal. +- **Why:** agents optimized for TDD often stop at implementation-shaped tests that pass while the real feature/bug remains broken, which wastes follow-up turns and burns tokens on false completion. +- **Example:** for “DM reply doesn’t show for the caller,” a passing `DirectMessageService` mock test is insufficient until the cross-signal conversation identity path is exercised (e2e or a behavior-level regression that fails on the old fork-thread bug). + ### Keep `NgOptimizedImage` off runtime blob and data URLs [angular] [images] - **Trigger:** Angular template lint suggests replacing `[src]` with `ngSrc` for a user-uploaded image rendered from `blob:` or `data:`. @@ -53,6 +74,20 @@ Durable rules for AI agents working on this project. Read this file at session s - **Why:** the failure only reproduces when caller and callee have different home signal servers, which no same-server e2e covers; and when one identity-alias bug is fixed in a domain, grep for the same `=== currentUserId` pattern in sibling domains that share the transport — the direct-call domain reused `PeerDeliveryService` but kept the naive check for another month. - **Example:** `direct-call-participant-identity.rules.ts#directCallPayloadIncludesAnyId` / `normalizeDirectCallPayloadSelfAliases`; regression e2e `e2e/tests/voice/dm-header-call-ring.spec.ts` registers Bob on a secondary signal server, meets in a primary-signal room, and asserts the DM-header call rings Bob's incoming-call modal (fails on old code, passes after). +### Resolve outbound direct-call recipient ids to the peer's connected signal identity [direct-call] [identity] [signaling] + +- **Trigger:** cross-signal direct calls still failed after the inbound alias fix — the caller joined voice and showed "In voice" while the callee never rang. `PeerDeliveryService.resolveSignalingPeerId` returned null when the stored peer id was a home id but presence/route was registered under the provisioned actor id, so `sendRawMessage` was never called; even when attempted, the server relays only when `targetUserId` exactly matches the callee's connected `oderId`. +- **Rule:** outbound DM/call delivery must collect every recipient alias (`peer-delivery-identity.rules.ts#collectRecipientDeliveryCandidateIds`), pick the routable id with `pickRoutableRecipientId`, always attempt signaling send (broadcast fallback when no single route works), and surface `call.errors.recipientUnreachable` to the caller when delivery cannot succeed — never leave the caller in a silent "In voice" state. +- **Why:** inbound and outbound identity bugs are independent; fixing admission on the callee does not help if the ring never leaves the caller or hits the wrong `targetUserId` on the wire. +- **Example:** `PeerDeliveryService.sendViaSignaling` + `DirectCallService.resolveRoutableRecipientId`; e2e `e2e/tests/voice/dm-header-call-ring.spec.ts` (callee-home room, people-search call). + +### Canonicalize direct conversation ids across cross-signal actor aliases [direct-message] [identity] + +- **Trigger:** cross-signal DMs delivered to the callee but the caller never saw replies — User1 had two DM rows for User2, replies landed in the actor-id thread, and DM routes could throw "Cannot use direct messages without a current user" when `openConversation` ran before hydration. +- **Rule:** incoming DM traffic must resolve to the existing home-id conversation for the same peer (`direct-message-conversation-identity.rules.ts`), merge duplicate alias threads on load/receive, queue inbound events until `currentUser` hydrates, and guard route-driven `openConversation` calls when no owner id is available yet. +- **Why:** recipient-alias admission alone is insufficient — if `message.conversationId` still carries a foreign actor id, the client forks a second local thread and the original chat stays empty. +- **Example:** `resolveDirectConversationId` + `mergeAliasDirectConversations` in `DirectMessageService.handleIncomingMessage`; `appendCrossSignalActorAliases` + `pickDeliveryTargetIds` when a foreign-home recipient is addressed by home id but only their shared-room actor id is routed (debug pattern: `c84813bb…` home id vs `e79a95e5…` actor on `signal.toju.app`). + ### Decide attachment receive admission once at request time; never re-gate size in the chunk handler [attachments] - **Trigger:** "Sending files between users doesn't really work" — a browser user clicked Request on a 10–50 MB generic file, the request gate (`canReceiveAttachment`) admitted it for in-memory receive, the sender streamed chunks, but `handleFileChunk` still had a leftover hard `size > MAX_AUTO_SAVE_SIZE_BYTES` rejection on the in-memory path, so every chunk was dropped, no ack was ever sent, the sender's `waitForAck` timed out, and the GUI never changed.