diff --git a/04-ISSUES/192-the-meshs-tools-reach-a-person-only-by-a-registration-made-by-hand/00-report.md b/04-ISSUES/192-the-meshs-tools-reach-a-person-only-by-a-registration-made-by-hand/00-report.md
new file mode 100644
index 0000000..68b31b6
--- /dev/null
+++ b/04-ISSUES/192-the-meshs-tools-reach-a-person-only-by-a-registration-made-by-hand/00-report.md
@@ -0,0 +1,78 @@
+---
+status: open
+opened: 2026-10-02
+located-in: [mesh-catalog modules/mesh-console, mesh-controller cmd/mesh-controller/plan.go (port assignment)]
+fixed-by:
+amended-design:
+---
+
+# 192 — The mesh's tools reach a person only by a registration made by hand
+
+## What was observed
+
+A design session on a workstation had none of the mesh's tools. The console was running on that
+machine and answering on its loopback port. It was reached over the bus as the console's account, and
+listed every running module's tools and every seat's verbs
+([ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md)). What was missing
+was the registration that tells the person's coding agent where the console is. That registration had
+been made by hand, once, while migrating the machine, and scoped to the one project directory it was
+made in. Every session started anywhere else had no mesh tools. Nothing said so: the agent simply
+offered no mesh tools, and the session fell back to a pull-request link for a person to open by hand.
+
+The predecessor did this job itself: it wrote its tool server into the agent's user configuration on
+every machine. Migrating removed that entry, as it should have, and no module took the job over.
+
+## Why this is here
+
+Three gaps, each of which would have stopped a module from doing it even if one existed.
+
+**1. The console tells nobody where it is.** Its definition listens on a port and provides nothing.
+A module that wanted to point an agent at the console has no requirement it could name, so it
+would have to write the address into its own definition as a literal. That is exactly what
+[ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md) and
+[ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)
+remove.
+
+**2. The console's port is one its definition chose.** The definition names a port, and the mesh
+never assigned one: no port assignment exists for the console on any machine. The plan assigns a
+machine port only to a port a container publishes through a mapping, "without one the software binds
+what it binds". The console runs on the host network with no mapping, but it reads its listening
+address from `${port:…}`, so the mesh could move it and does not. That is a module choosing a
+machine port, which [ADR 0038](../../02-DECISIONS/0038-the-mesh-assigns-the-port.md) exists to
+prevent, through a gap in how the rule is applied rather than a decision against it. A module that
+reads its port from the mesh should be assigned one like any other.
+
+**3. Nothing in the mesh owns a person's agent configuration.** No catalogue module writes the agent's
+settings, its tool-server registrations, or the rules and skills the predecessor delivered. On the
+four machines these are hand-kept, or left over from the predecessor, or missing.
+
+## What a fix looks like (not decided)
+
+- **The console provides its endpoint.** A provision, working name `mesh-tools`, served as the URL on
+ the machine port the mesh gives it. The console listens only on loopback, so the provider must be on
+ the consumer's own machine. Co-location already chooses it
+ ([ADR 0084](../../02-DECISIONS/0084-which-provider-serves-a-consumer.md)), and a machine with no
+ console refuses the consumer, naming the provision.
+- **A module for the coding agent requires it** and writes the registration into the agent's
+ system-wide managed settings. The agent reads tool servers from a `managedMcpServers` key there. That
+ file is the machine's rather than a user's, so the module owns it whole and no home directory is
+ named. People keep their own registrations beside it. The agent's separate *exclusive* managed
+ file is the wrong one: it blocks every registration a person makes and hides the hosted connectors.
+ The agent's per-user file is rewritten by the agent continuously and sits in a home directory,
+ which would make its path an operator value. These facts come from the agent's documentation
+ (managed MCP and managed settings pages), not yet verified on a machine.
+- **The same module owns the rest of the agent's configuration** the predecessor delivered: managed
+ settings and the rules, skills and instructions every session reads. Each declared setting carries
+ a default (ADR 0164,
+ proposed on its own branch), so one configuration serves every machine and one machine may differ.
+
+## Open questions
+
+- **Is the agent's configuration one module or several?** Tool registration, managed settings, and
+ the instruction files have different readers and change at different rates.
+- **Whose machine port is the console's?** Should a host-network container that reads its port from
+ `${port:…}` be assigned one, or should a machine-only listener keep its declared number? The second
+ needs a decision, because ADR 0038 does not allow it today.
+- **Credentials.** The console's authority is the machine's login (ADR 0152). A registration that
+ reaches it carries no secret today. If the console ever listens beyond loopback, the registration
+ needs one, from the vault.
diff --git a/04-ISSUES/193-the-store-seats-read-only-query-is-read-only-by-convention/00-report.md b/04-ISSUES/193-the-store-seats-read-only-query-is-read-only-by-convention/00-report.md
new file mode 100644
index 0000000..45c3b18
--- /dev/null
+++ b/04-ISSUES/193-the-store-seats-read-only-query-is-read-only-by-convention/00-report.md
@@ -0,0 +1,112 @@
+---
+status: resolved
+opened: 2026-10-02
+located-in: [mesh-catalog modules/postgres/client.ts (readOnlyQuery)]
+fixed-by: [mesh-catalog PR 209 (postgres), mesh-catalog PR 210 (mssql)]
+amended-design:
+---
+
+# 193 — The store seat's read-only query is read-only by convention, and its answer is unreadable
+
+## What was observed
+
+Asking the store seat's `query` verb for a count through the console returned this. Rows are each
+wrapped in an object under a key named `BEGIN`: the column name, then the value, then the word
+`ROLLBACK`. A query returning nothing gave the column name and `ROLLBACK` alone. The answer to
+`select count(*) as n from
` was:
+
+> `rows: [ {BEGIN: "n"}, {BEGIN: "46"}, {BEGIN: "ROLLBACK"} ]`
+
+A reader can work it out. A program cannot, and a query with two columns loses which value belongs to
+which.
+
+## Why this is here
+
+**The cause is the same line that makes the query read-only.** The holder's tool sends
+`BEGIN TRANSACTION READ ONLY; ; ROLLBACK;` to the command-line client as one
+string. The client prints a command tag for each of the three statements, and the parser takes the
+first line, `BEGIN`, as the header.
+
+**And it is not read-only.** [ADR 0159](../../02-DECISIONS/0159-a-tool-call-names-the-machine-and-a-holder-serves-its-seats-verbs.md)
+decided the store seat's `query` verb is "one read-only statement against one database". The only
+thing enforcing that is the wrapping transaction, and the caller's statement is pasted inside it as
+text. A statement that begins by ending the transaction (a commit, then anything) runs whatever
+follows it outside the read-only transaction, with the holder's own role, the administrative one that creates every
+consumer's role and database. A rule stated in a decision and enforced by string concatenation is enforced by nothing.
+
+*This is read from the code, not tried against the live store, and it should not be tried there.*
+The lab bed is where it gets proven.
+
+Every caller with `invokes` on the store seat's `query` can do this. The console has `invokes: ["*"]`,
+so that includes anyone logged in on a machine running the console.
+
+## What a fix looks like
+
+- **One statement, refused otherwise.** Send the caller's statement alone, through the client's
+ single-statement path (the extended protocol takes one statement per call and refuses more). The
+ read-only property then comes from the session, not from text around the statement.
+- **Read-only by role, not by transaction.** Run the verb as a role that can only read, granted
+ `pg_read_all_data`, not as the administrative role. A statement that escapes every wrapper still cannot write.
+- **Rows as rows.** Parse the client's output with the column names it returns, or use a driver
+ instead of the command-line client, so a row is an object keyed by its columns.
+- **The check 0159 lacks:** a test that sends a commit followed by a write and asserts the write is
+ refused and nothing changed. Another asserts a two-column row comes back keyed by both columns.
+
+## Proven, 2026-10-02
+
+On a throwaway server — the same engine image, no network, reached over a socket — the module's code
+from the catalogue's main branch ran `COMMIT; COPY (select 1) TO PROGRAM ''` and **the
+command ran on the database host** as the server's own user. `COMMIT; DROP TABLE t` executed the drop
+outside the read-only transaction; the wrapper's own trailing rollback happened to undo it, which a
+caller ending their statement with a commit of their own would get past (not tried). Nothing was tried
+against the live store.
+
+The fix (mesh-catalog PR 209) runs the caller's statement as a login granted `pg_read_all_data` and
+nothing else, read-only by its role and its session, with a password the mesh mints as one of the
+module's own secrets; without that password the call is refused rather than run as the admin. On the
+same throwaway server every escape above, and `SET ROLE`, `RESET SESSION AUTHORIZATION`, turning
+read-only off, creating a table, altering the role and reading a server file, is refused; a plain
+select comes back keyed by its columns. One attempt — turning the transaction's read-only off, then
+deleting — got past the first layer and was stopped by the second, which is why both exist.
+
+**Not answered by the statement-count fix proposed above.** The command-line client sends one string
+in one message, so several statements still arrive together. They are harmless as the reader, and
+refusing them is left to whoever moves the module to a driver.
+
+## The same hole, elsewhere — and two worse ones
+
+The `mssql` module wrapped a caller's statement the same way (`BEGIN TRANSACTION; … ROLLBACK;` as its
+administrator) for its `mssql_query` tool. Its command-line client added two holes of its own. Both
+were proven on a throwaway server, running the client the way the module ran it:
+
+- **It substitutes `$(NAME)` from its environment into the caller's text**, and the administrator's
+ password is in that environment. Selecting it as a string returned the password.
+- **It reads a line beginning `:!!` as a command that starts a program**, in the container that holds
+ the administrator's password and the module's bus credentials. Its switch for refusing such commands
+ makes the shipped version ignore the statement entirely, so the switch cannot be the guard.
+
+None of it was reachable on the live mesh, for a reason that is a defect of its own: the runtime image
+never installed the client, so every mssql tool failed (`spawn sqlcmd ENOENT`). The fix (mesh-catalog
+PR 210) installs the client at a pinned digest and runs the caller's statement as a login that can
+connect and read and do nothing else. Substitution is off. The statement must be one line, placed after
+the module's own text, so no line of it can begin a command; a line break is refused before the client
+starts. On the throwaway server, writes, `xp_cmdshell`, impersonating the administrator, and joining
+the administrators' role were all refused, and the variable came back as the literal text.
+
+**The general lesson**, worth more than either module: *a command-line client is an interpreter with
+its own syntax, and a caller's text handed to it is a program in that syntax as well as in SQL.* A
+module that passes a caller's text to a client has two languages to defend, and a transaction drawn
+around the text defends neither.
+
+## Resolved, 2026-10-02
+
+Both pull requests merged, built and pushed to the two machines that run each module. Checked live, on
+every copy, by asking each one who it is:
+
+- the store seat's `query`, and postgres's own tool on each machine, answer as the reader login —
+ not a superuser, in a read-only transaction — with rows keyed by their columns;
+- mssql's tool, on each machine, answers as its reader login, outside the administrators' role, and
+ returns `$(SQLCMDPASSWORD)` as the literal text it is. Its tools work for the first time.
+
+The escapes themselves were tried only on the throwaway servers above; on the live mesh the check is
+the identity a statement runs as, which is what makes every escape a statement that the login cannot do.