Commit Graph

86 Commits

Author SHA1 Message Date
Edward Langley 4c6959da53 feat: add skills for various agents 2026-09-20 01:18:59 -07:00
Edward Langley 31252a1744 chore: update configs 2026-09-20 00:52:33 -07:00
Edward Langley 2eea626ab4 chore: update issues.jsonl 2026-09-20 00:46:13 -07:00
Edward Langley bfd70a08fa chore(beads): close WU-A's three beads — the wave is complete
improvise-anr9 and improvise-1125 closed by PR #23; improvise-s1xw closed as
absorbed, verified fact by fact rather than assumed.

anr9's close records the user's P1 and why it was larger than it looked: the
migration did not break a check, it OUTGREW one — the pre-anr9 code compared
slash-joined strings and never had cats to compare. And it was wrong end to end,
because grid_updates diffs against the framed mirror, so an apply that kept the
cells made the producer send nothing at all.

1125's close records that the ticket's stated home was unreachable:
improvise-wasm-client cannot depend on improvise-command, so putting the width
rule in viewport would have guaranteed the third copy the ticket exists to
prevent.

Wave 1 complete: 8 PRs merged, 1189 -> 1309 passing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 00:41:40 -07:00
Edward Langley 9bc59f6a28 chore(beads): close WU-C's five beads, file clause 4 and the drill-dirty bug
improvise-368h, gv9r, 955n and 7v62 closed by PR #26; improvise-08wb closed as
a duplicate of 955n.

955n's close records the finding that mattered most, and it was a regression the
work introduced: hoisting the basis check to the frame hoisted ALL THREE TIERS.
The document and layout tiers are frame-level facts; the record tier is not and
cannot be, because which records are at stake is a property of the command. An
independent reviewer confirmed the lost update by executing a real Host.

7v62 closed won't-fix with the compiler output on the type, which is what the
ticket asked for: a proof discharged before a mutation cannot be represented as
a borrow held across it.

improvise-yqpd filed — clause 4, transactional rollback — carrying the costed
design note, the two non-obvious acceptance criteria the P1 produced, and the
trade nobody had named: interleaving the record check with the applies would
close clause 4's silence for free, but it buys the legitimate double-write and
sells the loud failure on a broken chain.

improvise-748f filed — a drill exited with no edits, and view-back, mark the
document dirty. Unlike the LoadModel case, that is a wrong answer to no
divergence at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 00:29:54 -07:00
Edward Langley bcdef21123 chore(beads): file the leading-group-header trap found by the PR #23 review
improvise-0o6x P2 — body_from(idx) starts at the first ITEM, so a leading
FrameEntry::Group is skipped at offset zero. Pre-existing and byte-identical to
main, so the TUI has always done it and the characterization is honest — but a
COLLAPSED group first on its axis therefore has no header anywhere on screen in
either front end, and no way back.

It sits exactly where improvise-anr9's windowing rule says it must not: that
rule carries all headers between two in-window items precisely because a
collapsed group's header is the only affordance for getting its rows back.
Position zero is the one place the rule does not reach — not because the frame
omits the header, but because body_from drops it on the way to the screen.

The author knew: the agreement fixture orders Carol before Family to dodge the
case and says so in a comment. That knowledge lived in one comment in one test
and is now a ticket, because the moment web/app.js grows a click handler for
group headers, a leading collapsed group becomes a trap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 00:26:40 -07:00
Edward Langley 846a76b931 chore(beads): three follow-ups from WU-A, and the 4zl7 ruling
improvise-gd83  P3 — AxisEntry emits a group header on ENTERING a group and no
                boundary on leaving one, so an ungrouped item after a grouped run
                inherits it. Copilot found it, PR #23 declined it: it is
                GridLayout's pre-existing rule, the TUI paints the same span, and
                fixing it would destroy the exact-buffer characterization that
                was that PR's only evidence the migration changed nothing. The
                test already asserts the wrong answer with the corrected
                expectation written beside it.

improvise-jq8l  P3 — web/app.js calls scrollIntoView nowhere, so a selection can
                sit outside the rendered window. Pre-existing, surfaced by the
                viewport-reservation argument, and not group-specific.

improvise-plcx  P2 — the browser harness renders web/app.js in three browsers but
                web/demo.improv has no groups and one category per axis, so the
                multi-level, group-span and collapse paths PR #23 added are the
                one part of gridTable no test sees. Would be the first test here
                able to see that code in a browser.

improvise-4zl7 retitled and re-scoped to the user's ruling: a fitted tree must
route per record and aggregate the results. The question was never about
categorical splits — aggregate-then-route is not route-then-aggregate, so every
fitted tree means one thing on a record row and another one row up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 00:16:35 -07:00
Edward Langley 3ba35a0ed8 chore(beads): close WU-J's three beads, narrow xf1 to Safari, file the watchdog bug
improvise-vvl3, improvise-vhcz and improvise-hmws closed by PR #24.

vvl3's close records the finding that outlives the harness: a contract nobody
can execute is prose. storage_contract had been cited in gotcha 28 for months
as the spec OpfsFs must satisfy, and the first time OpfsFs was handed to it, it
failed on its FIRST assertion — on a semantic disagreement about what list
means, sitting in a trait doc everyone had read.

hmws needed neither of its two proposed fixes: an orphan-rule refusal is a fact
about where you are standing, not about the pair of types. The impl is simply
allowed in the crate that owns the trait.

improvise-xf1 narrowed rather than closed, and raised to P2. Chromium and
Firefox now run the full battery; Playwright's WebKit provides no OPFS at all,
so Safari is untested and the ticket's own 16.4+ claim is unverified. Parked as
a runtime probe-skip so it starts running when that changes.

improvise-xps8 filed: the handshake watchdog overwrites a genuine mid-session
disconnect eight seconds later. A third bug in the same family, and it survives
improvise-taxx's fix — confirmed by simulating that fix rather than assuming.
Not a CI guard: its trigger is the host's network stack, and an expected-failure
whose trigger is platform-dependent inverts CI's meaning rather than flaking.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 23:44:52 -07:00
Edward Langley 98e0c59ab9 chore(beads): close WU-L's four beads, correct improvise-1ey, file the date limit
improvise-odbc, improvise-7yx, improvise-g6s1 and improvise-q0vb closed by
PR #22, with the reasoning on each.

improvise-odbc's close records that the ticket's premise was FALSE and that
testing it is what made the ticket cheap: quote_name mirrors bare_name
character for character, so the round-trip property was complete and the
generator needed nothing. The file had been hand-damaged through the
v2025-04-09 migration.

improvise-1ey's design field named bank-info.improv, which PR #22 deleted.
roadmap.org:194 is generated from that field, so the fix belongs at the bd
source and the file is regenerated — Copilot found the stale line and WU-L
correctly declined to hand-edit a generated file.

improvise-l8rh filed: the ambiguous-date heuristic reads American-first, so a
European CSV imports with days and months swapped. Pinned rather than changed
on the user's ruling — but it is now wrong DETERMINISTICALLY, which PR #22's
HashSet fix is what bought.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 23:29:06 -07:00
Edward Langley bd3f89a74c chore(beads): close the four WU-I beads with their reasoning
improvise-oai9 closed on evidence with no code written — the report() helper
was already in the tree, and the fault was injected ({e:#} → {e}) before
closing rather than trusting the description.

improvise-r4lo, improvise-taxx and improvise-o8fa closed by PR #21. Two of the
three record that the TICKET was wrong rather than merely that the bug is
fixed: r4lo's offered escape hatch does not exist, and taxx's proposed
readyState mechanism cannot work because both cases arrive as code 1006 with
wasClean:false.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 23:22:15 -07:00
Edward Langley 553b2cb90d chore(beads): file the CSWSH hole, the records-mode minibuffer exit, and route vvl3's assertions
From WU-I (PR #21) and WU-G (PR #27) hand-backs. Searched the open backlog
first; none of these duplicates an existing bead.

  improvise-kc7n  P2 — improvise-ws-server never checks Origin. Verified
                  independently: `grep -rni origin crates/improvise-ws-server/src/`
                  returns nothing. A WebSocket handshake is exempt from the
                  same-origin policy, so any page an allowed user visits can
                  drive their session from their own allowed address — the
                  attack an IP allow-list structurally cannot see, because the
                  request genuinely is from an allowed client. Loopback is not
                  exempt and has no proxy in front of it, so the local path is
                  the least protected. Related to improvise-kpnr rather than
                  blocked by it: authentication and CSWSH are different
                  questions and an answer to either leaves the other open.

  improvise-sihr  P2 — every minibuffer mode entered from records mode exits to
                  plain Normal while the layout is still records. Affects `:`,
                  Ctrl-E and Ctrl-W; only the last is new. Declined in PR #27
                  because fixing one arm of a three-arm rule is not a fix, and
                  characterized there instead.

improvise-vvl3 also gains the four assertions improvise-taxx's fix owes, so
that ticket need not re-derive them — including the one that must fail against
the previous unconditional close handler, which is the guard and not the
decoration. Recorded there because vvl3 now owns the JS test-infrastructure
decision that PR #21 declined.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 23:21:28 -07:00
Edward Langley aa1534e078 chore(beads): file five follow-ups surfaced by WU-G
All from PR #27's hand-back, searched against the open backlog first and
none of them duplicates.

  improvise-mai9  P2 — a status message written while a minibuffer is open is
                  never drawn. draw_bottom_bar renders the minibuffer INSTEAD
                  of the status line, so improvise-6t3 carefully ordered a
                  failure message to win a race to a surface nobody can see.
                  Copilot then found the regression this causes, in the same
                  PR that documented the mechanism.
  improvise-z05r  P3 — CommitExport does not bail on an empty path and
                  ExportCsv does not abort the batch: the export arms of what
                  PR #27 fixed for save. Third instance of the same asymmetry,
                  so the structural fix may beat the third patch.
  improvise-g9jb  P3 — the text-entry mode list is hand-written in two crates
                  and checked against AppMode in neither, which now decides
                  whether a browser refuses to open a prompt it cannot commit.
  improvise-zx8t  P4 — draw.rs::mode_style matches AppMode with a wildcard, so
                  the repo map's "the compiler names every site" promise holds
                  in five places and not the sixth.
  improvise-rw3a  P3 — the open half of improvise-lae: whether ZZ routes into
                  the new save prompt, and what ZZ means once it is open.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 23:04:38 -07:00
Edward Langley 4abcdaaa64 chore(beads): close improvise-2yc2 and improvise-hkt6, file three follow-ups
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
CI / Browser bundle + static site (push) Has been cancelled
CI / Deploy to GitHub Pages (push) Has been cancelled
Closed by PR #20 (WU-K), with the reasoning on each bead rather than in a
commit nobody will read again.

Filed from WU-I's hand-back:
  improvise-kpnr  improvise-ws-server has no authentication, and the
                  reverse-proxy path the README now documents removes the
                  loopback binding that was its only control.
  improvise-j7v6  the typed-string classification lives in two crates, and
                  its guard test states the obligation in its name while
                  asserting three outcomes by hand.

Filed from WU-K's hand-back:
  improvise-44ad  ColWidths is redundant under the label fold — tried and
                  reverted, because the test it would rewrite is gotcha
                  33(c)'s standing evidence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 22:55:01 -07:00
Edward Langley 97ea48957e chore(beads): WU-O — rewrite the ADR 003 cluster and defer nine beads
The first pass of the whole-backlog campaign, applying the user's rulings
before any agent starts, so the tracker reflects reality while the campaign
runs rather than after it.

Deferred with the reasoning recorded on each bead:

  improvise-e0u   the in-process client/server split wants a campaign of its
                  own; it would conflict with every other work unit. Its cheap
                  cluster members (2ud, drg, s17o, i5tk) are being done now.
  improvise-ltq   epic; closes when e0u does, and every other child is closed.
  improvise-uq7   blocked by e0u.
  improvise-m91   stretch, and its natural home is the e0u restructuring.
  improvise-a5q   no concrete interop request; sits behind the ADR 003 direction.
  improvise-zavn  deferral re-affirmed; status set so it stops reading as ready.

Rewritten to context/adr/003-live-backed-models.md and deferred as a unit:

  improvise-rucf  the original text framed import-once vs live-backed as an
                  open choice and "read-only for now" as a scoping convenience.
                  ADR 003 decided both, the other way: live-backed, and
                  read-only as a feature. Now an epic over three filed children
                  — the .improv source specification and its grammar change,
                  the native-only SQLite source, and projection freshness for a
                  source an external writer can change.
  improvise-qddo  carries ADR 003's two new constraints: shape the trait around
                  the aggregate rather than the cells, or pushdown is
                  foreclosed; and keep writes out of the trait, so a read-only
                  source is unrepresentable rather than failing at runtime.
  improvise-mjy7  the interop claim is untouched by the ADR and still good, but
                  it carries the cluster's rusqlite dependency for a capability
                  nobody has asked for concretely.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 22:31:57 -07:00
Edward Langley 66df7056e3 fix(worker): give report() its own doc, instead of stealing start()'s
I inserted report() between start()'s fifteen-line doc comment and start
itself, so rustdoc attached the whole of it -- 'Boot the worker over OPFS',
the out-of-band naming argument, the reason the document is an argument
rather than a fourth ClientFrame -- to a two-line JsError shim, and left the
exported entry point undocumented.

Compiles either way, which is why nothing caught it: a doc comment is legal
on any item, so the only symptom is rustdoc nobody regenerated. Found by the
PR agent reviewing PR #17.

Documentation placement only, no behaviour change, so no test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 21:39:36 -07:00
Edward Langley d175b9934f docs(principles): a front end displays state; it does not reimplement behaviour
User ruling 2026-09-20: minimise the amount of new code a new UI framework
needs. Centralise logic in a redux-like store so a second or third front end
is a function from state to pixels plus a wire for events, not a second
implementation of how the application behaves.

Names the pieces that already exist as what they are -- Command is the
action, reduce_view and WidgetCache::apply are the reducer, ViewState plus
the caches are the store, Sessions::apply is dispatch -- and gives the
question to ask of any front-end code: would a third front end have to write
this again?

Three things make it concrete: the measure is lines of framework-specific
code and should trend to 'paint and wire'; a derivation done twice is the
defect even when both copies are correct today (improvise-edp is the worked
example, gotcha 44 is the same defect still open); and sharing a table beats
testing two copies of it, which is why the keymap is shared rather than
mirrored. Also says what legitimately stays local: painting, event wiring,
and a semantic-value-to-framework-type lookup like AxisColor -> axis_fg.

Sections 5-7 renumbered; the §6 testing references in repo-map.md and the
code-review skill updated with them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 21:31:58 -07:00
Edward Langley 62bc9bab51 fix(worker): report an error with its cause, not just its label
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
CI / Browser bundle + static site (push) Has been cancelled
CI / Deploy to GitHub Pages (push) Has been cancelled
The first real browser run of the deployed page produced 'Cannot open
demo.improv' and nothing else. That string is the CONTEXT vfs::open attaches;
anyhow's Display prints only the outermost context, so mapping through
e.to_string() kept the label and discarded the Storage or parse failure
underneath.

The worst possible place for it: bindings.rs is wasm-gated and is the one
module no test here can reach, so a devtools console is the only instrument
pointed at it -- and it was the code path discarding its own diagnostics.

report() renders with {:#}, which walks the chain. It lives in lib.rs rather
than bindings.rs so it can be tested at all. The test is written against the
old rendering and pins anyhow's Display as the trap, so the helper stops
being needed if that ever changes.

This does not fix the underlying failure; it makes the next reload name it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 20:55:27 -07:00
Edward Langley 4e1a79fe5f docs(web): from https the address must be wss://, and the server does not speak it
The page and the README told the reader that a ws:// address is blocked from
an https origin, and then gave them a ws:// address. improvise-ws-server has
no TLS at all -- no rustls, no native-tls, nothing -- so wss:// is not
obtainable from it and the documented pairing could not work from the
deployed site.

Both now say the address must be wss:// from the deployed page, that the
server speaks plain ws:// only so TLS has to be terminated in front of it,
and that the ws:// form works when the page is served over http://localhost.
The missing capability is filed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 20:54:04 -07:00
Edward Langley 78d82dd71d chore(beads): PR #13 review findings
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 20:35:38 -07:00
Edward Langley 21152b2c7a chore(beads): bpq2 closed; three successors filed
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
CI / Browser bundle + static site (push) Has been cancelled
CI / Deploy to GitHub Pages (push) Has been cancelled
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 19:36:49 -07:00
Edward Langley 6f84e3608b chore(beads): djm and hwpi closed; the browser-harness case
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
CI / Browser bundle + static site (push) Has been cancelled
CI / Deploy to GitHub Pages (push) Has been cancelled
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 19:29:58 -07:00
Edward Langley 4286bcc4fc chore(beads): improvise-9x6 closed; four shared-layer gaps filed
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
CI / Browser bundle + static site (push) Has been cancelled
CI / Deploy to GitHub Pages (push) Has been cancelled
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 19:10:26 -07:00
Edward Langley 4f5894db66 docs: the basis exemption is no longer a door
Gotcha 43 and ADR 001 both described apply_command/reduce_full as a
deliberate exemption with a hole in it. improvise-ondj closed the hole with
a type: Checked<'_>, whose only constructor runs the check. The principle is
unchanged -- a host holding the App is the truth, not a claimant -- but the
unchecked path is now unconstructible rather than discouraged.

Records why visibility was the weaker option (reduce_full has no production
caller, so pub(crate) would have made it dead code), that the regression
guard is the signature rather than a test, and the residual hole: a Checked
does not name which App it was checked against.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 18:54:44 -07:00
Edward Langley 5a6c35d4b6 docs(repo-map): gotcha 42 loses the mirror it was written to explain
improvise-xks6 consolidated three envelope vocabularies into one pair, so
the 'socket.rs is not wire.rs' half of gotcha 42 is now history. Replaced
with the question that had to be answered first -- do the worker and the
socket want the same vocabulary -- and the test applied to each difference:
whether the ARGUMENT for it names the thing they differ in. None did.

Also records why deleting both mirror tests was the measure of success: what
they checked is now type identity, and a surviving compatibility test after
a merge is evidence the merge did not happen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 18:52:59 -07:00
Edward Langley 181fdd00de chore(beads): Pages pipeline landed; d31 stays open on djm
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 18:51:33 -07:00
Edward Langley 1f4e5354de docs: six invented ticket IDs, and what the PR loop actually needs
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
Six improvise-* IDs cited in repo-map and ADR 001 never existed -- filed
with output suppressed and then written into the docs from memory. A dead ID
is worse than no ID, because it reads as a promise the follow-up exists. All
six corrected to the real tickets, and AGENTS.md gains the audit loop.

AGENTS.md also gains what PR #12 learned: requestReviews takes botIds and not
userIds, gh is shell-aliased to a different tool here, a Copilot review takes
about six minutes, and a finding can be CORRECT and still be wrong to fix in
that PR -- #12 applied a truncate panic (byte-identical on ASCII, so it cost
no evidence) and declined a padding bug in the same file that would have
rewritten the characterization tests which were the PR's only evidence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 18:26:59 -07:00
Edward Langley cba60893a9 docs: the audience follows the optimistic fold; the basis is coordination, not authentication
From the wave review. Gotcha 36 gains why every relay out of apply_model is
ToAll and why copying apply_switch's ToOthers is invisible from the
bystander's side -- a test asserting through a 'what do the others hear'
helper looks away from the asker by construction, which is how the bug
shipped. Gotcha 24 gains the incompleteness of the reset_cursor calls the
epoch observes. Gotcha 43 gains what Basis does and does not buy, and that
apply_command/reduce_full are a second DOOR rather than an exemption: the
struct variant made the basis impossible to omit from the message, not
impossible to ignore on arrival, and '..' is the syntax for ignoring it.

ADR 001 gains a 'what the check is not' section saying the same plainly, so
the unforgeability claim is not relied on later.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 18:05:32 -07:00
Edward Langley 52e82c7903 docs(adr-001): the ADR specified a check no client could satisfy
The important thing layer 1 found. ADR 001 says a batch carries the basis it
was composed against and never says how a client comes to KNOW one -- nothing
in the projection stream carried any of the three tiers, so the check as
written was satisfiable only by forging. Projection::Basis + BasisCache close
it, emitted after the grid updates because the counters describe the cells
those updates carry.

Also: a fourth force class (RecordUnstated), because 'any record the batch
touches' had to name whose list and the answer is the server's -- reading it
off the client's claim is a force by omission. And the two places the check
deliberately does not reach, with why.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:58:49 -07:00
Edward Langley 594e19607f docs(repo-map): the index narrows and the primary map orders; a fit names only what the document holds
Gotcha 18 gains matching_cells/matching_values and the reason the old hash
walk was not a tidiness issue -- the drill snapshot froze its records in that
order while pending_edits are keyed by row index, so a second process
addressed different records.

Gotcha 37 gains (d): a fit is storable only when the document can name its
features, the check asks the document rather than the name's shape, and this
is an interim because remedy 3 needs a scalar coordinate predicate the
formula language does not have.

Also corrects improvise-gp's dependency line, stale since improvise-4s8.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:45:33 -07:00
Edward Langley 74ac4cb6ab docs(AGENTS): write down the wave workflow
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
Most work here is done by waves of agents in worktrees, and every rule in
this section was learned by breaking it: the three orchestrator-only files
and why, that kb.pl additions must be batched because loadProgram replaces a
whole predicate, that tickets sharing a file belong to one agent, that a
worktree does not reliably branch from the orchestrator's HEAD, that direnv
status reports 0 for allowed, and that the code review's characteristic find
here is a test that passes while the thing it names is broken.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:42:46 -07:00
Edward Langley b195c3a096 test(wasm-client): the socket-framing check-the-checker was checking the tokenizer
`a_frame_named_the_other_envelopes_way_does_not_parse` exists to prove that
`the_socket_framing_matches_the_server` is discriminating: mirroring
`improvise-ws-server`'s two wire enums is only safe if a *renamed* variant
fails a test. The injected fault was not a rename.

    ours.replacen("Commands\":[", "Command\":", 1)

rewrites `{"Commands":[{"SetStatus":"saved"}]}` to
`{"Command":{"SetStatus":"saved"}]}` — the opening bracket is deleted with
the variant name and the trailing `]` is left behind, so the string is not
JSON at all. `is_err()` then held for a *syntax* error, and would have held
identically against a `ServerFrame` that really did carry a `Command`
variant. Proven by adding one: with `ServerFrame::Command(Vec<Command>)` in
the enum the old assertion still passed, because malformed JSON fails to
parse whatever variants exist. The checker was checking serde_json's
tokenizer, which is exactly `doctrine(check_the_checker)`'s failure mode
wearing the name of its cure.

The rename is now asserted well-formed before it is fed in, and the failure
is asserted to be `unknown variant` rather than merely a failure — the same
correction applied to the `Bootstrap` case, which was right by luck. Under
the injected variant the corrected test fails.

Second gap, same test: the round trip enumerates variants **by hand**, so a
variant added on the server side and never mirrored here fails nothing.
`every_server_variant_is_one_this_crate_mirrors` matches exhaustively over
`ClientMsg` and `ServerMsg`, so that is a compile error in this crate rather
than a runtime surprise in a browser.

1091 passing, clippy and fmt clean, and `cargo check -p improvise-wasm-client
--target wasm32-unknown-unknown` still clean — the guard lives in a test
module and `improvise-ws-server` stays a dev-dependency.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:39:46 -07:00
Edward Langley 7edc654f1e chore(beads): improvise-cr3 closed; framing ticket deduped
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:37:49 -07:00
Edward Langley c000f3a5e7 chore(beads): wave closures and findings
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
2026-09-19 17:35:04 -07:00
Edward Langley 901a99e8dc docs(adr-001): multi-document hosting landed; note the no-eviction rule that protects it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:33:16 -07:00
Edward Langley f327a146be docs(repo-map): gotcha 7 gains the unanimity rule for non-numeric aggregation
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:24:05 -07:00
Edward Langley 51b45eac17 docs(adr-003): a live-backed model is read-only, and that is a feature
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
User ruling: for many use cases editing capabilities would be a downside. Two
simplifications follow. It dissolves the ADR 001 tension by scoping rather than
amendment — ADR 001 governs concurrent edits and a read-only model has none, so
what remains is projection freshness, a smaller and different question. And the
machinery already exists: ADR 002 makes SetCell take a CellId::Record, so a
source projecting none makes a write unconstructible with no runtime check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:18:57 -07:00
Edward Langley d97a3afdaf docs: ADR 003 — a model may be backed by a live query source
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
Accepted but explicitly not a priority: the goal is the OPTION of using
improvise as a DB client. Written down now so the option stays open, since
several decisions in flight could foreclose it by accident.

The consequence worth the ADR: a live-backed document splits into our
configuration and their data, which .improv does not distinguish — and read
through the DB-client framing that is the natural shape, a saved query plus a
pivot configuration, i.e. a report definition. Also flags that an external
writer voids a guarantee ADR 001 currently states.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:16:29 -07:00
Edward Langley 770d35f127 docs(repo-map): gotcha 41 — Storage is not a VFS, and that closes the SQLite question
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
improvise-9ix declined on measurements: 22% larger than .improv, 4.7x gzipped,
160x at the floor, and still rewritten whole on every save. The generalisable
half is that a second serialization format duplicates the grammar and its own
round-trip test cannot detect drift from the first — the missing cross-format
equality test is what made the ticket look cheap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:08:47 -07:00
Edward Langley 9ff0b3f98b chore(beads): decide vjms — both panels show the virtual categories
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
2026-09-19 17:06:56 -07:00
Edward Langley bf7f0e0969 chore(beads): Bloom-filter analysis on the hover-preview ticket
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:01:33 -07:00
Edward Langley 6c5e58e445 docs(adr-002): CellId is a two-variant enum; drill addressing resolved
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
An aggregate is not the absence of an identity but a different kind of one, so
Option<CellId> became CellId::{Record, Aggregate}. That makes an invalid Drill
unconstructible too, not just an invalid SetCell, and it closes the drill
addressing question the Option shape had left open. Hover previews deferred.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 16:59:55 -07:00
Edward Langley f74800c799 docs(adr-002): the surrogate is a hash of the normalized record, resolved via a maintained index
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
Decided: hash the record (coord tuple minus _Measure), not the cell — it is
already ADR 001's atomicity unit and what record_versions is keyed by, so one
identity serves addressing and staleness. Resolution is a maintained index in
DataStore, which already interns names and already keeps a (Symbol,Symbol)
secondary index that makes invalidation tractable.

Records the three ways this breaks silently: a per-process-seeded hasher, a
derived Hash with no stability guarantee, and a width chosen for convenience
rather than for the consequence of a collision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 16:56:13 -07:00
Edward Langley 666423d5c8 docs: ADR 002 — how a client names a cell
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
An opaque surrogate on CellDisplay whose absence means the cell is an
aggregate, so a client cannot compose SetCell for something unsettable.
Records why _Index cannot serve: VirtualIndex is positional over the matching
set, so a stale one names the wrong record silently — but what it points at,
record identity, is ADR 001's unit, and one surrogate can serve both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 16:53:50 -07:00
Edward Langley 7eae2a9352 chore(beads): q08 closed; composability list recorded
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
2026-09-19 16:46:23 -07:00
Edward Langley e3f695cb55 chore(beads): keymap is client-only; invert the keypress-door ticket
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
2026-09-19 16:42:56 -07:00
Edward Langley bfb3d9c217 chore(beads): wave-5 closures and follow-ups 2026-09-19 16:40:38 -07:00
Edward Langley fd2820d23e docs(adr): conflicts scope to one document; multi-document hosting deferred
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
The server is expected to host several models, and sessions editing independent
models are trivially non-conflicting. That scoping falls out of the existing
shape: Sessions takes the App as a parameter rather than owning it, so it is
already bound to one document's generation sequence and multi-document hosting
is a registry above it. Recorded so nothing forecloses it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 16:32:31 -07:00
Edward Langley 68d849d409 docs(adr): a rejected edit must be forceable, and force means re-compose
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
User refinement. Force never bypasses the basis check -- it re-composes the
same intent against current state. Records what force can mean per conflict
class, the rule that force never creates structure, and the two obligations
this pulls into layer 1: keep the rejected values staged so force is not a
retype, and make a forced overwrite visible to the client it overwrote.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 16:15:04 -07:00
Edward Langley a08f24f323 docs: ADR 001 — concurrent edits between clients
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
Records the user's decision: record-level atomicity (the coordinate tuple
excluding _Measure, which record_row_identity already names) with optimistic
concurrency per batch, and a basis tiered by ModelChange so a shape change
invalidates in-flight cell edits without any lock.

Documents why a causal clock was rejected: the server already serialises, so
what is needed is staleness detection (one counter per unit) rather than
concurrency detection (a vector per client) — and no clock answers the policy
question of whether an edit into a deleted category should survive.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 16:10:08 -07:00
Edward Langley be7ddf3f84 chore(beads): wave-4 merge, PR #11 findings
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
2026-09-19 15:50:15 -07:00