personal memory agent

feat(convey): tear down the Jinja shell; guard against its return master

The SPA conversion (L1–L6) flipped all 22 owner apps to the static shell (convey/static/shell.html). This capstone removes the now-dead Jinja container and makes shell-serving unconditional: - Delete the unrendered Jinja shell: templates/{app.html, menu_bar, date_nav, status_pane, diagnostic_console, chat_bar}.html. Only the static, construct-free init.html remains under convey/templates/. - Retire the transitional `spa` app flag end-to-end: drop the App dataclass field, its parse, all 22 app.json entries, and the /api/shell payload key. The injected index route now always serves shell.html and the workspace fragment route serves unconditionally; workspace_url is non-null for every app. - Remove the orphaned App.get_workspace_template (get_background_template is retained and unchanged). - Add an AST-based CI guard to scripts/check_api_conventions.py: flag any flask render_template(...) call under solstone/apps + solstone/convey (excluding tests), resolved by import binding so the local provider_readiness._render_template helper is not matched. The two PDF routes (news, reflections) are the only allowlisted call sites. A construct-free guard asserts convey/templates/ carries no Jinja markers. Executable negative controls prove both guards fire. No app's rendered output changes — this is dead-code removal plus an anti-regression guard. Docs (CONVEY-FRONTEND.md, APPS.md, CONVEY.md) updated to retire the flag and note the guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>