27 Commits

Author SHA1 Message Date
Edward Langley 303a0388fb chore: stuff 2026-09-27 10:27:12 -07:00
Claude (agent-1xfb4) 676347674a chore(site): stop tracking generated site/ (improvise-1xfb.4)
site/ is web/assemble.sh's default output, and site/docs is
scripts/build-doc-site.mjs's. 27 generated pages under site/docs were
committed by accident and went stale. Nothing reads the tracked copy:
CI's build job assembles with `nix build .#site` into the store, the
harness assembles into harness/serve/, and build-web-release.sh
assembles into a temporary stage.

Remove site/ from the index, ignore /site/, and say so in
context/code/doc-site.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-24 20:22:23 -07:00
Edward Langley 90ccfb0154 feat: attach the assembled site to every release, and let the page name its build
improvise-oujb. A tagged release carried four native tarballs and nothing for
the web; the site shipped only by ci.yml's pages-deploy, on a push to main.

Pages keeps tracking main (user, 2026-09-22: "I like Pages showing the latest
main for now"), so this is a CAPTURE, not a second deployment: the tarball is a
self-hostable copy of the site as of the tag and a rollback target, not what a
visitor to the published site is running. pages-deploy is untouched.

Mechanism is dist's own [[dist.extra-artifacts]], so it survives `dist init`:
`dist plan` under the pinned 0.30.4 lists improvise-web.tar.gz beside the four
platform tarballs and `dist generate` leaves release.yml byte-identical. The
build command is scripts/build-web-release.sh, which calls web/build.sh and
web/assemble.sh -- the same two scripts ci.yml and a developer run, so there is
no second way to build the bundle. Its toolchain pins come from
scripts/web-toolchain-versions.env, which ci.yml's build job now reads too.

`github-build-setup` was measured and does NOT help: it injects into
build-local-artifacts only, which is the wrong job for a global artifact.

The page now names the build it was assembled from in #build-version, stamped
by web/assemble.sh with the same token as the bundles and verified by the same
scripts/assemble-stamp.sh gate. An unassembled tree honestly reads "unstamped".

Verified by running `dist build --artifacts=global` locally: the script ran,
the site assembled, and dist placed improvise-web.tar.gz in target/distrib.
Not verified: a real tag push, which would require cutting a tag.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-21 22:37:05 -07:00
Edward Langley 09ada9c1b5 test(harness): a grouped, multi-level fixture, and the first browser run of anr9
web/demo.improv has no groups and one category per visible axis, deliberately
(gotcha 46), so the multi-level header, the group span, the collapse indicator
and the inline group row -- exactly the code improvise-anr9 added -- had never
been rendered anywhere a test could see (improvise-plcx).

harness/fixtures/grouped.improv is served beside the shipped demo by
harness/build.sh; web/demo.improv is untouched. Two categories on each visible
axis, three row groups with one collapsed, two column groups.

A missing seed is NOT an error -- web/worker.js answers a 404 with an empty
model -- so every assertion is about labels the fixture really carries, never
that a grid exists.

Five injections, five distinct failures, patched in harness/serve so no
rebuild stands between the fault and the run: slash-joining the level tuples;
colSpan = 1 on the group headers; a stray cell on the inline group row;
indicator(false) hard-coded; and the collapse removed from the document.

One thing the ticket did not predict: the row frame's FIRST group header is
never drawn, so the spec asserts two group rows where the document has three,
with improvise-0o6x named on the line.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-20 16:34:24 -07:00
Edward Langley 1dd9fbda8e chore: ignore __pycache__, which the checker generates on every run
scripts/check-comment-runs.py is Python, so running it leaves
scripts/__pycache__/*.pyc — interpreter-version-specific bytecode that a wave
agent then committed, because nothing ignored it. Found by review on PR #41.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 10:27:27 -07:00
Edward Langley a550201fae feat(web): the page spawns the worker, opens a demo, and reads a key
Three pieces of the standalone deployment, all in web/.

improvise-djm — app.js picks its peer instead of assuming a socket. The
wasm bundle is the same artifact either way (improvise-xks6), so the
difference is confined to one factory: `?ws=` selects the websocket, its
absence spawns `new Worker('./worker.js?doc=&seed=', {type:'module'})`.
The websocket path is KEPT, because improvise-ws-server is how a
TUI-hosted document is shared and nothing else can reach it; the presence
of the parameter is the whole selection mechanism, since a second knob
naming the same fact is a second thing to get wrong.

improvise-bck — web/demo.improv, built against an assertion rather than
by flipping an axis. examples/demo.improv leaves Customer/Date on None,
so every cell is an Aggregate and nothing is editable. The new demo puts
exactly one category on each visible axis (Product on row, _Measure on
column, Quarter on page) and carries Profit = Revenue - Cost, so a
visitor sees the formula move beside the cell they typed in, with no
keystroke and no scrolling. Two tests hold it: tests/web_demo_model.rs
projects the whole grid through grid_of and asserts every CellId is
Record (ADR 002 — that is what `editable` means), and post_message.rs
drives the shipped bytes through the real seed -> VFS -> open -> Snapshot
chain. The first was proved discriminating by pointing it at
examples/demo.improv, where it names the aggregate at (0,0).

One category per axis is asserted too: GridCache slash-joins a
multi-level tuple (gotcha 44), so a second category on an axis puts that
known gap on the front page.

improvise-hwpi — all four. A keydown listener on window feeds
Client::on_key_event, so the classifier is reachable at last; a frame
that will not fold now writes to #status instead of only the console; the
socket has an error listener, so "not running" no longer reads like
"still connecting"; and close() has a re-entry guard. Plus one the ticket
could not have predicted: a worker whose start() rejects is caught inside
worker.js and fires no `error` on the main thread, so the same silence
was about to be rebuilt in the new path — a first-frame watchdog covers
both deployments, because "the peer never answered" is the one condition
that means the same thing on each.

Verified: cargo test --workspace 1159 passing (1155 + 4), clippy clean,
fmt clean, actionlint clean, ./web/build.sh end to end (345 KB / 123 KB
gzipped client, 1.33 MB / 498 KB gzipped worker), every call present in
the generated .d.ts with the right arity, every relative reference in the
assembled site resolving. NOT verified: there is no browser here. No line
of JavaScript in this commit has ever run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 19:28:27 -07:00
Edward Langley a1cc0dc86b chore: update gitignore 2026-09-19 17:51:55 -07:00
Edward Langley 7c5a3f6332 chore: drop unused root deps (pest/pest_derive/dirs), ignore tags+graph artifacts
improvise-v9w: the root `improvise` crate's [dependencies] listed pest,
pest_derive and dirs, none of which appear anywhere under src/. pest and
pest_derive remain genuine dependencies of improvise-formula and
improvise-io (persistence/improv.pest); examples/gen-grammar.rs reaches
pest_meta through [dev-dependencies], which examples do get, so the
examples still build. Verified with cargo build --workspace, cargo build
--examples, cargo test --workspace and the wasm check. Cargo.lock loses
only dirs and its transitive deps.

improvise-wg1: add the GNU Global tag files (GTAGS/GRTAGS/GPATH/GSYMS) and
the root graphviz artifacts (/graph.dot, /graph.html, /graph.svg) to
.gitignore. Named exactly rather than globbed so no checked-in asset is
swallowed; `git ls-files | git check-ignore --stdin` prints nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 13:31:38 -07:00
Claude 00565796e5 fix(hooks): make the tracker swap safe on every failure path
Review of #7 found the swap could corrupt or lose the database: the move
of the live database out of the way was unchecked, so if it failed the
next move put the new database *inside* the old directory and still
reported success; the restore was unchecked too, and the `rm -rf "$TMP"`
that followed could then delete the only remaining copy.

All three are real. The root cause was doing the critical swap across
filesystems, where a move is a copy that can half-finish.

install_tracker() removes the class instead of patching the cases. The
staged copy is brought alongside the live one first, so every step after
that is a same-filesystem rename: it either happens or it does not. The
previous database is removed only once the new one is in place, and every
move is checked, with a loud message if a restore fails and leaves it
parked. The caller collapses to one condition, which also removed a
duplicated pair of warning branches.

Proved rather than asserted, since this block has now been wrong twice.
`mv` was stubbed to fail at each point in turn:

  - old-move fails     -> rc=1, live database intact, no nesting, no leftovers
  - final rename fails -> rc=1, live database restored, no leftovers
  - missing source     -> rc=1, live database intact

A first attempt at the third case used chmod, which proves nothing as
root; the stub is what actually exercises the path.

The transient .incoming/.previous directories are gitignored so a crash
mid-swap cannot leave untracked noise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
2026-09-19 06:59:00 +00:00
Claude f16baec7cd chore(agents): address review on the session-start hook
Three findings from the review, plus a bug the first one uncovered.

Sync on every session, not only when the database is missing. Cloud
containers reuse a cached workspace, so "already present" does not mean
"still current" — the two runs in testing came back 169 and then 168
issues, which is precisely the drift that makes a skipped sync serve
stale data. Installing bd and warming the build still check first, being
genuinely one-time.

Making the sync unconditional exposed a worse failure than the one it
fixed. `bd init` exits non-zero when the remote is unreachable but still
leaves a .beads/embeddeddolt behind — an empty database. The old check
tested only for that directory, so a failed sync swapped an empty
tracker over a good one and logged success. Verified against a bogus
remote: the good tracker was destroyed. The sync now checks bd's exit
status and confirms the result actually carries issues before installing
it, and the swap parks the old database in the temp directory until the
move succeeds, so a failure keeps the previous tracker rather than
leaving none. Re-verified: the 168-issue tracker survives a failed sync
and the warning says so.

An empty remote tracker is indistinguishable from a failed clone here.
Keeping what we already have is the safe way to be wrong.

The bd installer pipeline redirected only `bash`, so a curl failure (DNS,
403, TLS) left /tmp/bd-install.log empty — exactly when it is read. The
whole pipeline is redirected now.

Also ignore *.gate.lock*: bd drops .beads.gate.lock beside .beads/ on
every run, which otherwise shows up as untracked noise in every session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
2026-09-19 06:26:54 +00:00
Edward Langley aabee3a460 chore: seed prolog knowledge base and track kb.pl
AGENTS.md tells agents to load the `kb` Prolog session at session start,
but prolog-sessions/ was fully gitignored, so the KB could never survive a
fresh clone -- every session started from an empty knowledge base.

Narrow the ignore to prolog-sessions/* with an exception for kb.pl, and
check in a seed KB built from context/repo-map.md and
context/design-principles.md: crate/layer/coupling rules, the
"where do I change X" location table, the Cmd -> Effect pipeline,
the load-bearing invariants, testing doctrine, and the beads workflow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V69PGQfMJuGRmtSWpfVAqn
2026-08-29 10:25:01 -07:00
Edward Langley 605d412225 chore: ignore prolog-mcp session directory
The prolog-mcp MCP server creates ./prolog-sessions relative to its
cwd, which is this repo root when Claude Code launches it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNMyVJTwXdcv369x98H98b
2026-08-29 10:16:59 -07:00
Edward Langley 72b42b9059 chore: ignore bd/dolt scratch files (.sqlhistory, backup.jsonl)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 23:44:03 -07:00
Edward Langley 08f190a036 chore: update gitignore 2026-04-15 22:45:18 -07:00
Edward Langley c79498b04b chore: update gitignore 2026-04-14 01:43:32 -07:00
Edward Langley 1ae6187285 chore: update gitignore 2026-04-14 00:50:29 -07:00
Edward Langley 70e7cfbef7 chore: update gitignore and claude instructions 2026-04-11 00:08:01 -07:00
Edward Langley 08df85664e feat: add examples/demo.csv and examples/demo.improv
40-row CSV with obviously-fake sales data (fictional companies like
Acme Corp, Wonka Industries, Cyberdyne Systems). demo.improv generated
via headless import with Profit formula and a default view showing
Region+Product on rows, Date_Month+Measure on columns. Added the
import command to the README quick-start section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:07:57 -07:00
Edward Langley 96d783a1c5 chore: update gitignore 2026-04-11 00:06:51 -07:00
Edward Langley f4e4b75b20 bd init: initialize beads issue tracking 2026-04-11 00:06:49 -07:00
Edward Langley 4f6c4aecd9 chore: update .gitignore
Update .gitignore to include patch and improv files.

Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
2026-04-11 00:06:48 -07:00
Edward Langley b2d633eb7d chore: update gitignore 2026-04-05 01:39:02 -07:00
Edward Langley 3d11daca18 chore: update gitignore 2026-04-05 01:09:37 -07:00
Edward Langley 038c99c473 chore: update gitignore 2026-04-03 23:07:13 -07:00
Edward Langley c5eab1f283 chore: update gitignore 2026-03-30 23:07:42 -07:00
Edward Langley f58ca625cc chore: misc nix/direnv changes 2026-03-30 22:29:12 -07:00
Ed L eae00522e2 Initial implementation of Improvise TUI
Multi-dimensional data modeling terminal application with:
- Core data model: categories, items, groups, sparse cell store
- Formula system: recursive-descent parser, named formulas, WHERE clauses
- View system: Row/Column/Page axes, tile-based pivot, page slicing
- JSON import wizard (interactive TUI + headless auto-mode)
- Command layer: all mutations via typed Command enum for headless replay
- TUI: Ratatui grid, tile bar, formula/category/view panels, help overlay
- Persistence: .improv (JSON), .improv.gz (gzip), CSV export, autosave
- Static binary via x86_64-unknown-linux-musl + nix flake devShell
- Headless mode: --cmd '{"op":"..."}' and --script file.jsonl

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:11:55 -07:00