Skip to content

Record the findings that have no Linear issue, in one place - #10

Open
HackTuah wants to merge 1 commit into
mainfrom
chore/unfiled-ledger
Open

HackTuah wants to merge 1 commit into
mainfrom
chore/unfiled-ledger

Conversation

@HackTuah

@HackTuah HackTuah commented Sep 7, 2026

Copy link
Copy Markdown
Member

The Linear workspace is at its free issue limit:

You've exceeded the free issue limit for this workspace.

Five findings from the 002 review rounds could not be filed. They were recorded as comments on the nearest open issue, which keeps them but does not make them findable — the two round-4 items already spent a round parked in a thread before anyone re-read them.

Owner decision, 2026-09-07: every unfileable finding goes on a comment and into a single running list, so nothing lives only in a thread.

What is in it

Three live defects, shipped in 0.3.0, being fixed in slice 003 — recorded as tracked-only-here so that if that slice does not ship them they revert to unfiled rather than disappearing: the non-primitive x-mcp-header annotation making a tool permanently uncallable, a host data fault losing the request id, and pre-read refusals answering without connection: close.

Two cleanup items waiting for board capacity: fault_response/4's re-raise branch is unpinned, and ToolCatalog.fetch/2's @spec is not honest now that it is public API.

Two structural gaps: nothing derives the modern era's required result fields from the specification, so the next missing one is found the way ttlMs/cacheScope were; and readme_claims_test.exs does not deliver what CONVENTIONS.md asks of it.

Each entry names the round and the lane that found it, gives the derivation, and says whether it is a live defect or cleanup. The file states how an entry leaves: deleted by the commit that discharges it, not marked done in place.

Scope

One new file, slices/UNFILED.md. It is in neither concurrent slice's file set — slice 003 owns mix.exs, README.md, CHANGELOG.md, lib/, test/; slice 004 owns tools/, .githooks/, .github/.

Gate green, every step line reading pass.

Noted while running the gate here

reuse reads pass (24 commentable files) with this .md file added and carrying an SPDX header. The count is unchanged because the REUSE population is a hand-written extension glob ('*.ex' '*.exs' '*.sh' '*.yml') under a comment claiming it is derived from the tracked set — so .md is invisible to it. That is SCR-262 observed in the ordinary course of adding a file, and it is slice 004's item 7.

The Linear workspace is at its free issue limit:

    You've exceeded the free issue limit for this workspace.

Five findings from the 002 review rounds could not be filed. They were recorded as
comments on the nearest open issue, which keeps them but does not make them
findable -- a comment thread is not an index, and the two round-4 items already
spent a round parked in one before anybody re-read them.

Owner decision, 2026-09-07: every unfileable finding goes on a comment AND into a
single running list here, so nothing lives only in a thread.

Each entry names the round and the lane that found it, gives the derivation, and
says whether it is a live defect or cleanup. Three live defects are being fixed in
slice 003 and are recorded as tracked-only-here, so that if that slice does not
ship them they revert to unfiled rather than disappearing. Two cleanup items and
two structural gaps wait for board capacity.

The file says how an entry leaves: deleted by the commit that discharges it, not
marked done in place. A ledger that accumulates ticks is the shape this repository
has already been caught by twice.

Signed-off-by: Ayla Croft <aylacroft@proton.me>
@HackTuah

HackTuah commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

Correcting one entry in this ledger, from a reproduction rather than from reading.

slices/UNFILED.md:100-109, "BeamMCP.ToolCatalog.fetch/2's @spec is not honest", says
three host-authored catalog shapes raise. Reproduced on today's main + slice 008
(slices/008-catalog-generalization/logs/probe-fetch-spec.txt, written by the probe that produced
it) — four raise, and there is a fifth case the entry does not mention that is worse than any of
them
:

host spec.name is a binary, not an atom       {:RAISED, ArgumentError}
all/0 returns a non-list map                  {:RAISED, BadMapError}
all/0 returns nil                             {:RAISED, Protocol.UndefinedError}
all/0 returns a bare map, not a %ToolSpec{}   {:returned, {:ok, %{name: :echo}}}
module is not loaded / does not exist         {:RAISED, UndefinedFunctionError}
CONTROL: a well-formed catalog                {:returned, {:ok, %BeamMCP.ToolSpec{...}}}

The missing raise is UndefinedFunctionError — a module that is not loaded or does not exist.

The fourth line is the one that matters. It does not raise: it returns
{:ok, %{name: :echo}} successfully, which violates the @spec silently and hands a
struct-shaped thing that is not a struct on to dispatch. The entry frames the problem as "says
:error and raises", so the case it misses is the one a reader of the entry would never think to
guard: a wrong answer that looks like a right one. A raise is loud; this is quiet.

Both directions are closed in slice 008 (SCR-294, PR #21), and not by widening the @spec or
adding a rescue — Catalog.validate/1 refuses all five shapes at Server.new/1, so the raise
conditions are unreachable for a server that started, and they are documented in the moduledoc
rather than caught. Catching would make a host bug indistinguishable from "no such tool", which is
the advertise-versus-call disagreement the behaviour exists to prevent.

The rest of this ledger's live entries are unverified against the current tree in the same way.
That is not a criticism of the entries; it is the same thing this correction demonstrates. Each
one was measured when it was written, on a tree several slices back — fault_response/4's
unpinned re-raise branch cites 137 tests, 0 failures, and the suite is at 187 tests, 0
failures
today. Nothing here says which of them still reproduce. Before any of them is acted on,
it should be re-run the way this one was, and the count in it should be treated as a claim about
that older tree rather than a fact about this one.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DiEKvejosdQbcRvQ47x9UQ

HackTuah added a commit that referenced this pull request Sep 13, 2026
…and prompts

BREAKING, to a host contract rather than to the wire. Taken now, before the
resources and prompts slices exist, so it is taken once.

(a) MIGRATION PATH: a clean break, no deprecated delegate.

    The decisive argument is not taste, it is the version policy already in
    place. Breaks land at the minor while this package is 0.x, and the README
    recommends `~> 0.3.0`, which EXCLUDES 0.4.0 -- measured in slice 003's
    version table. No consumer is carried across by a routine mix deps.update;
    that tight pin exists for exactly this.

    And a delegate would cost the thing the contract is for: keeping
    ToolCatalog as a shim means two entry points into "which tools exist",
    which is the second reader that decision (b) forbids.

(b) THE SINGLE-LOOKUP GUARANTEE: preserved, and strengthened.

    tool_catalog.ex promised it in prose and left two call sites --
    server.ex:185 called catalog.all() directly, Catalog.fetch/2 called it
    again. Now both go through Catalog.tools/1, one function, so advertise and
    call cannot drift apart without deleting it. Slice 002 fixed a real defect
    of exactly this shape: tools/list honoured an injected catalog and
    tools/call ignored it.

    Pinned by effect, not by prose: catalog_test.exs compares what tools/list
    advertises against what tools/call accepts, for a catalog whose tool exists
    in no other catalog in the suite -- so neither path can pass by the
    coincidence that made slice 002's original test green.

(c) THE fetch/2 @SPEC: documented honestly, not caught.

    Catching would turn a host bug into :error, which is indistinguishable from
    "no such tool" -- the advertise-versus-call confusion this behaviour exists
    to prevent, reintroduced by the error handling meant to be defensive. The
    raise conditions are listed in the @doc and one is pinned by a test.

    THE COUNT IN PR #10's LEDGER IS WRONG AND IS CORRECTED. It claims three
    shapes raise. Measured on this tree
    (slices/008-catalog-generalization/logs/probe-fetch-spec.txt):

      host spec.name is a binary            ArgumentError
      capabilities/0 returns a non-list map BadMapError            <- not the
      capabilities/0 returns nil            Protocol.UndefinedError   one error
      module not loaded                     UndefinedFunctionError

    Four, not three: "returns a non-list" is two different raises depending on
    the shape. And a FIFTH case the ledger missed entirely, which is worse than
    a raise because nothing fails: a bare map entry returns {:ok, %{name: :echo}}
    SUCCESSFULLY, violating the @SPEC silently. That one is closed by init-time
    validation rather than by documentation.

CALLBACK RENAMED, not just re-typed. Keeping all/0 while changing its return
from a list to a map compiles against every existing host and fails at the
first request with a BadMapError. capabilities/0 makes the break arrive at
compile time as an unimplemented callback. The option is :catalog, not
:tool_catalog, for the same reason: a catalog carrying resources and prompts is
not a tool catalog.

A malformed catalog is refused at Server.new/1 -- runtime, so calling the host
is safe. Transport.HTTP.init/1 checks only the export, deliberately: under
Plug's default init_mode it is the host's COMPILE time, and a correct catalog
reading config would fail there.

The call-site list I was given missed several: server.ex's alias, moduledoc,
typespec and new/1 key; stdio.ex's two moduledoc mentions; http.ex's alias,
moduledoc, opts lookup and the ArgumentError text naming all/0. server.ex:311
(dispatch) is NOT affected -- it consumes a ToolSpec, which is unchanged.

    187 tests, 0 failures -- quoted from the run, and corrected: the first
    version of this message typed 185, which no command had printed.
    Gate OK.  GATE_EXIT=0

Signed-off-by: Ayla Croft <aylacroft@proton.me>
HackTuah added a commit that referenced this pull request Sep 13, 2026
… scores

PLAN and FINDINGS in slices/008-catalog-generalization/, plus the scored
mutation archive.

The PLAN specifies no review rounds, and says so rather than leaving the absence
to be inferred: the brief was a single pass, nobody is scoring this slice, and a
round boundary with no reviewer behind it is a heading. The mutants and the gate
stand in.

FINDINGS records four things that were measured rather than assumed:

  - PR #10's ledger says three host shapes make fetch/2 raise. Four do, and a
    fifth case the ledger missed is worse than any of them: a bare map entry
    RETURNS {:ok, %{name: :echo}} successfully, violating the @SPEC silently.
    All five are refused at Server.new/1 now.
  - The brief's call-site list was wrong in both directions -- two readers
    missing, one listed that is not affected because it consumes a ToolSpec.
  - Mcat2's first version was a compiler kill, left in the log above the
    corrected run.
  - 4a2d2e9's message typed "185 after" where the run printed 187. The commit
    was amended before anything was pushed; recorded here too, because an
    amended commit leaves no trace and the defect is the habit, not the digit.

Also stated plainly: the Linear issue was filed at record time, not before the
code, which is a departure from slice 007 and costs something real -- nothing
external witnessed the acceptance criteria before they were met.

Signed-off-by: Ayla Croft <aylacroft@proton.me>
HackTuah added a commit that referenced this pull request Sep 13, 2026
…and prompts

BREAKING, to a host contract rather than to the wire. Taken now, before the
resources and prompts slices exist, so it is taken once.

(a) MIGRATION PATH: a clean break, no deprecated delegate.

    The decisive argument is not taste, it is the version policy already in
    place. Breaks land at the minor while this package is 0.x, and the README
    recommends `~> 0.3.0`, which EXCLUDES 0.4.0 -- measured in slice 003's
    version table. No consumer is carried across by a routine mix deps.update;
    that tight pin exists for exactly this.

    And a delegate would cost the thing the contract is for: keeping
    ToolCatalog as a shim means two entry points into "which tools exist",
    which is the second reader that decision (b) forbids.

(b) THE SINGLE-LOOKUP GUARANTEE: preserved, and strengthened.

    tool_catalog.ex promised it in prose and left two call sites --
    server.ex:185 called catalog.all() directly, Catalog.fetch/2 called it
    again. Now both go through Catalog.tools/1, one function, so advertise and
    call cannot drift apart without deleting it. Slice 002 fixed a real defect
    of exactly this shape: tools/list honoured an injected catalog and
    tools/call ignored it.

    Pinned by effect, not by prose: catalog_test.exs compares what tools/list
    advertises against what tools/call accepts, for a catalog whose tool exists
    in no other catalog in the suite -- so neither path can pass by the
    coincidence that made slice 002's original test green.

(c) THE fetch/2 @SPEC: documented honestly, not caught.

    Catching would turn a host bug into :error, which is indistinguishable from
    "no such tool" -- the advertise-versus-call confusion this behaviour exists
    to prevent, reintroduced by the error handling meant to be defensive. The
    raise conditions are listed in the @doc and one is pinned by a test.

    THE COUNT IN PR #10's LEDGER IS WRONG AND IS CORRECTED. It claims three
    shapes raise. Measured on this tree
    (slices/008-catalog-generalization/logs/probe-fetch-spec.txt):

      host spec.name is a binary            ArgumentError
      capabilities/0 returns a non-list map BadMapError            <- not the
      capabilities/0 returns nil            Protocol.UndefinedError   one error
      module not loaded                     UndefinedFunctionError

    Four, not three: "returns a non-list" is two different raises depending on
    the shape. And a FIFTH case the ledger missed entirely, which is worse than
    a raise because nothing fails: a bare map entry returns {:ok, %{name: :echo}}
    SUCCESSFULLY, violating the @SPEC silently. That one is closed by init-time
    validation rather than by documentation.

CALLBACK RENAMED, not just re-typed. Keeping all/0 while changing its return
from a list to a map compiles against every existing host and fails at the
first request with a BadMapError. capabilities/0 makes the break arrive at
compile time as an unimplemented callback. The option is :catalog, not
:tool_catalog, for the same reason: a catalog carrying resources and prompts is
not a tool catalog.

A malformed catalog is refused at Server.new/1 -- runtime, so calling the host
is safe. Transport.HTTP.init/1 checks only the export, deliberately: under
Plug's default init_mode it is the host's COMPILE time, and a correct catalog
reading config would fail there.

The call-site list I was given missed several: server.ex's alias, moduledoc,
typespec and new/1 key; stdio.ex's two moduledoc mentions; http.ex's alias,
moduledoc, opts lookup and the ArgumentError text naming all/0. server.ex:311
(dispatch) is NOT affected -- it consumes a ToolSpec, which is unchanged.

    187 tests, 0 failures -- quoted from the run, and corrected: the first
    version of this message typed 185, which no command had printed.
    Gate OK.  GATE_EXIT=0

Signed-off-by: Ayla Croft <aylacroft@proton.me>
HackTuah added a commit that referenced this pull request Sep 13, 2026
… scores

PLAN and FINDINGS in slices/008-catalog-generalization/, plus the scored
mutation archive.

The PLAN specifies no review rounds, and says so rather than leaving the absence
to be inferred: the brief was a single pass, nobody is scoring this slice, and a
round boundary with no reviewer behind it is a heading. The mutants and the gate
stand in.

FINDINGS records four things that were measured rather than assumed:

  - PR #10's ledger says three host shapes make fetch/2 raise. Four do, and a
    fifth case the ledger missed is worse than any of them: a bare map entry
    RETURNS {:ok, %{name: :echo}} successfully, violating the @SPEC silently.
    All five are refused at Server.new/1 now.
  - The brief's call-site list was wrong in both directions -- two readers
    missing, one listed that is not affected because it consumes a ToolSpec.
  - Mcat2's first version was a compiler kill, left in the log above the
    corrected run.
  - 4a2d2e9's message typed "185 after" where the run printed 187. The commit
    was amended before anything was pushed; recorded here too, because an
    amended commit leaves no trace and the defect is the habit, not the digit.

Also stated plainly: the Linear issue was filed at record time, not before the
code, which is a departure from slice 007 and costs something real -- nothing
external witnessed the acceptance criteria before they were met.

Signed-off-by: Ayla Croft <aylacroft@proton.me>
HackTuah added a commit that referenced this pull request Sep 13, 2026
… keeps where it folds the literal

xml/1 escapes seven things now, from one table: &, <, >, " as before, and tab, LF, CR as
&#9; &#10; &#13;. The goldens carry none of them and are byte-identical (regenerated and
compared). The document says why, and no longer cites the schema's NMTOKEN typing as if
node ids met it: they carry / and are read as strings by the readers this targets; key
ids are numbered so a key carrying a space or a quote never lands in an id. Mca24 writes
a tab literally again.

    2 properties, 318 tests, 0 failures   TEST_EXIT=0   Canonical 100.00%

Signed-off-by: Ayla Croft <aylacroft@proton.me>
HackTuah added a commit that referenced this pull request Sep 13, 2026
… keeps where it folds the literal

xml/1 escapes seven things now, from one table: &, <, >, " as before, and tab, LF, CR as
&#9; &#10; &#13;. The goldens carry none of them and are byte-identical (regenerated and
compared). The document says why, and no longer cites the schema's NMTOKEN typing as if
node ids met it: they carry / and are read as strings by the readers this targets; key
ids are numbered so a key carrying a space or a quote never lands in an id. Mca24 writes
a tab literally again.

    2 properties, 318 tests, 0 failures   TEST_EXIT=0   Canonical 100.00%

Signed-off-by: Ayla Croft <aylacroft@proton.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant