(opus) in brackets, instead of a bare CLI default: brackets mean «nobody chose it here, and this is what
the session will run on». griglia:check prints it the same way ({agent: claude, model: (opus)}).agent_default_model / agent_default_effort (GRIGLIA_AGENT_DEFAULT_MODEL,
GRIGLIA_AGENT_DEFAULT_EFFORT, same shape as agent_models): tell the board which value the agent CLI —
or the worker, through GRIGLIA_WORKER_MODEL/GRIGLIA_WORKER_EFFORT — already starts with, and it names it
everywhere. A caption only: the board never sends it, so each worker keeps its own default. A value the
agent no longer offers is ignored.GRIGLIA_TRANSPORT now defaults to auto:
sync-context.py, sync-skills.py, claude-tokens.py, agent-status.py and the persistent worker
(--transport auto|docker|local, printed at startup) probe $GRIGLIA_CONTAINER once and, when no daemon or
no container answers, run php artisan from the project root. A machine that serves Laravel with
composer dev, Apache or nginx needs no configuration at all, instead of failing with «Cannot connect to the
Docker daemon»; GRIGLIA_TRANSPORT=docker|local still pins the choice. Every failure now prints the
transport in use and the variable that changes it.griglia:auto-archive needs, caches, assets and live updates outside a container.GRIGLIA_AGENT_MODELS="claude:opus,sonnet;codex:gpt-5", GRIGLIA_AGENT_EFFORTS="low,medium,high" —
and a list picks its default while any task overrides it, from the badge under its title or from the commands
of its modal, next to the agent. The effective value is always visible: on the row, in the modal, and next to
the title in griglia:check ({agent: claude, model: opus, effort: high}). The persistent worker reads it
from --worker-json and passes it to the CLI, falling back to its own --model/--effort. Without the two
variables nothing changes: no selector, and every session keeps the CLI's default.griglia:check --agent= and griglia:watch --agent= now match the configured
agents by key or label, in any case (Claude Code, Claude, CLAUDE → claude), and an agent nobody
configured stops the command with the list of the real ones. Before, an unrecognised text ran as itself and
the ownership guard compared it with the resolved owner, so every task was refused with a message where the
two agents looked like the same one («belongs to agent «Claude», you are «claude»»). On a board with a
single agent the option is now decoration instead of a refusal. A label stored where a key belongs
(todos.agent / checklists.agent) resolves to its agent as well, instead of falling back to the default.griglia:check and griglia:watch now cover
every list of the agent-list owner that holds a task marked open to work (or working, or waiting for an
answer). Those tasks are listed last, under 📋 List «…», after the agent list and the started plans, and
--take / --done / --ask accept their ids. Before, a green dot outside the agent list and outside a
plan was invisible to the agent: the user waited for work nobody could see. Foreign lists never show more
than what was opened for the agent, --all included, and a list with nothing open stays out of the output.python3 throughout agent instructions, installation guides, worker examples and cron snippets, so commands do not depend on executable file permissions.claude -p …, codex exec …) for scripts and cron, and a persistent worker — with what all
three need and why the working directory decides whether the agent sees the board.cd + CLI command and the first message to send, then points at the new page for the other two ways.dashboard_route path (/dashboard by default) — the very path Breeze and Jetstream give their own
dashboard. In a host application that owns it, the package route never wins and the tab framed the
host's page. The URL is now built from the package's own board route (griglia.home, falling back to
griglia.dashboard when the home route is off), which no host path can shadow, and it is exposed as
Alle80\Griglia\Support\Board::url().dashboard_route no longer governs the side tab: setting it to null drops the legacy redirect only,
and the tab keeps pointing at the board. show_dashboard_tab in /settings remains its off switch;
with no board route at all there is nothing to frame and the tab is not rendered.Alle80\Griglia\Http\Middleware\InjectBoardTab, is pushed into the web group and splices the
slide-out tab into every HTML response just before </body>, the way laravel-debugbar injects its bar:
the board is one click away from anywhere in your app, with nothing to add to your layouts. It stays out
of everything that is not a full page (JSON/AJAX, redirects, downloads, streamed and binary responses,
Livewire /livewire/update, Turbo frames, Inertia visits), out of the package pages — whose layout
prints the tab already, so it is never doubled — and out of the pages of a visitor who may not open the
board (same verdict as the route guard, now readable as GrigliaAccess::allows()).inject_tab_except: paths where the tab must not be injected, as Request::is globs matched
against the path and against the route name (default: an empty list, i.e. inject everywhere). The
show_dashboard_tab switch in /settings still turns the tab off everywhere.griglia.css nor Alpine. Its rules left resources/css/griglia.css (they
had a single consumer) and the desktop-only rule is a media query instead of the Tailwind utilities
hidden lg:block, which a host app may not have built.composer require. It opens with a
copy-paste short version, then explains the two steps that were missing: publishing config/griglia.php
with a table of the keys that matter on day one (table_prefix — to be decided before the first
migrate — user_model, mode, route_prefix, dashboard_route, agent_list, agent_name,
agents/agent_key, attachments_disk), with two ready .env blocks and the cached-configuration
gotcha, and generating the instruction files the agent reads (AGENTS.md, CLAUDE.md, GEMINI.md).vendor:publish --tag=griglia-scripts, scripts/sync-context.py --backup (originals kept in
docs/context-originals/) — and how to undo: --restore and the «Generate instruction files from the
board» switch on /context./context is named as the UI names it.checklists, todos, ingredients,
attachments, questions, context_groups and context_blocks become griglia_checklists,
griglia_todos, … so an installation no longer squats generic names in the host database. migrate
renames the existing tables in place, data and foreign keys included; nothing else to do.griglia.table_prefix (GRIGLIA_TABLE_PREFIX, default griglia_): set it to '' to keep the
historical unprefixed names. Alle80\Griglia\Support\Tables resolves every table through it, and the
models follow via the HasPrefixedTable trait.settings (spatie/laravel-settings),
notifications (Laravel) and the push subscriptions (webpush) — are deliberately not prefixed: those
libraries look their tables up through their own configuration.assertDatabaseHas('todos', …)) must use
the new names, or Tables::name('todos'). Eloquent, the components and the commands need no change.CODE_OF_CONDUCT.md at the root of the repository (where GitHub looks for it, with the
reporting address filled in and a section on what it means with a single maintainer), and a new
documentation page in English and Italian — why a standard text, where it applies and where it does not,
what is and is not a conduct problem in a review, how a report is handled and in how long, and the full
text included from the root file. README.md, CONTRIBUTING.md, GOVERNANCE.md and the Contributing and
Governance pages link it, and CodeOfConductTest guards the file, the links, the reporting contact and the
navigation entry.griglia:check, while the user guide stays focused
on connecting and using an agent.CONTRIBUTING.md at the root of the repository (where GitHub looks for it
in the issue and pull request forms) with the one-minute setup and the short version of the rules, and a
rewritten Contributing page (docs/contributing/contributing.md) in English and Italian: ways to contribute,
what to do before writing code, what a bug report and a proposal must contain, the code/tests/translations/
documentation/changelog requirements, commit and branch conventions, a pull request checklist, what CI runs
and what happens after the review, agent-written contributions, expected conduct, license and security. The
README and GOVERNANCE.md now link the root file, and ContributingTest guards the files, the links and
the essentials of the page.0.x is.Checklist, Todo, Ingredient).GOVERNANCE.md link it, and the broken LICENSE.md link in the README now points at LICENSE..html URLs), instead of producing the nonexistent /features/index/.tests badge is green again. v0.89.19 was tagged without regenerating the link block at the end
of this file, so ReleaseProcessTest failed on master and on every fresh clone — the first thing an
external contributor runs (composer qa) came back red for a reason that had nothing to do with their
change. The block is regenerated, and the release procedure now refreshes it before tagging instead of
trusting a manual step.composer qa survives being run twice. The suite leaves the Testbench skeleton without this package, so
the next composer docs:check answered There are no commands defined in the "griglia" namespace; it now
rebuilds the manifest first. The mirror image of the same state made PHPStan report three errors CI never
sees, because Larastan analyses a skeleton that registers the package: those three exceptions are now
tolerated in either state (reportUnmatched: false) instead of failing the run.Route [login] not defined (EN+IT). Installing the package into a Laravel application
that has no authentication at all and opening / answers 500, because server mode redirects to a login
route that does not exist. The page, the installation tutorial and the README requirements now name the
symptom and the two ways out: add a login flow, or GRIGLIA_MODE=local on a trusted machine.private_vulnerability_reporting is part of .github/repository.json and of repo-metadata.php, so the
private reporting channel that SECURITY.md and the issue templates point at is checked like the other
repository metadata instead of depending on a switch somebody remembered to flip.0.x minor, no
backports) and the response times to expect. A short GOVERNANCE.md in the repository root points to it,
and the contributing guide and the README link it.--help texts included. Only text changed: no behaviour, no renamed symbol. The user-visible strings stay
where they belong, in resources/lang, translated on purpose.scripts/agent-status.py sends English window labels (5 hours, 7 days) and the board translates the keys
it knows (five_hour, seven_day, seven_day_opus, seven_day_sonnet, primary, secondary), so
/agents reads the same as before in Italian while the collector stays language-neutral. An unknown key
still falls back to the label in the snapshot.scripts/sync-context.py writes an English header in the generated instruction files. It recognises the
header of older releases too, so an upgrade does not mistake a generated CLAUDE.md for a hand-written one.scripts/builtin-skills.json describes the built-in skills in English, like every skill read from disk.QualityStandardsTest fails on the first Italian word found in a comment of src/, scripts/, tests/,
the views or the stylesheets — the rule the contributing guide already stated now has a check behind it.docs/architecture.md, EN+IT): the task cycle as a Mermaid state diagram — every
arrow with the command that moves it, from --take to --approve — plus the tables behind it: what lives
in each directory of src/, which table holds what, which columns you write and which ones the agent
writes, the three chains (depends_on_id, parent_id, review_of_id), the middleware chain of a request,
the broadcast path, and the line between config/griglia.php and the settings in the database. It closes
with what is deliberately absent: no HTTP API, no machine tokens, no webhooks, no MCP.docs/roadmap.md, EN+IT) in three parts and with no dates: what already works, what comes
next (quality gate, first contact, griglia:install, zero-config access, task history, stuck tasks and
griglia:doctor, export/import with a versioned --json, file and link attachments, keyboard shortcuts,
the road to 1.0), and what is out of scope by choice — teams, PM fields, HTTP API/webhooks/MCP, chat
channels, PWA and offline, a login of its own — each with the reason behind the decision.pymdownx.superfences custom fence), documented for
contributors in Building this site.ArchitectureAndRoadmapTest: the diagram draws every state and every command that moves it, the page names
every table the migrations really create and every directory of the source, the roadmap keeps its three
answers apart and says where each closed branch stands, both pages are in the nav and in the README, and
none of their links points at a file that is not there.docs/glossary.md, EN+IT): the sub-task seen from the
code — the Ingredient model, the ingredients table, the IngredientModal component — plus a headword
for the agent and its key, and one for a style next to the theme it goes beyond. The seven states
are now a table with the same dot icons the board draws, instead of a sentence.docs/faq.md, EN+IT), grouped under Before you install, Using
the board and Working with agents: why the first griglia:check says there is nothing to do (the task
is still waiting, or it is outside the agent_list), whether the agent can keep working unattended
(persistent workers), how the board reaches you when it asks or finishes (bell, Web Push, mail and the
notify_on_* settings), and what it costs in tokens — plus the ⚡ optimization settings that lower it.FaqAndGlossaryTest: both pages exist in both languages with the same questions, the glossary defines
every word the interface uses and shows every state icon, the two pages are in the nav and in the README,
and none of their links points at a file that is not there.docs/configuration/extending.md, EN+IT,
in the nav and in the README). One working example per seam: publishing the views
(vendor:publish --tag=griglia-views, which overrides the Blade components too, file by file), publishing
the strings, adding a third board language (lang/vendor/griglia/<locale>/t.php — the selector in
Settings picks it up by itself), generic themes, a dedicated style with its own Livewire component and
its #[Layout], the settings skin (Themes::registerStyle / registerSkin), listening to
TodoChanged, the access hooks (canAccessGriglia, canManageGriglia, GRIGLIA_USER_MODEL) and taking
over the routes (register_routes).ExtensionPointsTest runs those recipes instead of trusting them: a published view really does win over
the package one, a published translation merges key by key (what you leave out keeps following the
package), a brand new locale really does show up in Locale::available() and in the settings selector,
and a registered style really does get its route and its skin.--tl-* variables grouped by what they paint, and a four-step walkthrough of the sample pack — which is now
linked (resources/themes/pollon) instead of merely mentioned, with its theme.json and theme.css shown..github/repository.json holds the description, the
homepage, the topics and the feature switches of alle80/griglia, and
.github/scripts/repo-metadata.php compares them with the live repository (--apply writes them).
Metadata that lives only in a settings page drifts unnoticed; now a diff shows it and
RepositoryMetadataTest fails when the file, composer.json and the documentation stop agreeing.docs/images/social-preview.png, 1280×640): the card GitHub shows when the
repository is shared, and the og:image of every page of the documentation site. overrides/main.html
now writes the Open Graph and Twitter card tags — title and description follow the page and the language,
so a link to the site stops rendering as a bare URL.CONTRIBUTING.md say so.composer.json gains homepage and a support block (issues, forum, source,
docs, security policy), so the package page points somewhere.composer.json keywords cover what the package is (task-board,
coding-agent, ai-agents, laravel-package, kanban).0.x version number
promises (a minor may break, a patch never does), what counts as public surface and therefore breaking,
how deprecations are announced, which versions are supported, and the four manual steps of cutting a
release before GitHub takes over. README.md, CONTRIBUTING.md, the Development, Contributing and
Upgrading pages point at it instead of restating a piece of it each..github/workflows/release.yml): pushing a
vX.Y.Z tag publishes a release whose notes are the section that version already has here, followed by a
compare link. A tag whose version has no section fails the workflow instead of publishing an empty release,
and re-running the workflow by hand on an existing tag refreshes its notes. The 210 tags released before
this one were backfilled the same way, and the two tags that never had an entry — 0.12.1 and 0.54.1 —
were written from their commits so that every tag is described here..github/scripts/changelog-notes.php — the one place that reads this file: the notes of a version
(changelog-notes.php 0.89.12), the version released before it (--previous), its date (--date), and
the whole [x.y.z]: …/compare/… block that closes the file (--links).[Unreleased]
one still compared from v0.88.11, so most version headings rendered as plain bracketed text. The block is
generated now, and ReleaseProcessTest fails when it no longer matches.CONTRIBUTING.md, the Contributing page and the Development page link it.composer qa — one command for the whole gate (lint, test, docs:check), in the order CI runs it
and with nothing but PHP and Composer required. New scripts alongside it: format (applies Pint instead of
reporting), test:coverage (needs pcov or xdebug; report in build/coverage), docs:check (the generated
reference pages still match the code) and docs:build (the strict bilingual site). Every script now carries
a description, so composer run --list explains itself..github/dependabot.yml) — grouped weekly pull requests for the development toolchain and
for the GitHub Actions, monthly ones for the front-end toolchain. Runtime constraints are deliberately
excluded: they are wide ranges the host application resolves, and narrowing one would be a breaking change.tests.yml declares a read-only token, adds workflow_dispatch, and cancels a run superseded by a new push
on the same branch — never on master, where every commit keeps a verdict of its own.reportUnmatchedIgnoredErrors: true), which is what the
documentation already claimed. Three entries that no longer matched anything were removed, one of them for a
file deleted long ago; the analysis stays green at level 5 without a baseline.QualityStandardsTest guards the setup itself: the Composer scripts and their descriptions, what qa runs,
the workflow jobs and permissions, the Dependabot ecosystems, the presence of the page in both languages and
in the navigation, and that no ignore entry excuses a file that no longer exists..github/ISSUE_TEMPLATE/ (bug report, idea or feature request, documentation
problem, question), a config.yml that disables blank issues and points a vulnerability to private
reporting and a first question to the FAQ, and a .github/PULL_REQUEST_TEMPLATE.md with the problem/what
changed/how you tested it sections and the contribution checklist. The bug form asks for the versions, the
mode and a reproduction; the idea form makes the scope and the two constraints (vendor-neutral agent
contract, AI optional and off by default) explicit before the code is written. The Contributing page in
both languages and CONTRIBUTING.md describe the forms, and IssueTemplatesTest guards their structure,
their labels and the checklist they share with the documentation..tl-page-wide, --tl-page-max) and centred beyond that: the old narrow container of /
(max-w-2xl in list view, max-w-6xl in grid view) is gone, and the grid columns multiply from a 1200px
window up everywhere, not only on the dashboard./dashboard redirects to the board — it had nothing of its own left. Old links, bookmarks and the
slide-out board tab keep working; the dashboard_route config key still names the path and still switches
the tab off when set to null. An application that disables home_route keeps the board on that path./settings,
/context, /stats, /plans and /agents.Alle80\Griglia\Livewire\DashboardTodoList — the board component (ThemedTodoList) renders the same
full-width page. Host apps that referenced the class in their own routes should use ThemedTodoList./dashboard) now uses the full width of the window instead of a centred
max-w-5xl container, and its grid view is no longer capped at three columns: from a 1200px window up the
columns auto-fill at a fixed card width (--tl-card-w, 22rem by default). Inside the narrow side tab the
ordinary responsive columns still apply (task 612)..db-status-filter) dressed like the former chips, with the
badge of the selected state — or a new filter funnel icon — beside it; seven chips no longer wrap onto their
own line on a phone or eat the toolbar of a full-width board (task 612).max-w-6xl on very wide displays instead of stopping
at max-w-3xl (task 612).Alle80\Griglia\Testing\DatabaseGuard: the package suite and vendor/bin/testbench now refuse to open a
connection to a database that is not a test database (SQLite, or a name containing test), so a run started
inside an application container can no longer drop the live schema. Escape hatch: GRIGLIA_ALLOW_PROD_DB=1.Todo, Checklist, Ingredient, and Question, resolved directly by their models
and covered by the Testbench suite (task 520).src/, without a baseline, to the standard composer lint quality gate;
framework inference exceptions are individually documented and counted (task 519)..editorconfig, and the standard composer lint and
composer test quality commands (task 518).artisan executable, and removed a visible escaped newline from the bilingual docs-site guide.canAccessGriglia() and canManageGriglia(); the former Devboard hook names are no longer called./plans page to browse, open, edit, start, pause and resume plans without crowding the lists menu.griglia:check --done atomically submits it as a separate reviewer-owned task, while tasks without a
reviewer keep the existing completion path; model and database invariants reject invalid review records (task 568).GRIGLIA_DEBUG is enabled (task 517)./<slug> routes are
removed, so /dashboard is the dashboard’s only route (task 564).\n sequences are normalized at
render time, while user-authored notes and code examples remain untouched (task 563).\n sequences are stored as real Markdown line breaks;
explicit compact result summaries normalize them to spaces instead of showing the escapes (task 563).griglia:check --take before launching the agent CLI,
closing the race where an agent/default change after polling could start the previously selected agent; a refused stale claim is logged and no CLI process is created
(task 495).config('griglia.themes'), Themes::registerTheme(), theme.json of a pack) may give
each text as a translation key, a literal or a per-locale map ({"en": "add", "it": "aggiungi"});
Themes::get() resolves them (Themes::text()), a config or runtime entry for a built-in theme overrides
it key by key, and griglia:theme-export writes the keys of a built-in theme as per-locale maps (task 516).griglia:check. Approval
atomically completes both the attempt and its original and releases plan dependants; requesting changes records the
feedback and reopens the original for its executor. Decisions are authorized, idempotent and immutable (task 570).griglia:check --pause=ID: the board shows a dedicated two-bar pause
state and filter, preserves progress, stops work-time accounting, and lets the user reopen it (task 576).id:N chip on the first level of every row, next to the state dot,
and in the modal title bar beside ‹ 3/7 › — the same notation griglia:check prints and --take/--done
expect. One tap copies the number (data-copy, handled by copy.js) (task 510).GRIGLIA_AGENTS), the toolbar shows a chip
with the robot icon next to the state filters: pick an agent to see only its tasks. The filter follows the
effective assignment (task override, then list default, then the global default), combines with the search — also
across all active lists — and the state filters, and disables drag & drop like the other filters (task 500)./settings now groups notification events, daily-summary scheduling, delivery channels and device controls
in the Notifications tab instead of splitting them between Agent and App (task 503).griglia:check --worker-json printed the stalled-plan warning after the JSON document, so the persistent
worker failed to parse the board on every poll («Extra data») as soon as a plan had work left but nothing
open: no session was dispatched and finished sessions were never reaped. The warning stays in the human
output, as it already did for --json (task 507).--adopt), and SIGHUP drains it — no new session starts, it exits when the running ones end and the
service manager restarts it with the current environment
(systemctl --user kill --signal=SIGHUP --kill-whom=main griglia-agent-worker@<key>.service). Until now a
code or environment change needed a systemctl restart that killed the sessions in progress.autonomy setting
becomes a five-step scale — autonomous agent, a few essential doubts, ask questions, ask many questions,
paranoid — each with its own rules. /settings previews the context block of the chosen level and, on save,
writes it into the agent context (/context → the generated instruction files) as a block generated from
Settings (context_blocks.key, read-only on the page, kept across griglia:context import);
griglia:check prints the same rules under the settings line (❓ question level) (task 499).agent.autonomy values: decide is migrated to autonomous, ask keeps its name; essential, many and
paranoid are new.--q and
--choices="First|Second" options; the modal shows selectable answers and always keeps free text plus
speech-to-text available (task 481).multitasking mode a worker
runs up to GRIGLIA_WORKER_MAX_PARALLEL independent agent sessions (default 2), while ordered remains
strictly serial. Each task is tracked and stopped independently; the board exposes scheduling mode through
the machine-readable worker contract (task 501).griglia:check --take no longer undoes a stop. A progress update now refuses a task stopped by the user
until it is open to work again; --force remains the deliberate override (task 477).griglia:check --done leaves no open question behind. Closing a task now clears both the question and
open-to-work flags, matching closure from the modal (task 477).griglia-agent-worker.py accepts --model/--effort
(env GRIGLIA_WORKER_MODEL, GRIGLIA_WORKER_EFFORT): the claude driver appends --model/--effort, the
codex driver --model and -c model_reasoning_effort="…", and the custom driver gets the new {model} and
{effort} placeholders. Without them nothing changes — each CLI keeps its own default. Documented in
«Persistent workers», EN + IT (task 475).claude -p --permission-mode acceptEdits, while the script has used bypassPermissions for a while (task 475).griglia:docs-build: half a toolchain now says so. With mkdocs and Material installed but
mkdocs-static-i18n missing, the command died on MkDocs' own The "i18n" plugin is not installed, which
says nothing about how to repair it. The python3 -m mkdocs probe now also requires mkdocs_static_i18n,
and a build that stops on a missing plugin prints the line that fixes it (pip install -r requirements-docs.txt, or --docker). Documented in «Building this documentation site», EN + IT (task 457).devboard names in the code. The three warning logs (Notify, Plan, SettingsPage) were
still prefixed devboard: instead of griglia:, the TodoList listeners PHPDoc named the config key
devboard.broadcast_channel (it is griglia.broadcast_channel), the test push notification used the tag
devboard-test and the consolidated migration header still called the package agent-devboard (task 456).TodoChanged payload — the events reference described the channel, its
authorisation and Event::listen(), but not what actually reaches the client. It now lists every field sent
by broadcastWith() (checklist_id, todo_id, title, state, source, deleted, state_changed) with
the possible values of state (done, question, working, otw, waiting), and shows how to listen
with Echo in both modes and from a Livewire component via Mode::echoListener() (EN + IT, task 454).griglia:docs-generate: one description per config key. The comment above a block of keys was reused
for every key of that block, so agents and agent_key — and assets, vite_entries, assets_url —
read as if they did the same thing. A comment now describes only the key right below it, config/griglia.php
gives each of those keys its own, and a key left with no comment is reported as a warning (an error under
griglia:docs-build --strict). Reference pages regenerated, EN + IT (task 455)./griglia-sw.js only handles push and
notificationclick: no fetch handler, no offline cache. The Mobile row (EN + IT) now describes what is
really there — a layout made for a thumb, camera attachments, Web Push — and links straight to the
Mobile section of «Using the board» (task 453)./dashboard (griglia.dashboard_route), the
DashboardTodoList component, the slide-out side tab and its two settings (show_dashboard_tab,
tab_side) only existed in the generated reference tables, and the README's route list did not mention
the route at all. «Using the board» (EN + IT) now has a Desktop: the dashboard section describing the
wider view, the theme fallback, the resizable tab and how to switch it off; the feature overview and the
README route list link to it. A test pins the two tab settings (task 452).notify_on_done / notify_on_question govern the board's notifications too — they read
as instructions for the agent alone, while Notify::todoCompleted() / Notify::questionAsked() also use
them to decide whether the bell, the Web Push and the mail go out, and the notifications page claimed the
two layers were «independent». The help of the two settings (EN + IT, hence the generated settings
reference) now says it is one switch for both layers, and the tip of the notifications page (EN + IT) says
which code reads it. Behaviour unchanged; a test pins the question side of it (task 451).griglia.default_list_name is alive again — the config key was documented but dead: the first list of a
new user always took its name from griglia::t.default_list, so changing the key did nothing.
Checklist::defaultName() now prefers the configured name and falls back to the translation when the key is
empty, which is the new default (GRIGLIA_DEFAULT_LIST_NAME): both behaviours keep working, and a fresh
installation can name its first list — the agent list, for instance — before the first visit. The config
comment, the generated config reference and the Quickstart (EN + IT) say so (task 450).dev. It does not: the first visit creates «My list» / «La mia lista», so
griglia:check, the very first command an agent runs, stops with No list named "dev". Step 1 of the
Quickstart (EN + IT) now names the list you actually get and gives the two ways to get an agent list —
rename it from the lists menu, or point GRIGLIA_AGENT_LIST / griglia.agent_list at a list you already
have (plus config:clear). The installation page says the same thing and links to the step (task 449).canAccessGriglia()
and canManageGriglia(). Since v0.34.0 the code looks for those first and only falls back to the
pre-rename canAccessDevboard() / canManageDevboard(), but README, SECURITY.md, the access,
installation, security and config-reference pages (EN + IT) still taught the old names, so a new
installation would write the wrong method. The config/griglia.php comments and the Italian reference
translations were corrected too (the generated reference pages come from there), and the example Gate
ability is now 'access-griglia'. AdminTest and ModeTest now cover the current names, the fallback
and the precedence between them (task 448).8.5rem. The select now sits on a full-width row under the
commands (.modal-cmds-agent), so on a phone the header stacks in three rows — state with ‹ 3/7 › and
the close button, the agent, then move/archive/delete.$wire.set(): the
field is flushed and finish*() is called in the same tick, so Livewire pools them into one round trip
(updates first, then the call) whatever set() returns.Enter (the
title), Esc or a click outside it, and as soon as the text differs from the starting one a back to the
previous version button (↩, new undo icon) appears next to the «Saved» flag: it restores the value the
field had when the editor was opened and stays inside the editor. Sub-tasks, which are not saved live, keep
their ✓ and ✕.TodoList::saveEdit() → finishEdit() and cancelEdit() →
revertEdit(); IngredientModal::saveTitle()/saveNotes() → finishTitle()/finishNotes() and
cancelTitle()/cancelNotes() → revertTitle()/revertNotes(). Custom views that call them must be updated.revert and msg.reverted, autosave_hint also mentions how to close the editor; the
now unused save_title, msg.renamed and msg.note_saved are gone (closing an editor no longer toasts:
with a click outside it would fire at every stray click)./settings selects (and the number, text and time fields) save again when you change them (task 436).
Only the switches were still saving: with Livewire 4 the wire:model.change modifier alone updates the value
in the browser without sending it to the server, so updatedValues() never ran and the old value stayed in
the database — you changed «Git» to «commit on main», left the page and found «branch + PR» again. The fields
are now bound with wire:model.live.change.Esc) puts back the text that was
there when the editing started. It works in the modal (title and note) and in the inline rename of a row./settings (task 432). The App group opens with «Board
language»: as in the application (the app.locale config, i.e. APP_LOCALE), English or Italiano —
the languages the board is translated into, read from resources/lang (the published
lang/vendor/griglia counts too, so a host application that adds a language sees it in the list).
The choice is applied by the new SetLocale middleware to every board page and, being a persistent
Livewire middleware, to the /livewire/update requests of modals and saves; Carbon follows, so «3 hours
ago» is translated as well. Choosing a language redraws /settings already translated.Alle80\Griglia\Support\Locale — available languages, their names, the options of the selector and
apply(). With no choice («as in the application») it touches nothing: a host application that sets the
locale by itself keeps deciding.MediaRecorder running that nobody would ever stop, upload or transcribe. Five minutes of talking, no
text and no error. The dictation session now lives in the module, outside Alpine: the button re-adopts it
after every re-render and keeps the resolver of the target field fresh, so the transcript is written into
the field that is on the page now and not into a detached node. Other silent deaths fixed with it: an
empty recording used to return without a word (now it says so), a failed upload threw the audio away
(now it is kept and retried — by tapping the mic, or by itself when you come back to the tab), an expired
session was reported as a generic failure, and errors erased themselves after four seconds.griglia.speech_max_seconds (GRIGLIA_SPEECH_MAX_SECONDS, default 300, 0 = no limit): a dictation is
closed and transcribed when it reaches the limit, instead of growing until the upload or the provider
refuses it.Alle80\Griglia\Support\Speech::frontend() builds window.GRIGLIA_SPEECH (mode, endpoint, limit and
every label the button needs): the JS reports failures on its own and cannot read the translations.mic_retry|empty|silent|denied|lost|expired|kept|recovered|limit; CSS .db-mic-warn,
.db-mic-time. Failed transcriptions are logged with size and mime, to tell a provider failure from a
recording the browser botched.page.it.md (mkdocs-static-i18n, suffix structure): English at /, Italian at /it/, a language
switcher in the header that lands on the same page, an Italian navigation, and a fallback to English for
any page that has no translation yet. Every page of the site is translated.docs/contributing/translations.md — how a translated page works, how to write one (links, anchors, front
matter), how the generated pages get their Italian, and how to add a third language.resources/docs/reference.it.php — Italian catalogue of the strings the reference pages take from the
code (command and option descriptions, comments of config/griglia.php), keyed by the English source.docs.Dockerfile — the documentation toolchain as an image (Material plus the plugins of
requirements-docs.txt), built on the fly by griglia:docs-build --docker: the official
squidfunk/mkdocs-material image does not ship the i18n plugin.griglia:docs-generate writes each reference page in both languages (commands.md and commands.it.md,
…). The settings page needs no catalogue: its labels and help come from the it translations of
/settings. Strings with no translation stay in English and are listed at the end of the run.griglia:docs-build --docker now passes --strict when asked to, instead of silently dropping it, and
builds the toolchain image before running.navigation.instant theme feature is off: the i18n language switcher cannot point at the same page in
the other language while instant loading is on.requirements-docs.txt gained mkdocs-static-i18n; the install hints of the troubleshooting page and of
griglia:docs-build now point at the file instead of pip install mkdocs-material.12rem) and the row icons keep their space. The gesture does not change:
picking an agent assigns the task, the empty option gives it back to the list default, and the name stays
visible either way.Predefinito (Claude Co…) — because the command block
carried ml-auto. State badge and ‹ 3/7 › now sit on the left edge, agent/move/archive/delete/close on
the right, and from md — where the panel is wide enough — the state badge shows its name (Working,
Open to work…) and the agent select fits its full label. Narrower than that nothing grows: the icon
speaks for the state and, on a phone, the two-line layout of 0.69.0 is untouched.<br> elements.griglia:check died with Undefined variable $me (the multi-agent
guard kept its call sites but lost its definitions), the coloured attention border went back to its
pre-0.72.0 shape and the outcome chip removed in 0.72.0 came back. This release ships the source the
test suite is green on.--take, --done and --ask acted on any id: a stale id in a prompt,
or a worker started with the wrong key, could steal, close or pause work another agent was doing. The three
actions now refuse a task owned by somebody else (--force is the deliberate way in), griglia:check
prints a 🔒 busy elsewhere line listing what the other agents have in progress, and the "new since the
last check" 🆕 baseline is stored per agent key instead of being consumed by whoever checks first. The new
documentation page Two agents at once maps what is left outside the board — checkouts, branches, asset
builds, migrations, releases, container-wide commands — with the rule for each.Todo::resumeChain()
walks the whole ancestry (cycle-safe, 20 steps at most) and it is used everywhere: griglia:check prints
every previous step newest first (resumes «…», 2 steps back «…», …) with its note, agent answer and
sub-tasks, griglia:check --json carries the same history in the new resume_chain field, and the task
modal lists every step inside the (still collapsed) context box, with a +N earlier counter..db-attention / .db-att-* rules of this stylesheet, but a host app runs the package's views from
vendor/ while its CSS comes from a build of its own: when the two copies are on different versions the
rules are missing — or are an older version of themselves — and the highlight is simply not there. That
is why it looked broken three releases in a row (tasks 397, 402, 406). The row now writes the colour on
itself (Todo::attentionColor(), inline border-color/border-width/opacity/filter/outline), so
the border survives a stale stylesheet and the grey filter a theme puts on completed rows. The
stylesheet keeps the pulse and the --db-att variable for themes..db-attention-badge,
.db-unseen-badge and .db-outcome* rules and the outcome_alert / outcome_blocked strings. New
strings result_question / result_question_hint describe a row with open questions.attachments_disk now defaults to Laravel's local disk and
all internal fallbacks match it. Keep GRIGLIA_ATTACHMENTS_VIA_CONTROLLER=true; the owner-scoped controller
is the only package route that serves these objects. The upgrade guide covers existing public files.griglia:docs-generate --check is deterministic..tl-done also applies --tl-done-filter, which is grayscale(1) on the slate theme, so the green,
yellow, red and violet all reached the eye grey — the highlight looked like it was not working at all.ok inherited the active theme’s
accent colour, which could make the outline yellow, cyan, or indistinguishable from the ordinary card border.nav (state badge, ‹ position ›) and tools (agent, move, archive, delete) — and below
640px nav stays on the first line beside the close button while tools wraps to a second one, with
36px touch targets. The agent selector truncates instead of pushing the row out, and the close button
finally carries a title/aria-label.griglia:check --done --summary.griglia:check --done now takes --outcome=ok|alert|blocked (default ok), stored in
the new todos.outcome column: green outline for a plain result, yellow when it needs a look, red when
something is in the way, violet while the agent is waiting for answers (--ask). Yellow and red add a
badge next to the title and a chip in the modal, above the agent's answer. Opening the task clears the
highlight as before, and a task the user closes has no outcome at all. griglia:check prints the flag on
closed tasks it lists, and taking a task again clears the previous outcome.db-attention + db-att-<level> (ok, alert, blocked,
question) and the badge is db-attention-badge; the colour comes from the --db-att variable. The old
db-unseen / db-unseen-badge selectors keep working for themes that styled them.sync-skills.py, sync-context.py, claude-tokens.py
and agent-status.py used to call docker exec unconditionally, so a persistent worker on the local
transport could poll the board but not count tokens or synchronize context. They now share the worker's
choice: GRIGLIA_TRANSPORT=docker|local (default docker) and GRIGLIA_PHP for the local executable,
which runs Artisan from the project root. The worker falls back to the same variables when its own
GRIGLIA_WORKER_TRANSPORT / GRIGLIA_WORKER_PHP / GRIGLIA_WORKER_CONTAINER are unset, so one
machine-wide setting configures everything.claude-tokens.py ignored GRIGLIA_CONTAINER and always queried the container named laravel-dev-app.GRIGLIA_WORKER_TRANSPORT=docker|local; local installations can override GRIGLIA_WORKER_PHP.
Codex, Claude and custom agent drivers remain available with either transport.griglia-scripts publishes a host worker with
built-in Codex CLI and Claude Code drivers, a shell-free custom argv driver for other agents, per-agent locks,
retry and Stop handling, plus a portable systemd user-service template. The new guide covers installation,
PATH configuration, lingering across logout/reboot, smoke testing and multi-agent instances.griglia:watch --agent=<key> applies the same effective-agent scope as griglia:check, including task
overrides and list defaults, so supervised multi-agent workers do not wake for each other's jobs.griglia:watch --once prints tasks that were already waiting on its first snapshot; use --no-initial
for baseline-only monitoring. This makes one-shot polling safe across worker restarts.sync-skills.py, sync-context.py, claude-tokens.py and
agent-status.py — the scripts that fill the skill catalogue, write the agent context back to CLAUDE.md /
AGENTS.md, count the tokens of a task and read the agent's plan — used to live only in the origin repository,
so the docs kept pointing at a repo you do not have. They are now part of the package and land in your project
with php artisan vendor:publish --tag=griglia-scripts; they find the project root on their own (or take it
from GRIGLIA_PROJECT_ROOT), so they also work run straight from vendor/alle80/griglia/scripts.
New documentation page: «Host scripts».agents in the import JSON, filled by scripts/sync-skills.py from the folder it comes
from), and the task modal only offers the ones the agent of that task really has: no more ticking a Claude Code
built-in for a Codex CLI task. Skills with no agents — the shared ~/.agents/skills folder, or catalogues
imported before this release — stay available to everybody, and a skill already ticked remains visible so it can
be removed./clear, which only you can type. The context is re-read at every turn, so a heavy session makes every
single step more expensive — not just the long ones.griglia:check shouted at plans nobody had started yet, which are not stuck —
they are waiting for ▶. It now talks only about plans that were started (or paused) and have nothing the
agent may take.3/7) open the previous and the next task, and the ← → keys do the same when you are not
typing. Following a plan step by step no longer means closing the modal after every task.griglia:check still prints it in full for the agent./plans/new (goal, dictation, optional name, agent, draft)
and /plans/{list}/edit (save the goal, or rebuild only the tasks nobody has started). The route list in
the README gained /plans/new.griglia:check --take refuses a completed task for the same reason (it reopened it in 0.55.0).--take on a completed task reopens it. The agent taking a task back left the row saying «done» while
it was being worked on; the command now clears the completed state (and any stale question) and says so.griglia:check --json, where it broke
whatever was parsing it.griglia:check warns when a plan still has work but nothing is open to work, and says how to get out
(never in --json, which stays machine-readable).?v=…): browsers keep a favicon for days, so after the logo
changed the old one stayed in the tab even after a forced reload. A 32×32 PNG is declared next to the
SVG for the browsers that ignore vector favicons./plans/{list}/edit, reachable from the Plan bar: change the goal (and the name and
agent of the list) without touching the tasks, or rebuild them — and only the ones nobody has started.
Tasks already done, taken by the agent or waiting for an answer are never replaced, and the confirmation
says how many will be.#33d17a for headings and links, thin green borders with the 6px radius of the cards) in
dark, its light counterpart on paper, and the header/footer in the colour of the board's chrome. The
landing hero and the feature cards follow the same tokens./plans/new, reachable from the lists menu («New plan…»). Describing a
goal used to mean typing a paragraph into a four-row textarea inside a dropdown that closed on any click
outside, taking the text with it. Now there is room to write, the name of the list is optional (the first
words of the goal become the name), Ctrl/⌘+Enter builds, and the wait for the AI has a page to live in.
The page wears the theme skin like /settings and /context.assets now defaults to precompiled: the
CSS and JS shipped with the package are used as they are, and they are published under Laravel's own
laravel-assets tag — which a default app republishes after every composer update, so an upgrade
cannot leave a stale build behind. composer require + migrate + storage:link and the board works.GRIGLIA_ASSETS=vite in .env
(or 'assets' => 'vite' in config/griglia.php) before updating: the default changed under you.
Everyone else gets the precompiled build and needs nothing.--ask help text and the agent_name config comment lost their emoji too, so the generated
reference pages stay clean..github/workflows/docs.yml at every push to master that touches the docs. The test workflow now also
fails when the generated reference pages are stale (and prints the diff), and it finally runs on master
instead of a main branch that does not exist.griglia:docs-generate lists the options of a setting only when they come from the translations: the AI
providers and the installed themes depend on the installation, so they cannot live in a page that must be
identical everywhere.config/devboard.php was still mentioned in one of them.griglia:docs-generate --check explains that it is meant for the package repository: inside a host app
the settings page legitimately differs (it lists the AI providers installed there).griglia:docs-generate now always writes the settings page in the English base language with a neutral
agent name, so --check does not depend on the locale (or the agent) of whoever runs it.griglia:docs-generate. The reference pages of the documentation site are written from the code:
every griglia:* command with its options, every key of config/griglia.php with its env variable and
default, every setting of the three groups with label, type and help. griglia:docs-build runs it before
building (--no-generate skips it) and --check fails when the committed pages are out of date, so the
docs cannot drift from the package.CHANGELOG.md. The hand-written inventory keeps only what does not exist yet (backlog).audio/webm;codecs=opus (or audio/ogg;codecs=opus); the codec parameter made the
provider receive the file named audio.mp3 and answer «Audio file might be corrupted or unsupported»,
so dictation was broken in Chrome and Firefox. The mime type is now normalised (and derived from the
extension when the browser sends none).it. Beyond accessibility, browser-mode
dictation reads it to choose the recognition language.GRIGLIA_SPEECH_PROMPT / config('griglia.speech_prompt'), disabled with an empty string.mkdocs.yml ready to publish on GitHub Pages (site_url, theme overrides,
tabbed/details extensions). mkdocs build --strict is green.xl:columns-2): each column is read top to bottom, so related settings stay together — «Riepilogo
serale» and «Ora del riepilogo» no longer end up in different columns as they did with the grid. And
from xl every non-toggle control (selects, time and text inputs) sits under its label at the full
column width, instead of squeezing beside it: long options such as «Branch per task + Pull Request» or
«Task ordinati — uno alla volta, in ordine» are readable in full.xl instead of lg. At 1024 px the columns were
~340 px wide and every label wrapped after two words; and three columns at 2xl were worse, so the
page stays at two columns and widens to 90rem instead. Selects get a minimum width and can take up
to 65% of the row, so options like «Chiedi quando in dubbio» are readable.<aside> that is display: contents
below xl (so they keep taking part in the grid) and a single cell from xl — this removes the empty
gap that the implicit grid rows left between them. The stacked order on phones is preserved
(chart → history → overview) with flex order./stats and /settings widen to 90rem on 2xl screens.--db-panel-h), and
from xl the per-list overview shortens (.db-panel-overview) so the right column ends at the same
height as the history table beside it. Each panel is the only scroll container in its card — no nested
or double scrollbars — and the page keeps its own scrollbar as the single vertical one./context and /agents follow the same shell as the other pages (lg:max-w-5xl), instead of staying
at max-w-3xl on wide screens.scroll-padding-top: 6rem on html, so in-page anchors do not land under the fixed top bar.lg the page splits into a sticky index on the left
(one entry per group — agent, optimization, app, notifications, themes — with the number of settings)
and a single panel on the right, so the 35 settings no longer stack into one very long column. Below
lg the index is hidden and every group stays stacked exactly as before. New .tl-btn-on marks the
selected entry.lg the daily chart and the per-list overview sit side by
side with the history below them (from xl the previous three-column split still applies), and the two
long tables scroll inside their own card (.db-panel-scroll, capped at min(62vh, 38rem)) with a
sticky header (.db-sticky-head) instead of stretching the page — the history has no row limit, so its
height used to grow with the data. Phones and tablets are untouched: both rules start at lg.#0F1720) on transparent,
and GitHub serves SVGs as <img>, so currentColor cannot help. Added lockup-horizontal-dark.svg /
lockup-stacked-dark.svg (wordmark in #E6EDF3) and the README header now picks one with a <picture>
media="(prefers-color-scheme: dark)".max-w-xl and
max-w-3xl) with no breakpoint above sm, so a fullscreen desktop showed a tall strip between two
empty margins. Now the shell widens (lg:max-w-5xl, xl:max-w-7xl) and the content spreads out:
settings rows flow in two columns from lg (three from 2xl), and from xl the stats page puts the
history table on the left (two grid columns) with the daily chart and the per-list overview stacked on
the right. Below lg/xl nothing changes — phones and tablets keep the layout they had.checklists.archived_at (migration included).Checklist::mineWithArchived() and Checklist::mineArchived() alongside mine(), which now returns
active lists only. Archived lists are skipped by griglia:check and griglia:watch when they look for
plan lists.griglia:watch now lists the items already open to work when it starts (🟢 OPEN TO WORK (already waiting)). Before, the first snapshot was only a baseline: a monitor started after the user flagged a
task never announced it, and the agent sat idle. --no-initial restores the old behaviour.slate no longer says "todo").<body> covering the page.
State and overlay now live on the section itself, outside the re-rendered block.height: 100dvh,
which some browsers do not recalculate when the virtual keyboard resizes the viewport; it now fills
.modal-shell (height: 100%) and the header is sticky.env(safe-area-inset-top) on notched phones..tl-btn (+ .tl-btn-sm, .tl-btn-icon, .tl-btn-ghost, .tl-btn-danger), .tl-menu,
.tl-menu-item, .tl-menu-label, .tl-menu-sep and .tl-meter. Themes can fine-tune them with
--tl-chrome-bg, --tl-chrome-hover, --tl-menu-bg (set for slate)..tl-meter), the same device used per list inside the
switcher, so header and menu read as one system.done/total count on the button itself.Style menu (component x-griglia::style-switcher) is gone: the style
is chosen in /settings (app.default_style). Themes stay reachable by their own routes.composer require alle80/griglia -W (Web Push pulls web-token/jwt-library, which caps
brick/math at ^0.17 while a fresh Laravel app ships 0.18), plus a note explaining why.Everything that carried the old name has been renamed. Nothing else changed: no logic, no database schema, no settings values — an existing installation keeps its data.
alle80/agent-devboard → alle80/griglia (the old package is abandoned and points here).alle80/griglia (old URLs redirect); docs, changelog links and the MkDocs
site follow.Alle80\Devboard\* → Alle80\Griglia\*; the service provider is
Alle80\Griglia\GrigliaServiceProvider, the middleware GrigliaAccess / GrigliaAdmin, the notification
base class GrigliaNotification.devboard:* → griglia:* (griglia:check, griglia:watch, griglia:context,
griglia:empty-trash, griglia:theme-import/export, griglia:skills-import,
griglia:agent-status-import, griglia:auto-archive, griglia:describe-images, griglia:docs-build).config/devboard.php → config/griglia.php, keys read as config('griglia.*'); env
variables DEVBOARD_* → GRIGLIA_*.devboard:: → griglia::
(<x-griglia::icon>, <livewire:griglia::todo-list>, __('griglia::t.…')).devboard-config|views|lang|assets|agents → griglia-*; published assets live in
public/vendor/griglia, the standalone build files are griglia.css / griglia.js./devboard/... → /griglia/... (attachments, transcribe, push subscriptions), service worker
/griglia-sw.js, theme assets /griglia-themes/...; route names griglia.*.canAccessGriglia() / canManageGriglia(). Compatibility: the old
canAccessDevboard() / canManageDevboard() are still honoured when the new ones are absent.window.grigliaPush, window.grigliaMic, window.GRIGLIA_ECHO|PUSH|SPEECH.Upgrading (only needed if you had the old package): composer remove alle80/agent-devboard +
composer require alle80/griglia; rename config/devboard.php to config/griglia.php and your DEVBOARD_*
env keys to GRIGLIA_*; re-publish the assets (--tag=griglia-assets) and update the import paths of
resources/css/griglia.css / resources/js/griglia.js; replace devboard::/x-devboard:: with
griglia::/x-griglia:: in any published views; use griglia:* in scripts and cron entries.
:focus-visible) uses the theme accent (--tl-accent) instead of a fixed blue,
so it no longer clashes on dark themes (blue fallback kept where no theme is active).docs/images/state-*.svg),
and everything from “Compatibility” on is restructured into sections with bullet lists and copyable
command blocks; install/routes notes aligned with the current access middleware and pages.interactive-widget=resizes-content (the keyboard shrinks 100dvh instead of
overlaying the modal) and, as a safety net, a focused field inside the modal body is scrolled into view
once the keyboard has settled.deleted_at and the statistics
(time, tokens, costs, history) survive the deletion. Trashed lists stay selectable on /stats
(marked "(deleted)"); the board, menus and CLI never show trashed items. A blank untouched new task is
still dropped for real on close.griglia:empty-trash {--days=N} {--dry-run}: permanently purges soft-deleted lists/tasks (attachment
files included) — that is when their statistics disappear.griglia:check --take=ID left progress
at null unless --progress was passed, so a working todo showed the spinning icon but no N%.
Now --take always shows a percentage: the given --progress, else the current value, else 0%.
Re-running --take=ID --progress=N updates it (live via Reverb); --done still clears it.griglia:check prints [N%] after the title of a working todo and — N% when taking in charge./settings now shows/hides instantly when the mode changes
(Alpine x-show), instead of waiting for a server re-render.task_mode (agent group): ordered = one task at a time in list order (default),
multitasking = the agent may take several 🟢 tasks at once if independent. Shown in the settings
summary printed by griglia:check, with an inline warning in /settings for multitasking.griglia:check --take=ID --progress=N (0–100) shows N% next to the
title and a thin progress bar under the row; --done clears it. New todos.progress column
(dedicated migration for existing installs).griglia:check --done), the row stays
highlighted (pulsing accent outline + "result" badge) until the user opens it; opening clears it
(live too). New todos.result_seen column (dedicated migration for existing installs).wire:key, so it was re-created (losing its open state) when the list re-rendered
after adding the new row. Added the key.open-ingredients to the child modal, and that server-side dispatch was lost when
the list re-rendered. The modal now creates-and-opens the task itself via a client dispatch
(open-new-task), so it opens reliably.<x-griglia::md-editor> — bold, italic, code, code block, list, quote, link, table, separator)
and safe rendering — GitHub-flavoured (tables, task lists, autolinks), with raw HTML stripped and
unsafe links blocked, via league/commonmark (Alle80\Griglia\Support\Markdown). The agent's
comment is rendered as Markdown too.league/commonmark ^2.4.('griglia::livewire.partials.modal-actions') string instead of
rendering the state badge + commands (a malformed [@include](https://github.com/include) insertion in 0.6.0).import(), which resolves
after Livewire wires its echo-private listeners (a race that reliably lost on slower / mobile
connections): window.Echo wasn't set yet, so the private channel was never subscribed and no state
changes arrived. Echo is now imported synchronously (no-op without a broadcaster key), so the
subscription happens before Livewire initialises.<x-griglia::icon name="…">) in the logo (slate) line style.app.tab_side (added in 0.5.0) is now seeded by its own settings migration, so installs that had
already run the initial settings migration get it on php artisan migrate (fresh installs were fine).config('griglia.dashboard_route'), default /dashboard) — more readable on large screens.prefers-reduced-motion.tab_side (right / left) in /settings, and config key dashboard_route.griglia:watch — a portable monitor for a coding agent: watches the agent list and prints
only the changes to react to (an item going open to work, answers to a paused question arriving,
a stop being requested). One command replaces harness-specific monitors. --interval, --list,
--once.AGENTS.md shipped with the package and publishable with php artisan vendor:publish --tag=griglia-agents — the full agent protocol (states, take-first, order, questions, stop, close),
so "connect an agent" = launch it in the project directory + read AGENTS.md + one griglia:watch.config('todolist.agent_list'),
todolist.broadcast_channel); corrected to devboard.*.<x-griglia::logo> component,
currentColor, so it follows the theme palette) instead of the theme icon/emoji; theme icons remain in
the style switcher, settings and modal.public/images/brand/ (mark in color / currentColor /
black / white, rounded-square app icons light/dark, horizontal and stacked lockups, PNG 16–512) published
with the griglia-assets tag. The themed layout now falls back to the brand mark (+ apple-touch icon)
when the theme has no icon_img; Web Push notifications carry the mark as system icon; the MkDocs site
and the README use the logo. Colours: Agent Green #16A34A (the existing accent), Devboard Ink #0F172A./context, setting
app.context_sync, griglia:context enabled for host scripts). When off, the host sync restores the original
files and leaves them alone (the origin repo's sync-context.py keeps the originals in docs/context-originals/
and offers --restore / --backup).devboard.agents (GRIGLIA_AGENTS="claude:Claude Code,codex:Codex CLI") declares
the active agents; each list (project) has a default agent (selector in the toolbar) and each task may
override it (selector in the modal header, chip on the row). griglia:check --agent=<key> (default:
GRIGLIA_AGENT_KEY / first configured) lists only that agent's tasks and prints {agent: key} per row; a
single configured agent keeps today's behaviour. Alle80\Griglia\Agent::all()/effective(), columns
checklists.agent, todos.agent.docs/ (installation, usage, agent side, plans, notifications, context,
skills, statistics & agents status, themes, configuration, security, development) with mkdocs.yml (Material for
MkDocs) and the command griglia:docs-build (--out, --serve, --docker, --strict) that builds the
static HTML site, with clear errors when MkDocs is missing or the build fails.SECURITY.md (reporting, security model, hardening checklist) and a Security section in the README.public/images/slate/slate.svg) — a terminal window with a green >_ prompt, drawn in the
theme's palette — wired as its icon_img (shown in the style switcher and as the corner motif).vendor/ untracked, compose parametrised
(APP_URL, BACKUP_DIR), personal paths/domains scrubbed from README/scripts/.env.example, root LICENSE (MIT),
third-party artwork removed./devboard/attachments/{id}, only users of that list;
nosniff + sandbox CSP), so attachments_disk can be a private disk; attachments_via_controller=false
restores direct public URLs.devboard.push_allowed_hosts,
wildcards; empty = any https host) — closes the blind SSRF.devboard.rate_limits: transcribe 10/min, notification test
5/min, push subscriptions 30/min; per-route buckets).theme.css is sanitised on install
([@import](https://github.com/import), external url()/src()/image-set(), non-image data:, expression(), behavior,
-moz-binding removed — relative urls and inline images kept); extraction limits (5 MB per file, 20 MB per
pack, 200 entries) checked on the declared size before inflating; icon_img must be inside the pack; theme
assets are served with X-Content-Type-Options: nosniff and a sandboxing Content-Security-Policy./settings and /context (settings, agent context, theme packs) are now restricted to
administrators — Alle80\Griglia\Admin: canManageDevboard(): bool on the user model, else Gate
devboard.admin_gate, else devboard.admins (ids/e-mails, GRIGLIA_ADMINS), else the first registered
user only; middleware GrigliaAdmin (also persistent on Livewire updates) + defensive boot() in the
components; menu links hidden to non-admins. Local mode: everybody.local environment
or devboard.allow_local_from_ui is on (a stale local override in the settings is ignored too).docs/config-and-settings.md: inventory of current configurations and settings, template, and the prioritised
backlog of future ones (with implementation notes)./agents, link in the lists menu): plan and usage windows of the coding agents — used %,
remaining %, progress bar with ok/high/almost exhausted/over-the-limit levels, reset countdown, $ limits and
extra usage when exposed, «updated … / stale» meta; empty, not-configured and error states; auto-refresh
every minute. Data come from a snapshot imported with griglia:agent-status-import (the origin repo ships
scripts/agent-status.py: it reads the Claude Code OAuth credentials on the host and sends only
percentages). Alle80\Griglia\Support\AgentStatus, Livewire AgentsPage, config agent_status_file./stats: the list selector also offers All lists and All plans (aggregated history with the list
name on each row).checklists.plan_paused stops the chain from opening the next task) and «Resume the plan» when paused /
stopped (clears the pause and opens the next not-started task). Icon pause./stats on phones: full-width list selector, history as cards (title, date, time/tokens/cost), at most
60 bars in the per-day chart; title without icon./stats, link in the lists menu): per list (project) — KPIs (completed, working
time with average, tokens, cost from a price list), per-day bars, the history of completed tasks
(date, time, lead time, tokens in/out, cost, sub-tasks/questions/resumed-from) and an overview of all
lists; period 7/30/90/365 days or all. Alle80\Griglia\Support\Stats (history/aggregate/series/
overview/cost), Livewire StatsPage.todos.completed_at kept by the model (set when completed, cleared when reopened; migration backfills
existing completed items from updated_at).cost_per_m_in, cost_per_m_out, cost_currency — price list used to turn tokens into
cost (0 = cost not shown).griglia:check and griglia:watch now cover the agent list
plus the owner's plan lists (built from a prompt / chained tasks); started plan tasks are listed after
the agent list under 📐 Plan «name», and --take/--done/--ask accept their ids. Starting a plan = the
agent works it, following the chain.griglia:check --take=ID --progress=N --phase="writing code" stores a short text of what
the agent is doing (todos.phase, cleared on done/ask) shown next to the % in the row and in the modal,
and printed by griglia:check ([45% · writing code]).TodoList::startPlan(), planStatus(); icon play./settings: permission, service worker, subscription on this device (push host),
opened as PWA/browser, devices registered on the server; «Show a local notification» (no network) and a
live log that confirms when a server push actually reaches the device (the service worker posts a message
to open pages). Helps telling apart OS-level blocking from delivery problems./context cards reorganised for phones: commands (handle, switch, select-all/rename/delete, chevron) on
the top row, title + stats wrapping below on their own line; block rows likewise (commands row, then the
text full width); smaller title fonts.image, camera,
lock, chart, puzzle, link, clock, tasks.agent_name is still used in the 🤖 comment box.user,
logout, ruler, list./settings (and the back link of /context) use the SVG icon set instead of emoji: page title, section
titles (agent / optimization / app / notifications / themes), device state, buttons. New icons: settings,
bot, bolt, board, bell, bell-off, palette, alert, send, package, arrow-left.devboard.agent_name (GRIGLIA_AGENT_NAME, default «Agent») used by the UI
labels (🤖 box, skills, questions, settings, context); Alle80\Griglia\Agent::name(). The Italian
strings no longer hard-code «Claude». AGENTS.md/README: compatibility table for Claude Code, Codex CLI,
Gemini CLI and other CLI agents (instructions file per agent, skills folders, token stats).GrigliaAccess is registered as a Livewire persistent middleware: since 0.14.0 it replaces auth on
the package routes, but Livewire re-applies only persistent middleware on /livewire/update, so component
actions (settings, context, lists) were not re-checked for authentication/access on update requests.PlanBuilder (structured output, default provider) into ordered tasks with notes and sub-tasks,
chained with the new todos.depends_on_id — the first one is left for the user to start, each next
one opens 🟢 automatically when the previous is completed (model hook). Chain shown in the row (⛓), in the
modal and in griglia:check. checklists.plan_prompt keeps the prompt. Without an AI provider the list
gets a single «Build the plan» task with the prompt (for the agent). Alle80\Griglia\Support\Plan
(fakeable via Plan::$resolver).linux becomes slate, the CSS class
.theme-linux becomes .theme-slate, and config('griglia.default_theme') defaults to slate.
The Tux image (public/images/linux/tux.svg) and its terminal-flavoured copy were removed./linux, default_theme/default_style = linux,
a .theme-linux { … } override, or griglia:theme-export linux), rename it to slate.IngredientModal::moveTo(),
icon move.IngredientModal::setState().Laravel\Ai\Transcription, provider
ai.default_for_transcription): the microphone button records with MediaRecorder and uploads the clip to
POST /devboard/transcribe; the text comes back and is appended to the field. New app.speech_mode
setting: auto (default: server when the SDK + a provider key are configured, else the browser's Web
Speech API), server, browser. Alle80\Griglia\Support\Speech, TranscribeController,
window.GRIGLIA_SPEECH. The mic shows busy/error states.no-speech)
and stops when the page goes to the background.<x-griglia::mic>, browser Web Speech API, no server needed)
in the Markdown editor toolbar (note, sub-tasks, context blocks), next to the task title field and in the
insert-title form; what you say is appended to the field (language = page locale). Hidden when the browser
has no speech recognition. Icon mic./context: the block editor spans the full width of the row (was squeezed on phones)./context: blocks are edited with the Markdown editor (toolbar + auto-growing textarea)./context uses the SVG icon set everywhere (grip handles, edit/trash, select-all, enable/disable,
chevron, title/tokens) instead of emoji. New icons: check, check-all, ban, chevron, grip,
book, coins./context, link 📚 in the lists menu): the agent's instructions file is
imported as groups (## sections) and blocks (bullets / paragraphs / ### sub-sections; fenced
code stays whole) — php artisan griglia:context import --file=… [--replace]. Each group and each block
has a switch; blocks can be multi-selected (per block or whole group) and enabled/disabled together;
blocks can be edited, added, deleted and reordered (drag), groups renamed/added/deleted/reordered.
Token estimate per block/group/total. griglia:context export prints the enabled context as markdown
(a host script writes it to the file), griglia:context status the summary. Models ContextGroup,
ContextBlock, support Alle80\Griglia\Support\Context.Alle80\Griglia\Mode): config devboard.mode (GRIGLIA_MODE, default server),
overridable from /settings (app.mode, '' = follow the config, with a warning for local).
canAccessDevboard(): bool on
the user model if defined, else the Gate ability in devboard.access_gate if set, else any logged-in
user (403 otherwise). Enforced by the new GrigliaAccess middleware, which also plays the role of
auth (redirect to login) — 'auth' in devboard.middleware is no longer needed and is ignored.griglia.local_channel); no bell / push / logout in the UI. For a board on your own machine.app.show_dashboard_tab to show/hide the slide-out DASHBOARD tab.devboard.middleware is now ['web'].--done) or asks
a question (--ask), on three switchable channels (app settings notify_in_app / notify_webpush /
notify_mail; the events follow the existing notify_on_done / notify_on_question):
NotificationBell, database channel, live via the same
private broadcast channel): unread badge, list, click → opens the task (switching list if needed),
«mark all as read».laravel-notification-channels/webpush (new dependency): service
worker served at /griglia-sw.js, subscription endpoints POST/DELETE /devboard/push-subscriptions,
«Enable Web Push on this device» + «Send me a test notification» in /settings. Needs VAPID keys
(php artisan webpush:vapid) and the HasPushSubscriptions trait on the user model.toMail) when a mailer is configured.?list=ID&open=ID (middleware OpenFromLink) open a task from a notification.notifications and push_subscriptions when the host app lacks them.Alle80\Griglia\Support\Notify, notifications TodoCompleted, QuestionAsked, TestNotification.createNew(?int $position)).griglia:skills-import from a JSON list of {name, description, source} — file or stdin — into
config('griglia.skills_file'), default storage/app/griglia/skills.json) is shown in the modal, under
the Task note, as a 🧩 accordion of checkboxes; the chosen ones are saved in todos.skills (JSON) and
griglia:check prints 🧩 skills to activate for this task: … so the agent invokes them. Read-only on
completed items. Dedicated migration for existing installs. Alle80\Griglia\Support\Skills.OptimizationSettings, group optimization) — switches that make the
agent spend fewer tokens, printed by griglia:check as ⚡ optimization: …:
compact_check (default on: action calls --take/--done/--ask/--progress print only the result line,
no settings/listing), terse_agent (prints TERSE MODE ON + the rules the agent must follow: no chat
prose, batched commands, targeted reads, short commits/comments), context_max_chars (trims previous
context in the command output; 0 = unlimited), progress_piggyback (progress updates only together with
other commands), token_report (report tokens on close). Settings migration for existing installs.--take to --done/--ask/a user stop; time spent waiting for answers is not counted) into
todos.work_seconds (+ working_since for the open interval). Tokens are reported by the agent with
the new griglia:check --tokens-in=N --tokens-out=N options (with --take/--done/--ask;
cumulative per todo, todos.tokens_in / tokens_out). Partially completed items keep their stats.griglia:check prints ⏱ working since … (Xm this interval) on working items and 📊 … on
completed ones / when closing. Dedicated migration for existing installs.Todo::workSeconds(), hasStats(), statsLine(), formatDuration(), formatTokens().First public release, extracted from the laravel-dev monorepo into a standalone, installable Composer package.
TodoList, IngredientModal, Checklist).ImageStore) and optional
AI descriptions for full-text search (Laravel AI SDK, any provider, no-op without keys).TodoChanged
event on a private per-user channel, with console-vs-web source tracking and toasts.spatie/laravel-settings): an agent group (how the assistant works) and
an app group (board behaviour), read by the griglia:check command.ThemeStore, griglia:theme-import/-export).griglia:check (alias sviluppo:check), griglia:auto-archive,
griglia:describe-images, griglia:theme-import, griglia:theme-export.public/build/devboard.{css,js} plus an Echo chunk, selectable via <x-griglia::assets />
between [@vite](https://github.com/vite) (bundled by the host app) and the precompiled files (GRIGLIA_ASSETS=precompiled);
Echo configured at runtime from config('griglia.echo').How can I help you explore Laravel packages today?