eos-simplify
Review *changed* code against EmptyOS conventions (CLAUDE.md patterns) for reuse, quality, and efficiency, then apply the fixes — the EmptyOS-aware cousin of the generic simplify skill. Use before com
SKILL.md.agent-bus/skills/eos-simplify/SKILL.md
GitHubEmptyOS Simplify
Review changed code against EmptyOS conventions, then fix what you find. This is the EmptyOS-aware cousin of the generic simplify skill: same spirit (reuse, quality, efficiency), but tuned to the patterns in CLAUDE.md so the review catches things a generic pass would miss.
Run this before committing a meaningful change, or when the user says "simplify", "clean up my changes", or "review my changes".
When to Use
- After building or modifying an app, plugin, or shared SDK/frontend code
- Before
/eos-session-wrapupor a commit - When the user says "simplify", "eos-simplify", "clean this up", "review my changes", "check my work"
Bare "review" is NOT this skill, in the body as well as the frontmatter — it names no surface, and
eos-architecture-review,eos-page-design-reviewand/code-reviewall answer to it. Ask which, or route on what the user named.
Scope
Operate only on changed files — don't rewrite the whole repo:
git diff --name-only HEAD
git diff --name-only --cached
git status --short
If nothing is changed, ask the user to scope the review (single file, single app, or a diff range).
The Checklist
Work through these in order. For each finding, fix it in place unless it's a judgment call — then flag it and ask.
1. Capabilities over raw tools (backend)
Apps MUST use capabilities, never direct tools (CLAUDE.md §Development Rules 1).
Grep the changed app.py / plugin files for:
| Bad | Good |
|---|---|
open(path), Path(...).read_text() in an app |
await self.read(path) |
requests.get, httpx.get to an LLM |
await self.think(prompt, domain=...) |
subprocess.run("grep ...") in an app |
await self.search(query) |
json.dump to a vault path |
await self.vault_create_note(...) or self.vault_write(...) |
hardcoded vault path (D:/Vault/...) |
self.vault_config("key") |
Exceptions: plugins may wrap external binaries directly; data/ SQLite/JSON writes are fine.
2. Vault data access patterns
If the app stores a collection of typed notes (songs, jobs, people…), it should use VaultLibrary (
emptyos/sdk/vault_library.py), not hand-rolledPath.glob()+ frontmatter parsing.If the app queries by frontmatter, prefer
self.vault_query(tags=..., **props)over globbing.If the change adds a vault write, confirm the path comes from
_vault-map.tomlviaself.vault_config(), not a string literal.Two-domain rule: human-authored / recovery-critical → vault; high-frequency telemetry →
data/. Flag any crossover.YAML-None crash check — grep the diff for the fragile shape:
grep -nE "\.get\([^)]+,\s*\"[^\"]*\"\)\.(lower|upper|strip|startswith|endswith|split|replace)" <changed-files>YAML
key:(empty) parses toNone, not absent.dict.get(K, "")returns the default only when the key is missing — when present-but-None, it returnsNone, and.lower()/.startswith()/.split()raisesAttributeError. Fix shape:(d.get(K) or "").method()instead ofd.get(K, "").method(). Theorchain catches bothNoneand absent. Triage hits by source:fm.get(...)/props.get(...)/p.get(...)from a YAML-parsed dict are dangerous;request.headers.get(...)/request.query_params.get(...)/os.environ.get(...)are always strings and safe. See memoryfeedback_yaml_get_method_crash.md.
3. Frontend reuse (EOS_UI / EOS helpers)
Grep the changed pages/*.html for reinvented wheels:
| Bad | Good |
|---|---|
Hand-rolled modal <div class="modal"> |
EOS_UI.modal({...}) / EOS_UI.formModal({...}) |
| Hand-rolled stat cards grid | EOS_UI.statCards([...]) |
confirm("Are you sure?") |
EOS_UI.confirm({...}) |
| Custom settings sidebar | EOS_UI.settingsPanel({...}) |
Manual location.hash juggling for detail view |
EOS_UI.hashRoute({onShow, onHide}) |
esc(path) for a vault note path |
EOS.noteActions(path) — renders view + edit + open-external |
Hardcoded D:\ or backslash paths in JS |
EOS.normPath(path) |
Inline <style> redefining button / card / donut |
reuse eos-components.css classes |
Then cross-check the changed page against docs/FRONTEND-DESIGN-LANGUAGE.md — specifically DL-1 (hardcoded hex colors), DL-2/3 (off-scale spacing/radius), DL-6 (forbidden patterns — alert/confirm/prompt, wheel UI, brand names), and DL-7 (AI-surface markers for any AI-authored content). Fix DL-1 and DL-6 in place; flag DL-2/3/7 if the call is non-mechanical. For a full-repo audit use /eos-design-system-audit.
4. Mandatory UI patterns
- Apps with
[provides.settings]in manifest must have a ⚙ Settings button usingEOS_UI.settingsPanel(.claude/rules/app-ui-patterns.md§In-App Settings Panel). If missing, add it. - Apps with a
showDetail(id)pattern must useEOS_UI.hashRouteso deep links work (CLAUDE.md §Deep-linking Detail Views). If missing, add it. - Every POST/GET list API added in this diff should have a UI surface — no backend-only features.
5. Prompts are first-class (CLAUDE.md §Development Rules 12)
For any self.think(...) or think_stream(...) in the diff:
- Prompt text must be a module-top
UPPERCASEconstant, not inline f-string - Persona/rules go in
system=kwarg; the user message carries the specific request - Content-generating prompts must include a what NOT to do section
- Temperature set explicitly: parsing 0.1–0.3, analysis 0.3–0.5, creative 0.6–0.8
- No thin prompts — user-facing output needs host persona + quality rules + structure
If a prompt is inline or missing these, refactor it to a constant and add the missing pieces.
6. Events over imports
If app A newly imports from app B, flag it. Cross-app coordination should use:
await self.call_app("otherapp", "method", ...)for request/responseawait self.emit("event:name", payload)for fire-and-forget
Same for reverse: if the diff adds a new event emitter, confirm [provides.events] emits = [...] is updated in the manifest.
7. Manifest completeness
For each changed app, verify the manifest declares what the code actually does:
- New
@web_route→[provides.web] prefix = "..."present - New
await self.emit(...)→ event listed in[provides.events].emits - New
self.call_app("X", ...)→"X"listed in[requires].apps - New capability used (
self.speak,self.draw, ...) → listed in[requires].capabilities
8. Extract-shared heuristic (CLAUDE.md §Development Rules 9)
If the diff duplicates a pattern that already exists in another app (session runner, feedback form, metrics ring, SRS scheduler, etc.), flag it. Rule of thumb: build specific first in one app; extract to sdk/ when a second app needs it. If this is the second app, propose the extraction — don't silently copy.
Cheap grep: pick a 3–5 line distinctive fragment from the new code and search other apps for near-duplicates.
Same heuristic for workflows → skills. Code-duplication isn't the only repeatable thing. If you hand-ran a multi-step workflow this session (a sequence of greps + edits + checks you walked manually, a prompt you typed near-verbatim, a process you narrated step-by-step), and you'd done that same shape before, it's a .claude/skills/<name>/SKILL.md candidate — the workflow analogue of an SDK extraction. Same trigger as code: second occurrence, not first (don't make a skill for a one-off — that just bloats the library). Same action: flag it, don't auto-build — propose "this felt repeatable; want a skill?" so the user decides. Extraction is a build step (/eos-sdk-extract for code, hand-writing a SKILL.md for workflows), not something simplify does silently.
9. Wellbeing wheel as lens, not feature (CLAUDE.md §Development Rules 16)
Refuse UI additions that expose the 8-dimension wheel to the user:
- dimension pickers / tag selects
- "which dimension does this serve?" prompts
- wheel visualisations inside an app's own page
The wheel is a silent rubric for what gets built, not what the user sees. Manifest-level dimensions = [...] is allowed; user-facing dimension UI is not. If found, remove it and explain why.
10. Branding & personal data
Run the two scripts on the diff:
python scripts/check-personal.py
python scripts/check-branding.py
- Personal: no personal paths/names/coordinates/API keys in tracked files — move to
emptyos.toml(gitignored) - Branding: no Obsidian/Suno/Kindle/etc. in app UIs, prompts, error messages — use generic terms ("markdown vault", "source URL", "Open external"). Plugin code integrating with a specific service is exempt.
Fix violations before continuing.
11. Tests for the change
- Touched an app's UI → run
pytest tests/test_sys_<app>.py -v - Touched an app's API/backend →
pytest tests/ --ignore=tests/personal -k "not test_ui" -v - Multi-app change → full
pytest tests/ --ignore=tests/personal -v - New app → ensure
tests/test_sys_<new>.pyexists with 10+ cases - Fixed a bug → add a user-story test that would have caught it (
tests/test_user_stories.py) - Any test added in this diff → prove it fails without the fix. A test that has only
ever been green is not evidence that it pins anything (
.claude/rules/audits.md§Failure mode 3). Invoke.claude/skills/eos-mutation-verify— don't re-derive the loop by hand; this checklist item exists because a simplify pass caught exactly that omission.
If tests fail, fix the code (not the test) unless the test itself encodes stale behaviour.
12. Small-thing hygiene
- Windows paths normalised (
.replace("\\", "/")orEOS.normPath()) - No comments explaining WHAT the code does — keep only WHY comments for non-obvious invariants
- No
// removedor# TODO: cleanupdebris from the edit - HTML pages hot-reload; Python changes need server restart — mention if the user needs to
restart.bat
Report Format
After the pass, output:
EOS Simplify — <N> files reviewed
Fixed in place:
- apps/foo/app.py: inlined prompt → module-top FOO_SYSTEM constant
- apps/foo/pages/index.html: replaced hand-rolled modal with EOS_UI.modal
- apps/foo/manifest.toml: added "bar" to [requires].apps
Flagged (needs your call):
- apps/foo/app.py:142 — duplicates session-runner from apps/practice/; extract to sdk/?
- apps/foo/pages/index.html — proposes a wheel-dimension picker; removed, confirm intent
Safety:
check-personal: CLEAN
check-branding: CLEAN
Tests:
test_sys_foo.py — 12 passed
Keep the report tight. Don't restate the diff; surface only what was changed by this pass and what still needs user judgment.
Safety
- Only modify files already changed in the working tree — don't drag unrelated files into the cleanup.
- Preserve behaviour — simplification, not redesign. If a fix would change app behaviour materially, flag it instead of applying it.
- Never touch
emptyos.toml,data/, or anything underapps/personal/orengines/personal/unless the user's diff already touched them.