mbs047/model-mind
Model Mind is a Laravel package that generates an interactive mind map of your Eloquent models and their relationships. Visually explore your domain model, spot missing or unexpected connections, and understand complex schemas without digging through code.
composer require the package, then run php artisan model-mind:install. As of v1.0.15 this publishes public assets by default — you no longer need a separate vendor:publish step for the widget CSS/JS. Run migrations if you haven't already.sync, after_response, queue). This is the single most important knob in this release.public/vendor/... (or wherever the installer published) to confirm the widget .css / .js actually landed and is web-accessible.after_response mode those side effects land slightly after the response returns, so check the DB a second later rather than in the same request.Set the mode explicitly per environment on day one:
// config/model-mind.php (conceptually)
'background' => [
'mode' => env('MODEL_MIND_BACKGROUND_MODE', 'after_response'),
],
MODEL_MIND_BACKGROUND_MODE=sync # local + test suites
MODEL_MIND_BACKGROUND_MODE=after_response # default prod
MODEL_MIND_BACKGROUND_MODE=queue # prod with workers/Horizon
Pick the mode by environment, not by taste.
| Mode | Use for | Trade-off |
|---|---|---|
sync |
Local dev, CI, feature tests | Deterministic, but all memory/summary/analytics work is back on the response path — this is the latency v1.0.15 removed. Never ship it to production. |
after_response |
Default for typical apps | Cheap to operate (no worker required), but the work is best-effort. |
queue |
Production with real traffic, Horizon/workers already running | Most robust, but requires a working worker and gives you a new class of silent failure. |
Treat async side effects as eventually consistent. Summaries, compaction, and analytics are no longer guaranteed to exist when the HTTP response returns. Any UI or report that reads them should tolerate "not yet written" instead of assuming presence. If a feature genuinely needs a summary immediately (e.g. showing "what we discussed" on the next page load), read it defensively and fall back to the raw conversation.
Instrument the learning loops deliberately. Assistant-answer learning and liked-answer learning are driven by feedback signals; action-click analytics are driven by your action wiring. These only pay off if you actually emit the signals — make sure thumbs up/down (or equivalent) and action links are hooked up, otherwise the jobs run against empty input and you get cost with no benefit. Use the extension events/hooks to attach custom signals rather than patching package internals.
Use current page context for scoped answers. Passing the current page/URL context is the cheapest quality win for support- and docs-style widgets — the assistant can answer "what am I looking at" questions without you building retrieval for it. Pass the smallest useful context (route name, entity ID, page title), not the whole rendered DOM; it ends up in job payloads.
Prefer hooks over forks. Register listeners on the package's extension events for analytics enrichment, redaction, or routing decisions. This survives upgrades; subclassing internals does not.
Test with sync + queue assertions. Run the suite in sync for behavioural assertions, and add a small number of tests in queue mode with Queue::fake() / Bus::fake() asserting the compaction, learning, and analytics jobs are dispatched. That combination catches both "wrong behaviour" and "job silently not dispatched."
Serve assets like any other front-end asset. The widget is now plain public .css/.js, which means it goes through your normal pipeline: cache headers, CDN, versioning, deploy steps. Bake asset publishing into your deploy script rather than relying on someone remembering to run the installer.
Breaking change — inline CSS/JS Blade views are gone. If you published and edited the package's inline <style>/<script> views, or @included them from your layout, those overrides no longer apply. Re-publish the assets and move customisations into your own stylesheet/script loaded after the package's, or into the documented extension points. This is the item most likely to bite on upgrade — grep your views for package includes before deploying.
after_response is best-effort, not guaranteed. It depends on the request lifecycle finishing the after-response work. Long-running workers (Octane), certain SAPIs, hard timeouts, or a process killed right after the response can drop the work. Nothing in the chat response will look wrong — you'll just quietly lose summaries and analytics. If the data matters, use queue.
Queue mode fails silently if no worker is running. Chat keeps working; memory, compaction, learning, and analytics just never happen. Add a check for pending/failed jobs and alert on failed_jobs growth — otherwise you'll discover this weeks later when the assistant "forgot" everything.
Upgrades need the publish step re-run. model-mind:install publishing public assets by default helps new installs, but existing deployments still need to publish after upgrading or the widget will 404 its CSS/JS. Many deploy pipelines don't run package publishes — make it explicit.
Cache-bust the widget assets. Public .css/.js files aren't content-hashed by default, so browsers and CDNs will happily serve the previous version after an upgrade. Append a version query string or purge the CDN on deploy; "the widget still behaves like the old release" is almost always this.
CSP implications. Moving from inline styles/scripts to external files changes your policy requirements: you now need script-src/style-src to allow the asset origin, and any nonce/hash strategy you had for the inline blocks is obsolete. Conversely, this is better for CSP long-term — just update the header in the same deploy.
Run php artisan config:cache after changing the mode. With a cached config, flipping the env var appears to do nothing, which sends people down the wrong debugging path.
Retries can duplicate analytics. If your queue connection retries, analytics jobs may write more than once. Make counters tolerant (or keyed/idempotent) before you build dashboards on them.
Don't depend on compaction ordering. Conversation compaction and answer-learning jobs are independent; a learning job shouldn't assume the freshly compacted summary exists. Read state defensively inside anything you add to that pipeline.
Debugging recipe: reproduce the issue with sync to see the failure inline, then switch back to queue and inspect Horizon/failed_jobs for payload and serialization problems (page context and conversation models must serialize cleanly — keep context payloads small).
How can I help you explore Laravel packages today?