From dc87fa42b2406504bf5a34e844213a868fb04585 Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Tue, 10 Mar 2026 00:00:57 -0700 Subject: [PATCH] Update AGENTS.md --- AGENTS.md | 9 ++++++++ app/AGENTS.md | 12 +++++++++- frontend/AGENTS.md | 22 ++++++++++++------- .../AGENTS_packet_visualizer.md | 0 4 files changed, 34 insertions(+), 9 deletions(-) rename frontend/src/components/{ => visualizer}/AGENTS_packet_visualizer.md (100%) diff --git a/AGENTS.md b/AGENTS.md index 1b02d8a..c8cf8dc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -93,6 +93,15 @@ Ancillary AGENTS.md files which should generally not be reviewed unless specific 5. **Offline-capable**: Radio operates independently; server syncs when connected 6. **Auto-reconnect**: Background monitor detects disconnection and attempts reconnection +## Code Ethos + +- Prefer fewer, stronger modules over many tiny wrapper files. +- Split code only when the new module owns a real invariant, workflow, or contract. +- Avoid "enterprise" indirection layers whose main job is forwarding, renaming, or prop bundling. +- For this repo, "locally dense but semantically obvious" is better than context scattered across many files. +- Use typed contracts at important boundaries such as API payloads, WebSocket events, and repository writes. +- Refactors should be behavior-preserving slices with tests around the moved seam, not aesthetic reshuffles. + ## Intentional Security Design Decisions The following are **deliberate design choices**, not bugs. They are documented in the README with appropriate warnings. Do not "fix" these or flag them as vulnerabilities. diff --git a/app/AGENTS.md b/app/AGENTS.md index e44d0f5..cdd6542 100644 --- a/app/AGENTS.md +++ b/app/AGENTS.md @@ -11,6 +11,14 @@ Keep it aligned with `app/` source files and router behavior. - MeshCore Python library (`meshcore` from PyPI) - PyCryptodome +## Code Ethos + +- Prefer strong domain modules over layers of pass-through helpers. +- Split code when the new module owns real policy, not just a nicer name. +- Avoid wrapper services around globals unless they materially improve testability or reduce coupling. +- Keep workflows locally understandable; do not scatter one reasoning unit across several files without a clear contract. +- Typed write/read contracts are preferred over loose dict-shaped repository inputs. + ## Backend Map ```text @@ -19,7 +27,7 @@ app/ ├── config.py # Env-driven runtime settings ├── database.py # SQLite connection + base schema + migration runner ├── migrations.py # Schema migrations (SQLite user_version) -├── models.py # Pydantic request/response models +├── models.py # Pydantic request/response models and typed write contracts (for example ContactUpsert) ├── repository/ # Data access layer (contacts, channels, messages, raw_packets, settings, fanout) ├── services/ # Shared orchestration/domain services │ ├── messages.py # Shared message creation, dedup, ACK application @@ -240,6 +248,8 @@ Main tables: - `contact_name_history` (tracks name changes over time) - `app_settings` +Repository writes should prefer typed models such as `ContactUpsert` over ad hoc dict payloads when adding or updating schema-coupled data. + `app_settings` fields in active model: - `max_radio_contacts` - `favorites` diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md index 68f9381..776ce3b 100644 --- a/frontend/AGENTS.md +++ b/frontend/AGENTS.md @@ -16,6 +16,15 @@ Keep it aligned with `frontend/src` source code. - `meshcore-hashtag-cracker` + `nosleep.js` (channel cracker) - Multibyte-aware decoder build published as `meshcore-decoder-multibyte-patch` +## Code Ethos + +- Prefer fewer, stronger modules over many thin wrappers. +- Split code only when the new hook/component owns a real invariant or workflow. +- Keep one reasoning unit readable in one place, even if that file is moderately large. +- Avoid dedicated files whose main job is pass-through, prop bundling, or renaming. +- For this repo, "locally dense but semantically obvious" is better than indirection-heavy "clean architecture". +- When refactoring, preserve behavior first and add tests around the seam being moved. + ## Frontend Map ```text @@ -37,12 +46,10 @@ frontend/src/ │ ├── index.ts # Central re-export of all hooks │ ├── useConversationActions.ts # Send/resend/trace/block conversation actions │ ├── useConversationNavigation.ts # Search target, selection reset, and info-pane navigation state -│ ├── useConversationMessages.ts # Dedup/update helpers over the conversation timeline -│ ├── useConversationTimeline.ts # Fetch, cache restore, jump-target loading, pagination, reconcile +│ ├── useConversationMessages.ts # Conversation timeline loading, cache restore, jump-target loading, pagination, dedup, pending ACK buffering │ ├── useUnreadCounts.ts # Unread counters, mentions, recent-sort timestamps │ ├── useRealtimeAppState.ts # WebSocket event application and reconnect recovery │ ├── useAppShell.ts # App-shell view state (settings/sidebar/modals/cracker) -│ ├── useAppShellProps.ts # AppShell child prop assembly + cracker create/decrypt flow │ ├── useRepeaterDashboard.ts # Repeater dashboard state (login, panes, console, retries) │ ├── useRadioControl.ts # Radio health/config state, reconnection │ ├── useAppSettings.ts # Settings, favorites, preferences migration @@ -154,7 +161,6 @@ frontend/src/ ├── useConversationMessages.test.ts ├── useConversationMessages.race.test.ts ├── useConversationNavigation.test.ts - ├── useAppShellProps.test.ts ├── useAppShell.test.ts ├── useRepeaterDashboard.test.ts ├── useContactsAndChannels.test.ts @@ -186,13 +192,13 @@ High-level state is delegated to hooks: - `useConversationRouter`: URL hash → active conversation routing - `useConversationNavigation`: search target, conversation selection reset, and info-pane state - `useConversationActions`: send/resend/trace/block handlers and channel override updates -- `useAppShellProps`: assembles the prop bundles passed into `AppShell` children, including the cracker-created-channel historical decrypt flow -- `useConversationMessages`: dedup/update helpers and pending ACK buffering -- `useConversationTimeline`: conversation switch loading, cache restore, jump-target loading, pagination, reconcile +- `useConversationMessages`: conversation switch loading, cache restore, jump-target loading, pagination, dedup/update helpers, and pending ACK buffering - `useUnreadCounts`: unread counters, mention tracking, recent-sort timestamps - `useRealtimeAppState`: typed WS event application, reconnect recovery, cache/unread coordination - `useRepeaterDashboard`: repeater dashboard state (login, pane data/retries, console, actions) +`App.tsx` intentionally still does the final `AppShell` prop assembly. That composition layer is considered acceptable here because it keeps the shell contract visible in one place and avoids a prop-bundling hook with little original logic. + `ConversationPane.tsx` owns the main active-conversation surface branching: - empty state - map view @@ -359,7 +365,7 @@ The `SearchView` component (`components/SearchView.tsx`) provides full-text sear - **State**: `targetMessageId` is shared between `useConversationNavigation` and `useConversationMessages`. When a search result is clicked, `handleNavigateToMessage` sets the target ID and switches to the target conversation. - **Same-conversation clear**: when `targetMessageId` is cleared after the target is reached, the hook preserves the around-loaded mid-history view instead of replacing it with the latest page. - **Persistence**: `SearchView` stays mounted after first open using the same `hidden` class pattern as `CrackerPanel`, preserving search state when navigating to results. -- **Jump-to-message**: `useConversationTimeline` handles optional `targetMessageId` by calling `api.getMessagesAround()` instead of the normal latest-page fetch, loading context around the target message. `MessageList` scrolls to the target via `data-message-id` attribute and applies a `message-highlight` CSS animation. +- **Jump-to-message**: `useConversationMessages` handles optional `targetMessageId` by calling `api.getMessagesAround()` instead of the normal latest-page fetch, loading context around the target message. `MessageList` scrolls to the target via `data-message-id` attribute and applies a `message-highlight` CSS animation. - **Bidirectional pagination**: After jumping mid-history, `hasNewerMessages` enables forward pagination via `fetchNewerMessages`. The scroll-to-bottom button calls `jumpToBottom` (re-fetches latest page) instead of just scrolling. - **WS message suppression**: When `hasNewerMessages` is true, incoming WS messages for the active conversation are not added to the message list (the user is viewing historical context, not the latest page). diff --git a/frontend/src/components/AGENTS_packet_visualizer.md b/frontend/src/components/visualizer/AGENTS_packet_visualizer.md similarity index 100% rename from frontend/src/components/AGENTS_packet_visualizer.md rename to frontend/src/components/visualizer/AGENTS_packet_visualizer.md