# Changelog All notable changes to Kronn are documented in this file. Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Release notes for 0.9.3 and earlier are available in the [legacy archive](docs/releases/CHANGELOG-legacy.md). --- ## [Unreleased] ### Added - Media: an image or a video can be generated from several source images of the discussion, ordered and removable in the Creator. Each saved image/video model (ComfyUI included) has a reference limit in Config: empty uses what the provider advertises, zero turns references off, a number declares an unknown limit or lowers the advertised one. The Creator, `agent_list` and the generation preflight read the same capability. Local ComfyUI takes sources through SDXL img2img (1), FLUX.1 schnell + Redux (3), FLUX.2 Klein 4B distilled (4) and Wan 2.1 VACE (3), and checks the nodes and model files each workflow needs before queueing. A source image must start like a PNG, JPEG, WebP or GIF, and one request's sources stay under 64 MB together (KT-1403, #265). - A workflow Agent step on an HTTP or local agent (Ollama, LiteLLM, OpenRouter…) can now generate an image or a video with `media_generate` and follow the job with `media_job_status`, as in a discussion: same validation, same per-request billing guard, same connection resolution. The asset lands in a room opened for the run (listed with it) and, once the job completes, as a file in the run's artifacts directory (`asset.artifact_path`) that a later step can read. The step waits with `wait_secs` instead of being woken. Every other task or orchestration tool stays refused in a workflow step (KT-1605, #502). - Reference images work in a workflow step: a picture of the run's rooms or the project's discussions (a brand image a launch variable names), an earlier step's generation by job id or artifact path, or a picture file inside the project. Nothing of another run or project is reachable. A run is also capped at 12 generations (`KRONN_WORKFLOW_MEDIA_JOBS_PER_RUN`, at most 100), on top of six per step, and a dry-run Test no longer bills. A reference that matches nothing gets the one scope refusal (KT-1605, #502). ### Security - rusqlite 0.41 bundles libsqlite3-sys 0.39, past the vulnerable SQLite of RUSTSEC-2026-0459 (KT-1620). - The copy of a generated asset into a run's artifacts directory is written through the directory's file descriptor, so a link an Exec step planted at the asset's name is replaced, never written through. A press of a step's Test button is its own scope for the media guard, never shared across workflows or projects (KT-1605, #502). - A project file used as a reference is read through directory fds: a link on its path is refused, a FIFO cannot block the open, and it must be a picture by its first bytes, not by its extension (KT-1605, #502). - Every copy of `config.toml` (migration backup, snapshots, the set-aside of an unreadable config, a retired key) is encrypted with the instance key, clear copies left by earlier versions are encrypted at the next start, and the set-aside never follows a link or replaces an existing file (KT-1528). - Writes guarded by `fs_guard` (docs migration, templates, audits, skills, worktrees) resolve through directory file descriptors with `O_NOFOLLOW`, so a parent folder swapped for a link after the check never lets a write leave the project; Windows keeps the check-then-write by path (KT-1595). ### Fixed - Windows: Kronn could not start after an upgrade with a pending migration ("Access denied (os error 5)"), and scheduled backups failed. The backup copy was flushed through a read-only handle, which Windows refuses; it is now opened for writing first, and the Windows CI job runs the test (KT-1625, #509). - An interrupted task execution no longer stays stuck on "recovery decision already consumed": it can always resume or deliver, and a resume never consumes the decision of a newer interruption (KT-1537, #492). ## [0.15.2] - 2026-11-08 ### Added - Trust levels: say "I trust my agents here" at the scope you choose, with the risks spelled out, instead of approving every step. Three levels (Standard, Trusted, Full access) set for the instance (Settings › Trust levels), a workflow or a room; the narrowest explicit setting wins. Under Trusted, an agent-written Quick Exec or workflow Exec step that passes values to a CLI is active at once, a room's request to read or plan in another of your projects is granted, and integration validations may reach Docker; Full access also runs validations without the sandbox. Choosing a level opens a risk panel listing exactly what changes and needs an explicit confirmation; an active level shows a badge on the room, the workflow and the settings, revocable in one click, and every approval it grants is journalled ("granted by trust level X"). Everything a level approves runs under an OS sandbox (macOS Seatbelt, Linux bubblewrap) that hides Kronn's own data (database, encryption key, recovery key, configuration, backups) from those commands and leaves the network, the API port and the rest of your machine open, as in your terminal; a host with no sandbox refuses Trusted and Full access with the reason. Raising a level needs a code from an authenticator app (TOTP, any app; ten single-use recovery codes; enrolled in Settings › Security), so a command an agent approved cannot raise its own level, even from this machine; lowering never needs it. Lowering a level takes back the approvals it granted and the journal lists them. The risk panel says what stays reachable (project databases, the network, Docker, other repositories, the credentials of other tools) and what stays closed. The journal keeps 10 000 rows or 180 days. No level lets an agent change a trust setting, skip a delete confirmation, or lends its trust to a P2P peer, and a room that edits a workflow never takes that workflow's level. Elevation cards keep refuse / this conversation / this project and now point to the global setting. Code write on other projects and host access for agents are in the policy table for Full access; their gates ship with their own tickets (KT-1492, builds on KT-1490, KT-1474, KT-1433). - The Page header shows the HTML weight: green up to 1 MB, orange up to 4 MB, red above, with advice on how to optimize. The Pages list badges heavy Pages, and a heavy write returns a notice the agent receives as `kronn_notice` (#252). - The review-round limit is configurable and reaches every launch path (KT-1345). Settings > Workers sets the default (5 out of the box, or Unlimited with a token-burn warning), readable over `GET /api/config/workers` and, for MCP principals, as `default_max_review_rounds` in `task_exec_prepare`. The campaign dialog is pre-filled with it, single-task MCP launches take an optional `max_review_rounds` (integer or `"unlimited"`; before, they were stuck on 3), and workflow-delegated subtasks use it when the step sets none. An unknown MCP argument is now refused instead of silently ignored, and an existing execution's limit is raised with `task_exec_reassign({extend_review_rounds})` (or the execution card's « Extend rounds »), which also re-offers the findings of an execution the limit had escalated. With unlimited rounds the campaign's `token_budget` stays authoritative: once its consumed tokens reach it, a `request_changes` escalates with `token_budget_exhausted` instead of scheduling another correction, a worker reassignment is refused, and an extension re-offers nothing. See `docs/operations/worker-review-rounds.md`. - A room can be granted `code` on another registered project, where Claude Code edits that project's checkout like its own (KT-1491, #411). The levels are now `read`, `plan` (what `write` always was: Kronn's planning tools, never the files) and `code`; a stored `write` is read as `plan` (migration 285). A `code` grant never opens credentials or anything that runs later: secrets stay closed at every level, `.git`, the hooks path, `.claude`, `.envrc` and agent configs are denied, and a checkout that holds the Kronn data directory is refused. `code` is enforced for Claude Code only, and full-access agents are told so. Granting it needs the strong confirmation when enrolled; revoking it, or lowering the trust level, stops the room's running turn. - Kronn checks the live database schema against its migrations at boot (KT-1483). A missing table, index or trigger is recreated (empty), a missing column is added back when SQLite allows it, and the rest is logged and listed under `schema` in `GET /api/health` (`ok`, `repaired` or `degraded`). See `docs/gotchas/schema-drift-boot-check.md`. - The prompt limit follows the target model's context window instead of a fixed 100 KB, under a configurable ceiling (`server.max_prompt_bytes`, 4 MB by default), on create, send and revise. The refusal names the limit and the model and suggests attaching a file or a larger-context model (KT-1529). - Discussion and Artifact pages share one full-height side panel with a vertical rail (KT-1532). Their headers share one shell: ids back next to the title, a primary action and « Détails » pair, the Artifact details as a side-panel tab, Sync in the Invite slot and a live « Sync the N workflows » count (KT-1530, KT-1344). The discussion panel has a single close button and waits instead of opening under the mobile sidebar. - The Automation list has a « Désactivés » filter, exclusive with « Actifs » (KT-1264). ### Changed - An agent can now save a Quick Exec or an Exec line that passes run values to a program Kronn does not model (`fastly purge --key {{surrogate_key}}`): it is stored inactive, the save response says a human activates it, and the agent carries on instead of stopping. The run keeps refusing it until then. Shells, paths, wrappers and interpolation inside inline code are still refused at save. A human activates it with one click on the room's card, in « À valider », on a Page or discussion action, or on the workflow's readiness list; the notice shows the exact line and sends exactly that line, so a line an agent rewrote in between is refused and shown again; each argument is shown quoted, so two different argument lists never read alike; the activation checks and writes the line in one atomic step and never overwrites a concurrent edit. « Refuser » is kept server-side (migration 277). Activation and refusal are the human's only: a bridge token, or a request carrying the MCP bridge's `X-Kronn-Agent` marker (a CLI launched from the host, whose room is restored before its first save after a reconnect), cannot do either, and that marker now also keeps the other human-only routes and the Quick API access policy from treating such a CLI as a person (KT-1490). - Live Pages no longer refuse HTML above 1 MB. A Page may weigh what its author wants; a 24 MiB safety guard only stops a runaway writer, and create, update and import apply the same rule. Artifact bundles go from 16 to 64 MiB and the bridge buffer of Kronn-launched agents from 32 to 64 MiB, so a Page at the guard still exports, imports and reaches agents. The MCP bridge keeps its 8 MiB request limit, so an MCP agent writes a Page of up to about 8 MiB in one call (#252). - Above 4 MiB, the HTML editor and the revision diff show plain text instead of highlighting every line on each keystroke (#252). - A Page's revision history no longer carries every revision's HTML; the revision you compare is loaded alone (#252). - The backend container accepts any host user or group id from 1 to 4294967294 (it used to fall back to 1000 above nine digits). - The backend container starts as root only to give its `kronn` user your `KRONN_HOST_UID` and `KRONN_HOST_GID` and to hand its volumes to that user, then runs the backend and every agent as that user, never as root. The image is built once for everyone instead of once per user. `docker compose exec` into the backend now needs `-u kronn` to get the same user as the agents. - A bare `docker compose exec backend ...` now opens a root shell (root's home, root-owned files): use `docker compose exec -u kronn backend ...` to act as Kronn. - `make start` on a checkout runs the published image of `VERSION`, not your local edits: use `make start-build` to run local code. - CI: the macOS portability leg runs on pull requests only when the tested modules change, and on every push to main, with a 150-minute limit (KT-1535, KT-1533). Every `backend/scripts/ci/test_*.py` must be wired in a workflow, and the MCP catalogue census test now runs (KT-1180, #286). ### Fixed - A cut Exec stdout no longer reaches an agent silently (KT-1570). Past its limit, a workflow Exec step's stdout ends with `[TRUNCATED: kept X KB of Y KB — full output: ]`, the step summary carries a warning, and the whole stdout (scrubbed, up to 8 MiB) is kept in the run's artifacts directory as `{{steps..data.stdout_file}}`. Collectors, Quick Exec runs and stderr keep the old suffix and write no file. - Agents can read run artifacts again (KT-1585). The run artifacts directory lived in the data directory, which the vault protection (KT-1495) hides from every agent, so no step could open a screenshot or report it was given. It now lives under `KRONN_RUN_ARTIFACTS_DIR` or the platform cache directory, in a `kronn-run-artifacts/` directory of Kronn's own; a base inside or containing the data directory is refused. Docker uses a new `kronn-run-artifacts` volume. Existing live run directories are moved at startup, dead ones deleted. Any Agent step can still read any run's artifacts; per-run confinement is KT-1587. - A task worker can edit its project's own `.cargo/audit.toml` and `.cargo/config.toml`, and a refused tool call is no longer silent (KT-1541). Claude Code's built-in sensitive-directory check asks on any path under `.cargo`, even in `acceptEdits`, and a `--print` worker cannot answer: the Edit came back refused with no reason. The worker's settings now answer that one ask, for the worktree's own `.cargo` only, through a script that refuses any path going through a symlink or leaving the worktree (the Kronn data directory stays denied). A refused tool call is written into the task worker's transcript with its reason, the "worker ended without a delivery" notice repeats the last refusals of the current attempt, and so does the delivery summary of a worker that delivers anyway. See `docs/gotchas/claude-code-sensitive-directories.md`. - A quota refusal no longer sticks to an execution after the reset, and no longer sends it to another provider behind the user's back (KT-1564, regression of KT-838 / KT-593). A refusal that announced its reset expires at that instant: the watchdog hands the execution back to the same provider, tier, model and profile and the escalation closes, unless its campaign is paused or a person moved it first. Falling back to another provider is now its own explicit opt-in (`quota_fallback` on the campaign, a toggle in the launch dialog; `quota_fallback_agents` on `task_exec_launch`), keeps the execution's tier, returns to the original worker after the reset even when the fallback provider resets first, and shows in the review card and the delivery report. **Migration 286 turns off the fallback that campaigns used to get implicitly from `allowed_agents`**: re-enable it per campaign with `quota_fallback`. - One request no longer starts a cascade of media generations, and Stop is final (KT-1481). Every agent generation is tied to the human message at the base of its wake chain: a result or failure notification now says it is a notification, not a new request; after a failed, timed-out or cancelled job nothing more is generated for that request (no retry, no other provider, no dropped references — KT-1420); a request may start at most six jobs; and a notification only leads to a new generation that builds on its result. Stop disarms the callbacks of running generations in the database, so a late result cannot wake the stopped agent, restart included. A new human message is a new request with a fresh allowance. - A task is no longer marked `done` when its delivery is integrated into a campaign or feature branch that has not reached the default branch yet (KT-1519). It stays `in_progress`, integrated and awaiting the merge; the planning page, the board and the discussion plan say « Intégrée dans — en attente de fusion ». The next task of the campaign still launches on top of it, and a campaign whose tasks are all integrated completes. The task closes when the default branch holds the integrated commit (local or `origin/`, which the sweep fetches first), when GitHub reports the pull request that merged its branch, or its own `github-N` issue closed (a squash merge never makes the commit reachable; an issue or pull request the task merely links proves nothing), or when a human closes it; Kronn checks at boot, after each integration into the default branch and every five minutes (migration 283). The Planning list, the project Tasks panel and the discussion monitor show the wait too: the plan counters have an `awaiting_merge` bucket apart from `in_progress` and `done`. - The dark-theme contrast scan no longer fails on `:root`, the name axe gives a node it could not address once the page re-rendered it; the Settings route left one such node behind on any dark theme, one run in two. - The boot screen's fade-out tolerates a torn-down document, so a settle timer that outlives its test no longer throws `document is not defined`. - Every release tag now publishes the backend and frontend Docker images (`ghcr.io/docroms/kronn-backend` and `kronn-frontend`, linux/amd64 and linux/arm64, built on native runners). From the release that ships this change (0.15.2), `make start` and `./kronn start` pull the image of the checked-out version and start it: they no longer compile the backend on your machine. A checkout of 0.15.1 or earlier keeps its old `make start`, which compiles; republishing the 0.15.1 images does not change that, so a machine under ~20 GB should use the desktop installer until 0.15.2. Before, a Docker install built the 715 000-line backend crate locally, which needs about 19 GB of memory, so a machine with less (for example WSL with 5.8 GB) ended with `rustc` killed after minutes of work (KT-1468). - `make start-build` (or `KRONN_BUILD_FROM_SOURCE=1`) builds from source; an image that cannot be pulled and is not on the machine yet (a `VERSION` with no published image, e.g. right after `make bump`) falls back to it. A checkout of `main` keeps the last release's `VERSION`, so `make start` runs that release's published image, not the local code. An image that cannot be pulled but is already local (an offline restart) is started as it is, without building. `KRONN_IMAGE_TAG` picks another published tag; quotes around it in `.env` are ignored and `${VAR}` references in it (`KRONN_IMAGE_TAG=${RELEASE_TAG}`) are resolved as Compose does, instead of being passed on literally. - Before any source build, Kronn reads the memory Docker can use (plus swap). Below 20 GB, the need of the default build (19 GB peak), it stops at once and names the alternatives: the published image, the desktop installer for your platform, a bigger machine. The message carries the measured numbers (19 GB peak; 10.4 GB and 12 minutes with one codegen thread). `KRONN_FORCE_BUILD=1` builds anyway. - The `latest` image tag follows the highest stable release only: republishing an older release from the workflow no longer moves it backwards. It is moved by its own job, one run at a time per image (a queue, nothing is cancelled mid-write), which points `latest` at the highest published stable release at that moment: runs that overlap in any order end with `latest` on the newest. A registry or `git ls-remote` error in that job fails the run (after a short retry), whichever release is the highest, instead of a green run that leaves `latest` unchanged; only a registry that confirms the highest release is not published yet ("not found", "manifest unknown") leaves `latest` alone. - An empty `KRONN_IMAGE_TAG` in the environment wins over `.env` and means the `VERSION` default, exactly like Compose; only an unset one reads the file. - A project's task count was three different numbers: the Planning sidebar, the project's Tasks tab and `task_list` each counted their own way. They now share one definition: archived tasks are out unless asked for, a subtask is a task, a multi-project task counts in each of its projects, and a task belongs to a project through a direct link or a linked discussion. The sidebar shows the backend's counts and lists every page, so a group lists the rows its count announces; "General" counts exactly the tasks with no project at all. The list stops after 5,000 tasks and says so. The per-project and General counts come with the first page of `task_list` only and are left out after a cursor, so an agent never reads an uncomputed 0 as "no task without a project". A search in the sidebar lists exactly the tasks the backend counted, including those matched only by their description (KT-1351, #237). - Opening an audit validation from the project page (audit timeline, tech-debt validation or disputes link, docs check panel, a partial audit that re-verified unchanged sections) opens that discussion again, instead of the last discussion viewed, which could belong to another project (#421). - A foreach child refused for quota waits and resumes through its parent instead of failing its item (KT-1556): 19 to 31 of 40 PR reviews were lost this way. The parent run goes to `WaitingQuota`, reclaims the same child at the refused step, and does not replay items that already failed in the same sweep (no duplicate PR comments). Claude's short "limit · resets …" wordings are classified as quota; "Context limit: reset the conversation" is not. The run page no longer offers Resume on a sub-workflow child. - A quota pause no longer spends a run's Timeout guard, which now measures active runner time (persisted, so a restart keeps it). A reset announced past the old deadline schedules a wake like any other instead of leaving the run in `WaitingQuota` for good; runs parked that way by the old engine wake from a reset less than a day old (KT-1562, KT-1563). - A quota fallback chain remembers every provider that refused, so two refusals without a reset escalate instead of bouncing (KT-1564). - An undelivered worker parks as a pending resume and a woken worker adopts it (KT-1540). - A burst of peer wake-ups starts one room-agent turn instead of one turn per message, which produced near-identical replies a minute apart (KT-1539). - A room's native agent can follow, review, stop, resume and reassign the execution it launched; before, only a joined CLI session could (#241). - An MCP join moves the session link only after its binding is saved (KT-1566). - An HTTP agent past a tool's per-turn ceiling stops after a few refusals: within one response, refusals past 8 are dropped and the model is told how many; refusal-only rounds lead to a synthesis, then a hard stop; the room gets at most two trace lines per tool and round plus a summary (KT-1545). - Tool calls some OpenAI-compatible models write as `###Call:name{...}###` text are stripped from HTTP chat replies, and a first turn made only of them is retried once (KT-1543, #472). - A 401 on a cached OAuth2 or token-exchange bearer clears the entry, mints once more and retries once, instead of failing until the guessed hour runs out (KT-1067, #282). - The Copilot host sync keeps every hand-written MCP entry, like Codex, and the user's Copilot or Codex config is copied to `.kronn-backup.1` before Kronn's first write (KT-1036, #278). - In Docker, network exposure follows `KRONN_BIND`: Settings shows a note naming it instead of a switch that changed nothing and asked for a restart, and the route refuses with the same pointer (KT-1066, #281). - An unknown value or key in `config.toml` no longer resets every setting (KT-1513). A credential this build cannot represent is never dropped: once the encrypted store holds the credentials it is kept in `config.toml.preserved.enc` (`0600`). See `docs/operations/key-management.md`. - The "Add a project" bootstrap form lets the user pick the agent and reasoning tier instead of silently running the first audit-capable agent; the endpoint refuses with a 422 an agent the form would not offer (KT-1591). - A Kronn write in flight no longer sends re-read docs back to the revalidation plan (KT-1590). - Docs edited outside Kronn on a validated project are flagged on step 4, which offers to approve them as they are (human only, refused while a citation is broken) or to fix the broken citations first (KT-1489, #406). - Audit card errors name the refused TD id and, when the central `inconsistencies-tech-debt.md` exists but cannot be read, its real cause (#258). - Commits reachable from `origin/HEAD` are no longer checked as the worker's identity trailers at delivery (KT-1498). - An external API base keeps a host named `v1` while `/v1` normalization stays pinned (KT-668). - The CI verdict no longer goes red while it waits: earlier gate check runs on the same head are marked "Superseded", a CI run that starts after the verdict is accepted (KT-1230, #288), and a verdict that outlives its upstream run stays pending and is re-armed when CI completes, instead of cancelling itself red (KT-1586). - Flaky tests fixed at the root: browser storage cleared after each frontend test (KT-1057), a simulated restart that erased a parallel test's warm-up claim (KT-1565), a disk-report check tripped by a temp dir name (KT-1567), the anti-hallucination mode changed only through a locked, restoring override (KT-1569), the OllamaCard registry re-check (KT-1588) and new-tab detection in the routing e2e test (KT-1589). ### Security - Settings › Security and system tells its three security mechanisms apart (KT-1534). The authenticator has its own card (`#settings-authenticator`, between the recovery phrase and the trust levels) with numbered enrolment steps; it names the real `human-admin-secret` path from the server (macOS `~/Library/Application Support/com.kronn.kronn/`, Docker `KRONN_DATA_DIR`) instead of a Linux path given for every platform, says when agents can read that file too, and how to get a new secret (`recover-admin-secret`). The network access token card follows the server (`auth_token_set` on `GET /api/config/network-exposure`) instead of this browser's stored copy: after turning on network exposure it no longer says « désactivé » and offers to create a token that would replace the one peers use. The risk panel puts the missing authenticator first, links to its card and shows no acknowledgement until one is set up; « coffre Kronn » becomes « dossier de données de Kronn ». Once an authenticator is enrolled, the admin secret alone no longer mints a publication grant, rotates the admin secret or rotates another grant's secret: those routes ask for the strong confirmation too (a holder renewing its own grant does not; before an enrolment nothing changes; the `recover-admin-secret` recovery is untouched). The admin-secret hints (first enrolment and replacement) say Kronn generated the secret (at first start, or at the last recovery or rotation) and that it proves the machine owner; once an authenticator is enrolled, enrolling or revoking a credential, rotating a secret with it and replacing the authenticator also need its code (listing does not). The claim about who reads the file is scoped to what the sandbox covers: agents Kronn launches lose the data folder while its protection is on, a CLI you run yourself can read it, so enrol before handing it tasks. - A safe plugin export no longer leaks a secret held in the plugin definition (KT-1040). Without values the bundle replaced only environment values and copied the definition whole: a token in a URL (query, path, user info, host), a literal header, stdio arguments, a description, a TokenExchange body or endpoint left in clear. The export is now an allow-list: names and env-key names with no token-shaped word, `${ENV.…}` references, URL origins with a clean host, plain endpoint paths and catalogue values stay; every other value, the config label and env-key names included, becomes a named placeholder `${ENV.KRONN_EXPORT_}` that the import review lists and asks for, and that the import puts back with its JSON type (one left empty is named in a warning and by the plugin's first call). Kronn's own plugin ids are kept when the name they derive from is, and any other id gets a fresh one shared by the configs of that server; a catalogue plugin round-trips with no placeholder. The per-plugin export masks the same way, and its file import accepts the placeholders and names those left; the export with values is unchanged. Hosts and plain lowercase paths are only shape-checked, and so are name-like values (names, labels, header names, scopes, the JSONPath, env-key and reference keys, body keys, plain-slug ids): an all-letter word up to 20 characters and a letter-digit word up to 8 (`OAuth2`, `GPT4`) outside a segmented run like `4B7D-9F1C` are kept, so keep secrets out of them or use `${ENV.KEY}`. - Agent processes Kronn launches lose the Kronn data directory to the OS sandbox (Seatbelt, bubblewrap); the API port and the network stay open, and `recovery.key` joins the secret names (KT-1495). - Once an authenticator is enrolled, secret and destructive routes (vault reset, import, token regeneration, recovery passphrase change, sensitive deletions) require the strong confirmation, through one gate for every caller. The recovery routes stay open (KT-1496). - A bridge-token GET with a non-JSON body is refused with 415; an empty GET is still accepted (KT-1068). A test keeps `Own` bridge routes on discussion targets only (KT-1069, #283). - `minimatch` 11.0.3 for GHSA-g3hv-75wg-m249 (ReDoS through eslint). - The security scan is green again (KT-1533). `calamine` moves to 0.37, so `zip` 8.6 leaves both lockfiles and RUSTSEC-2026-0429 no longer applies to the spreadsheet reader. The only remaining `zip` < 9 is the build-time copy `utoipa-swagger-ui` 9 uses to unpack its own bundled Swagger UI. The advisory is ignored for that path with a dated rationale in `backend/.cargo/audit.toml`, and moving to `utoipa` 6 is tracked separately. `calamine` then moves to 0.38 for RUSTSEC-2026-0436: 0.37 panics or allocates without bound on a malformed CFB/OLE2 file (a legacy `.xls`), and Kronn reads untrusted spreadsheets in context files. ## [0.15.1] - 2026-10-22 ### Added - Custom skills keep a version history, like Quick Prompts. Every change is saved as a full copy of the skill; a save that changes nothing adds no version, and a skill written before this release gets its current state as v1 at its next save. The skill page has a 🕒 history button: the timeline of versions, a line diff with the previous version, and a restore that asks first and records the restored content as a new version, so nothing is lost. Agents can read the history (`skill_get` lists the versions, `custom-x@N` reads one) but never restore a version. Built-in skills have no history (KT-1434). - Agents can create and edit custom skills, CLI agents (MCP bridge) and HTTP/local agents alike, with the rules of Quick Prompts: `skill_create` saves a skill in the room's or workflow's project, `skill_update` changes a skill of a project in reach, never a builtin one. Each change is a new version, so a step pinned to `custom-x@N` keeps what it was validated on, while an enabled workflow loading the bare skill is disabled by an agent's edit until a human enables it again. A human tries the new version on real runs from the workflow's shadow settings. An agent never sets a skill's `allowed_tools`; restoring a version and deleting a skill stay human. - Agents know and can read what these versions and shadow runs need. CLI agents (MCP bridge) and HTTP/local agents get the same tools and the same guidance: `skill_get` lists a custom skill's versions and reads one with `custom-x@N`, `qp_get` lists a Quick Prompt's versions and reads one with `version`, `workflow_get` shows the shadow config. Both are read only and scoped like the object itself: a project's skill or Quick Prompt, and its versions, stay in that project. The step schema and the workflow guide's new « Versions and shadow runs » section explain `external_effect` (mark every step that acts outside Kronn), `{{run.variant}}` (keep the two runs' branches and paths apart), `{{refs.}}` / `KRONN_REF_` and pins; configuring or promoting a shadow stays human-only, and the agent points the user to the workflow editor and the Page's Resources panel. HTTP agents can now also author a workflow's `refs`. - A workflow step, a Quick Prompt or a discussion can load a precise version of a custom skill instead of the latest: `custom-x@3` loads version 3, and later edits of the skill no longer change what that step runs. The skill pickers of the workflow editor and of the Quick Prompt form show, for each custom skill picked, « latest version » or a version to pin. A version that does not exist is refused at save, never silently replaced by the latest (KT-1447). - An Agent step can also pin the version of its Quick Prompt (« Quick Prompt version » under the Quick Prompt picker). Left unset, the step loads the latest version, as before; pinned, an edit of the Quick Prompt reaches the step only when someone picks the new version. A repository Quick Prompt's pinned content is checked against its approval too, and an imported workflow drops the pin, since its Quick Prompt comes without history. - Shadow runs: a workflow can start, alongside each normal run, a shadow run on the same inputs that loads other versions of its skills and Quick Prompts. Steps marked « acts outside Kronn » (posting, sending, writing elsewhere) and Gates are skipped in the shadow run, so nothing happens twice; it never counts against the concurrency limit, never pages on failure, and its sub-runs are shadow runs too. It works in its own git worktree, unless the workflow declares it never writes the checkout. Set it from the « Shadow » button of the workflow page or in the workflow editor (« Shadow runs »: on/off, the projects it covers, the version of each skill and Quick Prompt). The run list marks shadow runs, and a normal run shows its shadow side by side, step by step. Steps read the variant as `{{run.variant}}` (`normal` or `shadow`). Agents cannot set it (KT-1449). - A Page shows the resources behind it: the Quick Prompts and skills its workflows use (those that publish to it, those its buttons launch, and their sub-workflows), with their latest version, their history, a link to edit them and, step by step, what the normal run and the shadow run load. « Promote » hands a version tested in shadow to the normal runs of that workflow: a skill in every step that loads it, a Quick Prompt pinned on the steps that load it. The Quick Prompt itself, which other workflows may use, is left as it is (KT-1450). - Workflow and Page exports carry the custom skills their steps and Quick Prompts use: each skill's file, name, version and a content hash. On import, a skill identical to one already here is reused, a missing one is created, and a skill with the same name but other content is shown in the import preview, where you choose to create a copy (the default) or use the local skill. Built-in skills stay plain references. A skill neither here nor in the file is listed before the import and reported after it, never dropped silently. Older export files still import (KT-1446). - Workflows can name the objects they use: a Page, workflow, Quick Prompt, Quick API, Quick Exec or skill gets a name in the editor's new "Named references" section, and steps write `{{refs.}}` instead of pasting its id; Exec steps also get it as `KRONN_REF_`. Saving checks the object exists and belongs to the workflow's project or to none. Exports carry the named objects and imports point each name at the imported copy; a file missing one of them is refused with the name it lacks (KT-1448). - Local image and video generation through ComfyUI (KT-1381). Config › Agents › Local gains a ComfyUI card next to Ollama: server state and version, the one-command install on macOS (`brew install --cask comfy`), and Image / Video pickers listing the installed models Kronn has a measured workflow for (FLUX.1 schnell, SDXL such as RealVisXL, Wan 2.1). Once a model is chosen, ComfyUI is offered wherever media is generated (new conversation, the Assets Creator) and in `agent_list` as `local:comfyui`, so agents generate on it through `media_generate`. Local generations cost nothing and never enter estimates; ComfyUI never appears as a chat agent. The card also downloads measured models (RealVisXL V5, FLUX.1 schnell, the Wan 2.1 1.3B pack) into ComfyUI's own model folders, with progress, cancel, resume and a disk-space check. Everything machine-dependent is a setting: the server address, the install folder (default `~/ComfyUI-Installs`), the workflow of a model Kronn cannot recognise on its own, and per-slot steps, guidance, sampler and scheduler. A stopped ComfyUI, a job it lost or one finished without a file fails at once with a sentence; a job that times out or is cancelled is cancelled on ComfyUI by its id, never by a global interrupt; downloads go only into the install folder of a ComfyUI on this machine, never through a link, and are checked against their SHA-256 (macOS and Linux; Windows links to Hugging Face). - A « hide orchestration messages » toggle beside « hide notes » in the discussion composer (KT-1330). What the orchestrator posts into a room (review requests, delivery summaries, terminal events, bounces, timeouts, worker-undelivered alerts) now carries a typed `kind: "orchestration"` (`messages.kind`; rows older than the column resolve it from their `orch-…` / `delivery-summary:…` id). Hiding folds each run of consecutive ones behind one expandable marker, persisted like « hide notes » (`kronn:showOrchestrationMessages`). A question card, a campaign gate waiting on a human, a message with an open action and a search hit are never folded. Display only: agents and the bridge/MCP read paths still get every message. Orchestration messages no longer raise the unread badge. See `docs/architecture/orchestration-message-kind.md`. - Decision models are recognised (KT-1366). Kronn reads what Ollama's `/api/show` says each local model can do, so a decision model such as `clef-flash:9b` (which answers typed questions and cannot chat) is no longer offered as a chat or agent model, and a launch on it is refused before it reaches Ollama. The decision model is chosen like the tiers, with the same picker: a « 🧭 Décision » row on the Ollama card and a Decision slot next to image and video on an HTTP connection, each listing decision models only. A local Ollama image model (such as `x/flux2-klein:4b`), or any model whose declared capabilities do not include chat, is no longer offered as a chat model either. An OpenRouter connection lists its decision models (Perplexity Decider, Jev, Clef…), which OpenRouter returns only when asked for by output modality. The manual catalogue form offers the `decision` capability, and a pull that needs a newer Ollama says to update it instead of showing the raw error. - The audit repairs more of what it used to fail on, and tells a human the rest (KT-1357, 18/10 audit matrix). Kronn splits a list of lines of one file (`deploy.yml:210,214,218`) into one marker per line, moves the comment that trails a `path:line` out of the marker, and names an empty file in backticks instead of citing it; the retry feedback for step 8 names each unaddressed detector signal with the exact text to write (the flagged file's path, or its name with the flagged line), and when the attempts are spent Kronn appends an `## Unaddressed detector signals` section to the tech-debt index and sends a `warning` instead of failing the run. The partial refresh of the TD index now gives a malformed coverage matrix back to the agent, as the Full audit does (a Node service's revalidation lost step 8 to its first attempt), and a refresh of a section built from the whole source tree ends `unchanged`, with no agent, while that tree is the one its baseline recorded; the glossary joins them (its baseline now records the tree). An `unchanged` partial step is stored as done. - The audit checks what its documents claim against the repository (KT-1387, KT-1388, KT-1370, KT-1389; v1/v2 A/B KT-1358). A `make` target or a `npm`/`yarn`/`pnpm`/`composer` script a document cites that no Makefile or manifest defines is sent back to the step that wrote it; the commit conventions are written from the observed history (`git log` pattern, prefixes, language) and detected tooling, and called enforced only when a GitHub Actions step under an established `bash`/`sh` shell or a dedicated commit-message action runs the checker (a local hook, another CI system, a Windows or matrix runner is « configured »); an unfilled commits template fails its step. Stack facts read from lockfiles and runtime files are given to the prompts, versions that contradict each other across documents are flagged, and a debt is not declared resolved without proof in the code. A citation marker no attempt could repair is demoted, after the last attempt, to a flagged backticked path. All of these are advice measured on 754 real audit documents (no false positive) and never fail a run: the partial refresh feeds them back per attempt like the Full audit, they are recorded as each step ends and survive a resume, and the audit timeline lists them next to the unverified references. Repository files are read through the project-rooted reader only, and a secret file is never cited by line. - Testing a LiteLLM connection no longer calls a valid key rejected (KT-1412). With no tier models, the test walks the catalogue; a model the proxy refuses for this key (its allow-list or tag routing, answered 401/403 with a reason) is a verdict on that model, so the next one is tried and the result says "the key works, N listed models are not allowed for it". A refusal the proxy does not attribute to a model still reports the key as rejected. - The audit no longer reports committed secrets as absent, and its documents agree on the tests and the CI (KT-1404; A/B KT-1358 round 3). The security, Docker, tech-debt and final-review steps are given Kronn's own scan of the tracked files: private keys (by name and PEM body), Slack / Teams / Discord webhook URLs, well-known vendor tokens and literal values assigned to token / secret / password names, listed by path and kind, never a value. A listed path is a finding to revoke and purge whatever the file is named, and « no secrets » is never written while one is listed. The project index, the testing and operations documents and the final review are given the tracked test files per runner, each CI workflow's events, each job's condition and the runtime version each job runs; a trigger or a gate is described only as the workflow states it (a label-gated CI never « runs on every PR », no purpose the repository does not write, « blocks merge » is a repository setting), and the final review makes every document agree with these facts. The stack facts also carry the application's version and the direct dependencies the lock file shows old (deprecated, abandoned, or an old major of a short list), which the Dependencies and tech-debt steps cite; a debt that rests on an absence (« no response caching ») must first look for the mechanism in the code. These are prevention only: no new check was added. - Arbitration cards are never lost (KT-1401). A `kronn-question` whose label, question or context is too long is recorded with the text shortened at a word (the full text kept in its description or context); a card still invalid is shown as such with its reason and returned to its author in the `disc_append` result (`invalid_cards`). Answering the last pending card of a workflow step that has ended wakes the room's agent once with every answer; an earlier card says its answer will reach that agent. - A finished task execution gives its disk back (KT-1397). Its managed checkout is removed once the execution is Done, Failed or Cancelled, nothing uses it and it has been idle for `server.disk_sweep_idle_hours` (by finish time and by the newest file written in it); the branch is kept and anything uncommitted is saved first as two patches (staged, then working tree) under the execution's evidence, so the exact state can be reapplied. A cancel with « remove if clean » still removes only a clean checkout; a dirty or preserved one waits for the sweep, which runs at start-up and every 30 minutes. Lane build dirs (`target/wt-*`) whose checkout is gone are reclaimed when durable lineage attributes them to a finished execution. A state over 256 MiB, with unmerged paths or written recently is kept, with the reason recorded. - A Definition of Done edited during an execution is adopted (KT-1402): the execution re-reads it and the worker is told the new requirements, instead of every delivery being refused. A refused delivery is recorded and shown in `task_exec_status` (`last_delivery_refusal`) and in the escalation, and an accepted one clears it. - One guide for building automations, read the same way by every agent (KT-1341). The built-in workflow architect is served section by section through `tool_manual({tool: "workflow-architect", section})` to CLI agents (MCP) and HTTP agents alike, and gains a « Reach » section: the workflow's own project, the workflow scope a human sets (Projects accessible, every project in red), why a step never inherits its room's grants, the parent task over several repositories, and the general resources. HTTP agents get `room_scope`. A refusal on another project now names the right way out for its caller: `room_scope` for a room's agent, the workflow's scope for a step, the principal for a worker (it used to name a `scope_request` tool that does not exist). A test fails when a tool exists for one audience only, in either direction, unless it is listed with its reason; the tools HTTP agents do not have yet are deferred to KT-1356. The MCP catalogue does not grow. - A full audit no longer ends Interrupted on what the audit could not fix (KT-1353, audit matrix: a PHP site and a Node service). The documentary blockers the committed documents held before the run are reported (`documentary_preexisting`, `phase: final_gate`) and never charged to the founding step, even when a step rewrites the document around them (a re-verification appending to its history); the validation lets them through only while the document is unchanged since the run. They are stored on the run (`documentary_notes`), listed in the audit timeline and at the top of the validation prompt, with the replacements Kronn applied; the blame ledger follows a blocker through the rewording of its message, so a bare sheet citation written by step 8 stays step 8's when step 10 grows the sheet. Where the gate's own message carries the one replacement (a link relative to the document's folder, the presence form of a secret file or of a tech-debt sheet, the single did-you-mean path, a range one line past the end), Kronn applies it before the step is judged and logs it (`documentary_autofix`); ambiguous suggestions are never applied. A citation into a git-ignored path says so instead of "does not exist", and the step 8 prompt shows how a sheet is cited. - A workflow's project scope can open every project (KT-1336): read, or planning write behind the same typed confirmation as a room, shown in red on the workflow page and the launch card, pinned to the validated revision and withdrawn on its own. The scope editor is redone: every project in one searchable view with a None / Read / Write picker, the projects already opened first, and one « Validate the scope » action. The editor is folded to one line by default (KT-1355): the title, the granted access as chips (or the red « Every project » badge, « No project access »), and a Details toggle that opens the full editor. It opens by itself when a human has something to do: a scope never validated, or suspended by an edit of the workflow. - A merge no longer bounces on doc citation line anchors (KT-1321). The integration merge, and the merge a worker finishes through `task_exec_commit`, stop before the merge commit: a doc conflict that differs only by the line numbers of `[src: file: …]` markers is resolved to the target's text, then every citation to a changed file the merged tree left broken is re-anchored by mapping its lines from the side it was written against to the merged file (the `#symbol` must lie in the new range; the nearest occurrence to the mapped position, within 20 lines and with no tie, settles a changed range; otherwise the merge bounces on the doc). Only `docs/` and `backend/docs/` are touched, from the merged index: a doc to rewrite, or a cited file, with unstaged work makes the merge bounce instead of sweeping that work into the commit. Both land in the signed merge commit. Any other conflict still bounces. `make docs-reanchor` also runs where no target-branch merge-base exists (a worker worktree) and, during an unfinished merge, maps from both sides of the merge without a `BASE`. - Kronn no longer fills the disk with per-lane build dirs (KT-1318). Every parallel task lane builds in its own `target/wt-` (5-30 GB) and nothing ever removed them: 195 of them took a 1.8 TB disk to 683 MB free. Now an orphan sweeper (boot, then every 30 minutes) removes the build dirs Kronn created, whose execution is finished, with no worktree, unwritten for `server.disk_sweep_idle_hours` (default 6, minimum 1: `0` is read as 1, never as "no idle check"); a dir Kronn did not create (no ownership marker) and an active lane are never touched. Below `server.disk_launch_min_free_gib` (default 30) a worker launch or a full audit sweeps first, then is refused with the reason, the execution parked `Blocked` and an alert in the principal room. **Settings → Disk**, the `GET /api/maintenance/disk` report and the `disk_usage` MCP tool show free space and Kronn's usage by kind and by task, with a Purge action that takes orphans only (a named entry needs a confirmation). The `disk_usage` MCP tool is read-only and scoped to the agent's project; the purge is a human action (the bridge token is refused on it). See `docs/operations/disk-usage.md`. - « Revalider l'audit » now re-reads the docs whose sections are all unanchored (no citation, no repository path), once per change (KT-1292). Such a doc has no verdict, so nothing ever sent it to an agent. A review stamp in `citations.json` (the doc's hash and the digest of its owning step's sources) queues it for that step when it has none, or when the doc or the step's sources changed since. After the pass the doc is stamped whether or not the agent added an anchor: the plan is clean again and the pass does not come back. The panel shows « re-read on » for each. - A principal can approve a delivery with a DoD item carried to another ticket (KT-1291): `dod_verifications: [{dod_id, met: false, carried_to: "KT-1290", evidence}]`. The approve integrates through the normal path; the item is detached from the task and a `dod_carried` event names the carrier. The carrier must exist, be open, share a project with the task and state the requirement the worker was briefed with (the execution records its DoD sentences at launch, and a `task_update` records the items it removes), or the approve is refused with the cause; the detach runs in the review checkpoint's transaction. A manifest that references a DoD id removed or carried after the delivery is judged against the execution's launch-time DoD snapshot instead of being refused as an unknown id. - A `#symbol` citation anchor that names a definition the cited range contains must open that range (KT-1074). When the range holds a definition of the symbol (`fn`, `def`, `class`, `struct`, `enum`, `const`, `interface`, `function`, `type`), that definition opens it: its doc comment and attributes, then up to 5 sibling lines (a constant and its comment), may sit above it. A range that holds no definition only has to name the symbol, so a call site or a usage still backs a claim about wiring. A range that starts a function early fails the docs check and the runtime verifier with `a definition anchor must start the range`, and `make docs-reanchor` proposes the range starting at the doc comment or attributes of the definition. 213 citations of `docs/` were repaired by reading their sentence (193 flagged by the rule, 15 moved by integrations, 5 more found by the review sample); the full check over every doc is green. - Doc relevance in « Revalider l'audit » (KT-1168). Beyond citation fingerprints, Kronn judges every section of the AI docs against the code as it is now, deterministically and without a model: `anchors_intact`, `outdated` (a cited line changed or vanished), `no_longer_relevant` (every anchor is gone) or `unanchored` (no citation, no path: listed as unverifiable, never called true). A backticked path counts as gone only when it resolves nowhere (project root, the doc's folder, the tail of any project file; not generated or git-ignored), and examples, history lines, proposals, templates and changelog docs are not claims. The panel shows the counts, the evidence and a proposed correction per section, the agent pass receives them (unanchored sections as « check or anchor »), and the sources badge turns to « to re-verify » with the vanished paths counted apart from the broken citations. A vanished mention is accepted per section with « Vérifié, inchangé » only, and from the project card only the paths its stale block displayed; a validation or a revalidation carries that acceptance over and never adds to it. The history and example rules apply to the sentence holding the path, not to the whole line, and a proposal heading must be a proposal phrase (« Ideas », « Future work »), not a word inside a real title. - A room can reach other projects, decided by a human (KT-1270). A room keeps its own project: its working directory, where what it creates lands, and the only project whose credentials and API configs it may use. In the room settings a human opens another project in read (its tasks, rooms, runs, pages) or in write (planning only: create and edit its tasks), or every project, the latter in write only after a typed confirmation sent with the scope the screen showed. An agent asks with the MCP tool `room_scope`; the request grants nothing until a human approves it, narrows it to read or declines it. The header always shows what the room reaches (a red badge for every project) and the requests waiting. Grants are re-read on every agent call, so a revoke applies at once; they never open another project's credentials, never run its workflows, never pass to worker rooms, and are kept as a journal with a `[kronn-scope: …]` line in the room for each change. Only the room's own agents reach its grants, never a workflow step or a worker posting in it. A workflow's runs reach other projects through a scope a human sets on the workflow page; any change to the workflow suspends it until validated again, and the launch card shows it. Design and refused attacks: `docs/design/discussion-project-scope.md`. - AI documentation template and audit v2 gap audit (KT-819). The project facts of the briefing gain « Documentation externe » (Confluence, Notion, wiki, other; https addresses only, no credentials, at most 6), rendered in `docs/project.md`; a Confluence space is read back with one read-only GET through the project's Atlassian connection, and a space the connection cannot see is « not verified », never a mismatch. The audit seeds `gotchas/known-traps.md` (the briefing's traps), `people/team.md` (roles, names, contacts and the team answer) and `conventions/git-workflow.md` (the answered Git facts) from the briefing, and lists the existing documents moved to `legacy/` that read as conventions, gotchas or people in each folder's README; a generated file carries the briefing marker, is regenerated by each audit and is yours once you delete that line. The project index's Stack table has `In place`, `Latest stable` and `Support end` columns filled only from the dated version sources of the run (`not verified: no source` otherwise), and a non-blocking `stack_currency` diagnostic names the rows that lack their source and date. Kronn removes the legacy « FIRST-RUN TASK » bootstrap block from a docs entry file at boot, audited or not, without cutting what a person wrote around it. - Tasks show which agents are on them (KT-154). A task carries one or more assignees, each an exact identity — the room's agent, a punctual agent or a joined CLI session — with an optional owner / reviewer role, so two Codex CLIs stay distinguishable. The discussion plan shows them as pastilles on each row (Focus and All views) and the worker of a running task execution appears there too, with its agent and tier, without any manual assignment. Assign and unassign from the plan row's quick menu or from the task sheet; agents use the new `task_assign` MCP tool (`unassign: true` removes), and moving a task to in_progress assigns the calling CLI as its owner when nobody is on it. - The briefing records the team's rules and bans (KT-1193). « Règles et interdits » holds up to 20 one-line entries of at most 200 characters, each a rule (always) or a ban (never). A ban may carry a pattern Kronn checks: a dependency declared in `package.json`, `Cargo.toml` (dotted keys included), `composer.json`, `requirements*.txt` or `pyproject.toml` (PEP 621 and Poetry), a module read from each language's import syntax (comments skipped; `lodash` matches `lodash/fp`, not `lodash.debounce`), or a file glob matched in linear time (at most three `*`/`**`). The entries go to a `## Project rules` section of `docs/briefing.md` with a machine copy, put back after the AI review, before `## Kronn options`; a briefing without rules keeps its earlier bytes. Kronn writes them into a generated section of the coding-rules doc (in the detected docs folder), restored after every audit step, so every agent of the project reads them; a section that lost its end marker is repaired without touching human lines. The briefing cannot be saved while an audit runs on the project (409). The steps that judge the code (tech debt and every `inconsistencies-*` step) get a short rules block: a violated ban or unmet rule is a TD with its `file:line` proof, and no fix may break a ban. Each ban with a pattern is checked by a read-only detector (`project-ban`, no symlink followed, size and count caps) whose hits join the step 8 signals and need a disposition. - The briefing form reopens with the saved answers, and every project fact takes an optional note (KT-1196). `GET /api/projects/{id}/briefing/answers` reads `docs/briefing.md` (or the DB notes before the project has a docs folder) back into the six questions, the project facts, the existing documentation and the Kronn options; unknown or malformed parts are left empty, and a section the form does not know stays inside the last answer so a re-save keeps it. Saving again unchanged rewrites the same bytes. Each fact (tracker, project key, forge, repository, default branch, commit convention, sign-off, merge strategy, review, release cadence, environments, people) gets « Note (facultatif) », up to 300 characters, validated like the other briefing texts, stored in the machine copy (absent when empty, so older briefings stay byte-identical), rendered next to its fact in `## Project facts` and `docs/project.md`, given to the audit with the fact and put back after an AI review. An answer line that would read as structure (a heading, a machine copy, a Kronn option line) is saved behind a `\` and given back as typed, and the questions are read in order, so an answer can never split the briefing. A click that only focuses a field no longer skips the prefill; when the saved briefing arrives after a real change, the typed text is kept, only the empty sections are filled from it, and « Charger le briefing enregistré » loads the rest, so a save never wipes what was not changed. The rules and bans (KT-1193) reload into their editor too, following the same late-prefill rule, so re-saving a reopened briefing no longer drops them; `## Project rules` ends the last answer instead of being read into it, and the reopened form is refused (409) while an audit runs. - The briefing asks about existing documentation and linked projects (KT-999). « Existing documentation? » lists the locations Kronn finds in the repository (`README*`, `docs/`, `doc/`, `wiki/`, ADR folders, `CONTRIBUTING*`, root `*.md`, OpenAPI/Swagger files, `apidoc/`; symlinks skipped), pre-checked, each with the date of its last commit (`git log -1`, literal pathspec, bounded and timed out). A path can be added by hand: it must be relative, without `..`, existing, and stay inside the repository once symlinks are resolved, otherwise the server answers 400. Each kept location gets a closed state (recent, old, obsolete, partial, reference), a one-line note and, at most one per kind, the source-of-truth mark (never on an obsolete location). The answers go to a `## Existing documentation` section of `docs/briefing.md` with a machine copy, put back as answered after the AI review, before `## Kronn options`. The section stays out of the audit and review prompts until a bench run shows a gain. The linked repos drawer is available in the briefing with one-click suggestions whose description is left to fill in. - Linked repos show which AI doc they really have (KT-999): « doc IA » (`docs/AGENTS.md` or the legacy `ai/index.md`), « README seulement » or « pas de doc IA ». - The audit can read the briefing's existing documentation (KT-999), off by default: `audit_briefing_docs = true` in `config.toml`, or `KRONN_EXP_BRIEFING_DOCS=1` for one run (bench). It reaches only the steps that use it — step 1 (`AGENTS.md`), step 8 (tech debt) and the API design audit — as a `## Existing documentation` block within the 6,000-character briefing cap (the block takes at most 3,000 of them, the briefing text gives way). A `reference` location is read, reused and cited; an `obsolete` one IS tech debt: step 8 records a TD row and a detail file citing the location and its last-commit date whatever the audit's scope, and no step copies a fact from it nor calls it out of scope — and on a later audit step 8 never parks it under `## Reconciliation candidates` as « out of scope this audit »; `old`/`partial` (and a state not provided) are verified before reuse, a `recent` one is spot-checked. The recorded locations are re-validated before they reach a prompt (a hand-edited `briefing.md` cannot add lines to it). Disabled, the prompts (and the audit golden) are unchanged. - Docs record what a human validated and ask for a reconfirmation when Kronn changes it, never blocking (KT-1079). A validation (`validate-audit`, a TD decision on a validation card, « Vérifié, inchangé », a revalidation that leaves the docs as validated) records the digest of each validated doc in `docs/provenance.json`, next to `citations.json`; the validated text stays local under `.kronn/audit/validated-originals/` for the diff. No marker is added to the prose. Every automatic writer now applies its change to a human-validated doc or an `owner="human"` section instead of refusing, reverting or skipping it: full and partial audits (no more restore + dated `human-section-diff` report), revalidation (re-anchoring and the agent pass now cover human sections), template reinstall (human sections are no longer put back), documentary repair (citation bundles in human sections are normalized) and the tech-debt index resync. The doc is then marked « to reconfirm » with the writer and a before/after diff. A « À reconfirmer (N) » button next to the audit status on the project card opens the docs to reconfirm with their diff; « Confirmer » (one doc or all, `POST /api/projects/:id/doc-provenance/confirm`, human only) restores the provenance. The template reinstall plan names the human-validated content it will rewrite before writing. The learning gate weights its sources by this provenance: a source in a doc a human validated and nothing changed counts as proof; one only an AI wrote, or changed since its validation, does not count as verified evidence for a `fact` until reconfirmed. The audit prompts let an audit correct a human section where the code contradicts it. No database change. - Settle the audit's tech debt from the project card (KT-982). A "Dette technique" block shows the progress (confirmed, kept on purpose, deferred, rejected, remaining), the remaining count per severity and the Validated rule, and opens a review screen built for a hundred items and more: filters (to settle, deferred included; settled; all), severity chips, a search over id, title and cited files, a list grouped by severity with a checkbox on every row, and a bulk bar that settles the whole selection in one atomic request (one unknown id writes nothing). The detail shows the problem, the clickable `file:line` pointers (opened in the code viewer), the verification criterion and four decisions; keeping an item on purpose records the reason in the project's decision log, trusted as a human decision like a validation card. Each item links its Kronn task or creates one (the KT-1167 conversion for one sheet); GitHub and Jira are disabled placeholders. "Discuter de cette TD" shows the validation messages that name the item and the item's own discussion, created with the agents picked in the comparative Quick Prompt selector (now a shared component). New endpoints: `GET /api/projects/{id}/audit/tds`, `POST …/audit/tds/decide`, `POST …/audit/tds/{td_id}/task`, `GET|POST …/audit/tds/{td_id}/talk`. - A project can be validated without the validation discussion's terminal marker once every Critical and High tech-debt sheet is confirmed, rejected or kept on purpose (a deferral does not count) and the other gates pass (KT-982). The discussion path still works. - CI checks the `[src: file:]` citations of Kronn's own docs (KT-1059). A test (`cargo test docs_citations`, run by the backend suite) walks `docs/**/*.md` (except the historical `docs/releases/`) and `backend/docs/` with the citation verifier's own extraction, path jail and line counting, and fails on a citation that has no line, names a missing file or a directory, is absolute or leaves the repo root, or points past the end of the file. Only a binary file may be cited without a line. Fenced code and placeholder examples (`:`) are skipped; a citation in backticks is checked. The report lists `doc:line`, the citation and the reason. The corpus was repaired to zero failures: every line-less citation now points at the symbol it means, paths of moved or split files were re-located, and sentences about code that no longer exists were rewritten. - The audit's tech-debt baseline runs as code (KT-152). Fourteen new deterministic detectors cover what the step 8 prompt used to ask the agent to grep: Dockerfile (root user, no `HEALTHCHECK`, apt flags and list cleanup, base image untagged or `latest`, `ADD` of a remote URL, secret-named `ARG`), compose images untagged or `latest`, CI (`StrictHostKeyChecking=no`, `pull_request_target`, actions on a moving ref), tracked `.env` files (filenames only), templates (`` without `alt`, inline `