Notechondria
Version: 0.1.52 Build Date: 2026-04-21T12:00
What's Changed
AGENTS.md §1.5: codified 1000-LOC hard ceiling
User flagged the urgent TODO that app_shell.dart grows unchecked
and introduced a rule: no code file may exceed 1000 lines.
Codified as a hard bullet under AGENTS.md §1.5 Style defaults
alongside the existing soft ~500-LOC suggestion:
Hard ceiling: 1000 LOC per code file. No code file should have any reason to exceed 1000 lines. When an existing file grows past 1000 LOC, split it in the same commit that would take it over — by extracting cohesive sub-modules (partials, helper files, or a new class), not by chopping arbitrarily at line 999. If you genuinely cannot find a clean split and must temporarily exceed 1000 LOC, document the reason in the same commit message. Line-count audits should focus on application source; do not count migrations, autogenerated code, or vendored dependencies.
Proof of pattern: recycle-bin extracted to core/local_trash.dart
All three apps had ~250 lines of duplicated recycle-bin code
(0.1.51) sitting in app_shell.dart. Moved each into a new
lib/core/local_trash.dart partial containing a single extension
on _AppShellState:
extension _AppShellLocalTrashX on _AppShellState {
Future<void> _persistLocalTrashedDrafts() async { ... }
Future<void> _moveDraftToLocalTrash(...) async { ... }
Future<ActionFeedback> _restoreTrashedDraft(...) async { ... }
Future<void> _openLocalRecycleBinDialog() async { ... }
// …
}
Dart's private-to-library scoping means the extension can touch
_localTrashedDrafts, _localDrafts, _log, _showMessage
etc. transparently. The one snag: setState is @protected, so
extensions can't call it even within the same library. Fix: tiny
void _trashRefresh() wrapper left behind on _AppShellState
that calls setState(() {}) on behalf of the extension. One line
of coupling for a clean 250-line extraction.
Per-app LOC deltas
| File | Before | After | Δ |
|---|---|---|---|
editor_app/lib/app_shell.dart | 5458 | 5211 | -247 |
planner_app/lib/app_shell.dart | 4094 | 3861 | -233 |
portal_app/lib/app_shell.dart | 3992 | 3760 | -232 |
new: editor_app/lib/core/local_trash.dart | 0 | 274 | +274 |
new: planner_app/lib/core/local_trash.dart | 0 | 249 | +249 |
new: portal_app/lib/core/local_trash.dart | 0 | 249 | +249 |
None of the three app_shell.dart files are under 1000 LOC yet
— this round is the first step, not the completion. See TODO.md
for the remaining split backlog (auth flow, sync loops, archive
export/import, sidebar categories, settings section builders,
note-editor helpers, client.dart HTTP plumbing).
Files Changed
New
docs/versions/0.1.52.md(this file).- frontend/editor_app/lib/core/local_trash.dart.
- frontend/planner_app/lib/core/local_trash.dart.
- frontend/portal_app/lib/core/local_trash.dart.
Modified
VERSION: 0.1.51 → 0.1.52.- AGENTS.md: §1.5 gains the 1000-LOC hard ceiling rule.
- frontend/editor_app/lib/main.dart,
frontend/planner_app/lib/main.dart,
frontend/portal_app/lib/main.dart:
new
part 'core/local_trash.dart';. - frontend/editor_app/lib/app_shell.dart,
frontend/planner_app/lib/app_shell.dart,
frontend/portal_app/lib/app_shell.dart:
removed the recycle-bin method bodies; added a
_trashRefreshhelper for the extension. - docs/TODO.md: Urgent file-size entry replaced with a concrete remaining-splits backlog naming each file still over 1000 LOC + the suggested extraction for each.
Verification
editor_app:flutter analyze56 issues (+1 informationaluse_string_in_part_of_directivesfor the new partial); smoke test passes.planner_app:flutter analyze70 issues (+1 same); smoke test passes.portal_app:flutter analyze68 issues (same; +1 absorbed by-1from now-referenced symbols); smoke test passes.
Notes / follow-ups
- Dart
partfiles + extension private access is the right pattern for splitting_AppShellStatewithout inheritance. Subclassing State would force a constructor rewrite and breakcreateState(); extensions avoid that entirely. client.dartresists the same pattern because extensions can't satisfy interface members. A base-class split is the only path, and it reduces each file by ~200 LOC which isn't enough on its own for the biggest clients. Flagged in TODO.md.- Cross-app duplication across the three
local_trash.dartfiles is ~250 LOC × 3 = 750 LOC of parallel code. A shared module innotechondria_sharedwould dedupe, but the extension targets_AppShellStatewhich is private per-app. Would require either (a) exposing a smallTrashHostmixin on the shared side that each_AppShellStateimplements, or (b) changing the extension to target a public interface. Not a blocker; revisit if the bin surface grows.