diff --git a/AGENTS.md b/AGENTS.md index f8a7cfe..aff87c8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -101,10 +101,18 @@ Management (IAM) fabric and WebAuthn Passkey authority. ## 5. AI-Optimized Code Organization & Engineering Principles -1. **Bounded Files & Concise Functions:** - - Target small, focused functions (4–20 lines) and keep files bounded (under - 300–500 lines) so agents can read, reason, and edit full units in a single - turn without context fragmentation. +1. **Strict 400-Line Ceiling & Concise Functions:** + - Target small, focused functions (4–20 lines) and keep all files bounded + under a **strict 400-line hard ceiling** (aim for the 150–250 line sweet + spot) so agents can read, reason, and edit full units in a single turn + without context fragmentation or output truncation. + - **Subdivide by Sub-Feature, Not Just Type:** When a feature slice grows, + subdivide into focused, SRP-aligned sub-files within the feature directory + (e.g., `login_fragments.tsx`, `register_fragments.tsx`, + `recovery_fragments.tsx` instead of one massive monolithic file). + - **No Anti-Formatting Hacks:** Never compress code onto single lines or use + blind skip annotations to bypass line-count linters. All files must pass + standard `deno fmt` and architectural linters without workarounds. 2. **Strict Single Responsibility Principle (SRP):** - Every module must do exactly one thing well. Independent modules allow agents to isolate and modify code without loading unrelated context. diff --git a/scripts/lint_arch.ts b/scripts/lint_arch.ts index 81e9e18..f25ace7 100644 --- a/scripts/lint_arch.ts +++ b/scripts/lint_arch.ts @@ -1,6 +1,13 @@ import { walk } from "jsr:@std/fs"; -const targetDir = "./src"; +const TARGET_DIR = "./src"; +const MAX_FILE_LINES = 400; + +// Auditable allowlist for exceptional files that legitimately exceed MAX_FILE_LINES. +// Every entry must be explicitly documented and approved. +const ALLOWLISTED_LARGE_FILES = new Set([ + // Currently empty: all modules in src/ must strictly conform to <= 400 lines. +]); let hasErrors = false; @@ -8,8 +15,39 @@ async function checkFile(path: string) { const content = await Deno.readTextFile(path); const lines = content.split("\n"); + // 1. Enforce strict 400-line ceiling + if (lines.length > MAX_FILE_LINES && !ALLOWLISTED_LARGE_FILES.has(path)) { + console.error( + `[Arch Lint] ❌ File size ceiling exceeded in ${path}: ${lines.length} lines (Hard Ceiling: ${MAX_FILE_LINES} lines).`, + ); + console.error( + ` -> Subdivide this module into focused, SRP-aligned component files within the feature directory (e.g. login_fragments.tsx, register_fragments.tsx).`, + ); + hasErrors = true; + } + lines.forEach((line, index) => { - // 1. Block banned DOM APIs + // 2. Block anti-formatting minification hacks (lines over 300 chars without SVG/CSS/raw string reasons) + if ( + line.length > 300 && + !line.includes(" Format code with standard 'deno fmt'. Do not minify to bypass line limits.`, + ); + hasErrors = true; + } + + // 3. Block banned DOM APIs in src/ if ( line.includes("document.getElementById") || line.includes("document.querySelector") || @@ -25,7 +63,7 @@ async function checkFile(path: string) { hasErrors = true; } - // 2. Block unescaped HTML in raw strings (basic heuristic for dangerouslySetInnerHTML) + // 4. Block unescaped HTML in raw strings (basic heuristic for dangerouslySetInnerHTML) if ( line.includes("dangerouslySetInnerHTML") && !path.includes("error_fragments.tsx") @@ -42,7 +80,7 @@ async function checkFile(path: string) { }); } -for await (const entry of walk(targetDir, { exts: [".ts", ".tsx"] })) { +for await (const entry of walk(TARGET_DIR, { exts: [".ts", ".tsx"] })) { if (entry.isFile) { await checkFile(entry.path); } @@ -51,5 +89,5 @@ for await (const entry of walk(targetDir, { exts: [".ts", ".tsx"] })) { if (hasErrors) { Deno.exit(1); } else { - console.log("✅ Architecture lint passed."); + console.log("✅ Architecture lint passed (All files <= 400 lines & clean)."); } diff --git a/src/features/admin/apps_fragments.tsx b/src/features/admin/apps_fragments.tsx new file mode 100644 index 0000000..850588a --- /dev/null +++ b/src/features/admin/apps_fragments.tsx @@ -0,0 +1,157 @@ +import { AdminLayoutFragment } from "../../shared/ui/fragments.tsx"; + +export const AdminAppsPageFragment = ({ + apps, +}: { + apps: any[]; +}) => { + return ( + +