Public AI agent contract for morph_kit — read by Cursor, OpenAI Codex, Aider, Devin, JetBrains Junie, and any AI tool that follows the agents.md convention.
Claude Code reads the deeper workspace config at
whuppi/.claude/rules/andwhuppi/.claude/memory/automatically — this AGENTS.md exists for every other AI tool.Stamped from
whuppi/AGENTS.template.md. Per-repo content lives in the placeholder sections; everything else is identical workspace-wide.
morph_kit is adaptive layout widgets for Flutter — ListDetailLayout (list + selected detail: slide-over with swipe-to-dismiss on phones, covering nav bars via an always-mounted OverlayPortal; side-by-side with a draggable, anchor-snapping divider on wide windows) and AdaptiveSplit (two always-present panes). Pane widget instances survive the compact ↔ expanded morph (GlobalKey reparenting), and a paint-visibility probe suppresses the overlay detail of inactive kept-alive tabs. Router-agnostic and state-management-agnostic — a ChangeNotifier controller is the whole integration surface.
This repo is one tool inside the whuppi workspace — a multi-tool monorepo. The workspace ships shared engineering standards, code conventions, brand identity, and build patterns that apply across every tool. They're documented in three layers:
- Repo-specific architecture, design, reference:
./docs/ - Workspace human-readable standards:
../docs/(when this repo is cloned as part of the whuppi workspace) — engineering principles, decision frameworks, secret/CI patterns - Workspace AI-only directives:
../.claude/rules/(Claude Code reads these automatically; other AI tools can read them as supplementary context)
If you're working on this tool standalone (cloned outside the workspace), the in-repo ./docs/ is your authority; ignore the workspace pointers.
Run these after every code change. A failing test or analyzer error means the task is not done — don't suppress with // ignore:, # noqa, or --no-verify. Fix the underlying issue.
# Setup (one-time)
make hooks
fvm install && fvm flutter pub get
# Full gate
make check # lint-shell + analyze (pkg + example) + analyze-floor + platforms +
# test (widget suite, host VM) + test-example (journeys)Match the style of existing code in this repo first. Workspace-wide standards live at:
- Engineering standards (seven questions before every decision, env-blind code, twelve-factor checklist):
../docs/universal/development-standards.md - Secrets and environments (GitHub Environments, branch=env, security walls, files-not-env-vars):
../docs/universal/secrets-and-environments.md - Python tools (SDK/CLI/MCP three-layer pattern, ruff config, hatchling):
../.claude/rules/python-shared/sdk-cli-mcp-pattern.md - Flutter packages (opaque boundaries, async at edges, dependency flow):
../.claude/rules/flutter-shared/package-design.md - Comments and doc-comments (what earns a comment, what doesn't):
../.claude/rules/universal/comments.md - Renaming anything (sweep all references in one session):
../.claude/rules/universal/rename-hygiene.md
When in doubt, read existing code in this repo and match it. Per-repo style consistency beats general-best-practice consistency.
- The overlay invariants are sacred (docs/UPDATING.md §1): the OverlayPortal is shown once and never hidden (the overlay child collapses instead — toggling trips the controller's layout-phase assertion); the paint probe evaluates during layout and reports during paint with a deferred re-show; suppression needs a rebuild pass of the inactive child. Don't "fix" any of this with timers or polling.
- Panes mount under stable GlobalKeys — that reparenting is the state-preservation guarantee across the compact ↔ expanded morph. Anything that changes a pane's tree position must keep the same key wrapping the same builder output.
- Core never imports components — divider / empty-state params are nullable; apps pass a shipped component or their own.
- Pane width math lives ONLY in
PaneWidthModel(shared by both widgets) — never inline resize/clamp/snap logic in a widget. - example/ is the integration harness — a full dummy app with nested kept-alive tab routers, URL sync, and overlay-mode list-details everywhere. Package changes must keep its journeys green (
make test-example).
This repo's .gitignore is stamped from ../.gitignore.template (workspace canonical). It already covers:
data/.envand every other.envflavor (only.env.example/.env.template/.env.sampleare committed)data/auth/(captured tokens, cookies, OAuth credentials)data/db/*.sqlite*(full app state — irreplaceable)cookies*.json,*.token,*.pem,*.keyoutput/,debug/,logs/,cache/
Never commit a sensitive file even if it's somehow not gitignored — surface to the maintainer instead. The gitignore is defense-in-depth, not the only check.
- Run the test suite before claiming completion. Always.
- Don't add
TODOcomments as a substitute for fixing things. If you found it, you own it — fix in this pass or surface to the maintainer. - Don't add backwards-compat shims for code that hasn't shipped. Code assumes the latest schema and contracts; migrations handle old data once.
- Don't refactor "for cleanliness" without a stated reason. Surface the suggestion before changing surrounding code.
- No co-authored-by AI in commits. The maintainer is the author.
- Never force-push protected branches (
prod,main,dev). Never skip pre-commit hooks.
For the engineering philosophy that informs every line of code in this workspace, see ../.claude/rules/universal/dc-engineering-philosophy.md if available.
This file is stamped from whuppi/AGENTS.template.md. The placeholder sections ({{...}}) are the only parts customized per repo. Re-stamping refreshes the shared content; per-repo placeholders are preserved.