Commit Graph

32 Commits

Author SHA1 Message Date
Cursor Agent ec30c8a7de fix: address three Codex P2 review findings
1. Shebang (Codex P2): scripts/agent-bootstrap.sh and .claude/hooks/session-start.sh
   now use #!/usr/bin/env bash instead of #!/bin/bash, matching repo convention
   enforced by checkers.sh.

2. Fail-closed bd pin (Codex P2): .claude/hooks/session-start.sh local path now
   exits 2 with a warning when pinned or running version cannot be parsed,
   instead of silently succeeding. An unparseable version is not a pass.

3. pr-reviews failure (Codex P2): scripts/pr-watch no longer uses '|| true' to
   swallow pr-reviews failures. If pr-reviews fails (fetch error, malformed
   response), pr-watch now exits 6 with a warning. Empty successful fetch
   (no reviews) is still exit 0.

   - Added exit 6 to usage text and skill documentation
   - Added test case 'pr-reviews failure exits 6' using malformed JSON fixture
   - All 57 pr-watch tests pass

Co-authored-by: Ed Langley <fiddlerwoaroof@users.noreply.github.com>
2026-09-26 01:30:00 +00:00
Cursor Agent 8dcbfbfc77 feat: align agent stacks (Claude, Cursor) on shared bootstrap
Unifies coding-agent setup across Claude Code and Cursor so both stacks get:
- Pin-matched bd from flake.nix
- Fresh Dolt beads tracker sync (not stale JSONL export)
- Same skills via symlinks to .agents/skills/

Key changes:

1. scripts/agent-bootstrap.sh: stack-agnostic bootstrap that handles bd
   installation, tracker sync, and optional cargo warming. Works for any
   agent stack (Claude, Cursor, or manual).

2. .claude/hooks/session-start.sh: now a thin wrapper that calls the
   shared bootstrap for cloud sessions, keeping only the local pin-check
   inline.

3. .cursor/environment.json: configures Cursor cloud agents to run the
   bootstrap via 'install'.

4. .cursor/skills/: symlinks to .agents/skills/* so Cursor agents
   discover the same skills Claude has.

5. AGENTS.md: adds Multi-Stack Agent Support section; clarifies that
   scripts/kb is the KB interface (Prolog MCP is not required and not
   available in all stacks).

6. context/cloud-agents.md: updated for multi-stack support; added KB
   section emphasizing scripts/kb over Prolog MCP.

The canonical skill source remains .agents/skills/. Both .claude/skills/
and .cursor/skills/ are symlinks to it, so there is one truth that both
stacks read.

Co-authored-by: Ed Langley <fiddlerwoaroof@users.noreply.github.com>
2026-09-25 18:59:25 +00:00
Edward Langley 2abf89ade9 skills: .claude/skills/{kb,pr-watch,code-review} become symlinks to .agents (improvise-5fzx)
The three were byte-identical hand-kept copies (same blob on gh/main), the
shape improvise-1f8e found drifted in wave. Done the way PR #175 did wave and
beads-merge already was.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-22 21:37:29 -07:00
Ed Langley ae96b8ccaa Merge pull request #179 from fiddlerwoaroof/fix/kb-guard-followups
CI / Build all bundles (push) Has been cancelled
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Browser JS unit tests (node --test) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
CI / Lint the harness crate (push) Has been cancelled
CI / Browser harness (OPFS contract + shell) (push) Has been cancelled
CI / Deploy to GitHub Pages (push) Has been cancelled
kb: count keys, tie the rm ledger to the file it describes, fix the guard's docs (ih3k, yea8, za97)
2026-09-22 21:29:29 -07:00
Edward Langley 4ca3a47b41 kb: refusal no longer offers committing the loss; skill and kb-tool.md describe the guard (improvise-za97)
The kb skill (both copies) now describes the HEAD comparison, the ledger,
exit code 3 and the recovery. kb-tool.md records the ih3k/yea8 arguments and
corrects the residue paragraph: .gitignore hides the leftovers, and there are
six, not one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-22 21:17:17 -07:00
Edward Langley b6d2c47ae3 wave skill: one home for the brief checklist and the agent rules (improvise-1f8e)
.agents/skills/wave is the one copy; .claude/skills/wave is a symlink to it,
as beads-merge already is. SKILL.md holds the brief checklist, agent-rules.md
the agent-side rules, and AGENTS.md points at both instead of carrying a third
list. The bd-generated Session Completion block is overridden in agent-rules.md
rather than hand-edited (improvise-x1uk).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-22 21:04:01 -07:00
Edward Langley 7ce778fca2 wave skill: the agents' standing rules move into the repo
.claude/skills/wave/agent-rules.md replaces /tmp/salvage/standing-rules.md.
SKILL.md and AGENTS.md point to it instead of restating it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-22 21:00:27 -07:00
Edward Langley cd8f3e3ec0 docs: a beads-merge skill, and the export the driver test regenerated
The skill covers the four things that bit us today: the per-clone git config
the driver needs (.gitattributes names it, but the driver lives in git config,
which a checkout does not carry -- without it git silently line-merges), how to
recover a file that already has markers in it, where genuine same-issue
divergence is reconciled (bd conflicts, against Dolt -- not reconstructed from
the JSONL), and the autostash trap that writes markers without anyone running
`git stash`.

Driver verified against a real conflict: plain text merge produced 3 marker
lines and 3 invalid JSONL lines; the driver produced 470 rows, 0 invalid, and
left the database byte-identical (470 rows before and after).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-21 14:22:58 -07:00
Edward Langley 0d847e9464 Review fixes: name the cap in the dispatch assertion, unstale the skill
Two findings from running /code-review on this branch.

The dispatch self-test's dirty arm asserted the run failed and named `f.rs`,
which is adjacent to the question -- a failure for any other reason naming that
file would have satisfied it. It now requires the cap's own message as well,
so the assertion names the question it means.

And .claude/skills/code-review/SKILL.md still told reviewers the flag "selects
rather than adds, so --check-evidence never runs the comment cap", which this
branch makes false. Grepped for every copy of the claim; that was the only one
outside context/. A doc that describes the script it gates is code, and leaving
it is the gotcha-48 failure exactly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-20 22:16:07 -07:00
Edward Langley 95839fce43 docs: gotcha 49 for the origin check, and the inventory is a dated snapshot
CI / Build all bundles (push) Has been cancelled
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Browser JS unit tests (node --test) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
CI / Lint the harness crate (push) Has been cancelled
CI / Browser harness (OPFS contract + shell) (push) Has been cancelled
CI / Deploy to GitHub Pages (push) Has been cancelled
Gotcha 49 records improvise-kc7n: a websocket handshake is exempt from the
same-origin policy, so no network control can see cross-site hijacking — an
IP allow-list, a private network and loopback binding all answer "may this
client reach the endpoint" and the honest answer is yes. Loopback is the
least protected case, not the safest. Records why all three arms are decided
rather than defaulted, and that none of it is authentication.

The File Inventory line counts were audited: 26 of 61 resolvable entries had
drifted past 10%, several by a third. Refreshed, and relabelled as a dated
snapshot with the method, because false precision in a carefully-checked
document is worse than an obvious estimate. Two traps found while doing it,
both recorded: a path-walking refresh resolves a bare name like lib.rs
against the first matching crate and rewrites the wrong number, and the
ws-server/ entries use a shorthand prefix no such walk resolves at all.

The code-review skill's gate list now runs BOTH spellings of
check-comment-runs.py. The flag selects rather than adds, so --check-evidence
never runs the comment cap and does not say so; three separate agents hit it
in one campaign (improvise-ijql).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-20 21:36:29 -07:00
Edward Langley 3c5836c9d9 Merge remote-tracking branch 'gh/docs/wave-skill'
# Conflicts:
#	prolog-sessions/kb.pl
2026-09-20 19:39:51 -07:00
Edward Langley debad21556 docs: run the gate CI runs, and bindings.rs is run
Two one-line corrections the wave's last review surfaced, both about a claim
being stated in more than one place.

.claude/skills/code-review/SKILL.md told every reviewer and every wave agent to
run `cargo clippy --workspace --all-targets` with no `-D warnings`. Clippy exits
0 on warnings, so the committed local gate was green on exactly the input CI is
red on. This is not one person's shell mistake, it is the instruction: a
reviewer following the skill as written would report clean and be wrong, which
happened on PR #71. Also adds --no-fail-fast, since fail-fast hides blast radius.

README.md was a sixth copy of "bindings.rs is never run", which PR #70 corrected
in five places -- its grep missed README, and PR #72 then rewrote the sentence
four lines above and carried the wrong half forward. Neither PR is at fault
individually; it is an interaction between two branches, and it is the exact
failure the corrected fact is a monument to.

Plus doctrine(a_weak_check_and_a_strong_one_agree_until_the_defect): five
instances this wave, one mistake -- a check written against the failure you
imagined rather than the outcome the user sees. The weak and strong checks agree
on every input anyone tried and diverge only on the input that is the defect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-20 17:24:17 -07:00
Edward Langley 5c3c796308 docs: a wave skill, so the brief stops being retyped
Four agents were dispatched without worktree isolation and all edited the same
working tree: one git status held one agent's effect.rs, another's
harness/build.sh and a third's scripts/node-test.sh. Any commit would have swept
up three units' half-finished work.

The brief had carried that rule before and lost it, which is what a retyped brief
does. It is now a skill: worktree isolation first, partition by crate rather than
ticket count, the base commit by name, the baseline, the never-write list, the
injection discipline, pr-watch --expect, and attribution.

Two doctrines: every_wave_agent_gets_its_own_worktree records the tell and the
per-agent recovery (copy only what an agent can PROVE it touched -- two units can
legitimately both be in improvise-protocol); the_orchestrator_delegates_substantial_work
records the user's rule and the non-obvious half of it, that orchestrator work
lands in the shared primary tree and collides with agents who own that ground.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-20 16:40:32 -07:00
Edward Langley fc6d229dda kb: design against the model before implementing, and fix the key rule it exposed
AGENTS.md has always said Prolog is a design tool for reasoning about tickets;
it was the least-used line in the file, because kb.pl was only ever written
retrospectively. `kb check --with <design.pl>` merges proposed facts into a
throwaway candidate and runs kb_consistent over the union, so a design can be
refused before any code exists. Nothing is written either way.

The model already derives rather than records -- illegal_dependency, may_use,
depends_transitively, portable, snapshot_safe, view_state_admits,
record_obligation, and eleven kb_error classes. Three proposals exercised it in
one sitting: one added dependency edge produced SIX layering violations through
the transitive closure; a proposal to declare decision/3 functional was refused,
correctly, because improvise-907 carries two decisions; and a mis-ordered
layer/2 argument was named rather than accepted.

Trying to model a design found a real defect in the tool. Keys were the functor
plus all arguments but the last, which is right for doctrine(Name,Text) and
wrong for any RELATION: crate_depends_on(A,B) keyed on A, so a crate depending
on two crates read as one key twice and was unaddable. It cannot be read off the
KB's functional/1 -- that keys an arity-3 predicate on its first argument, right
for crate/3 and wrong for decision/3 -- so key_arity/2 is a table, and anything
unlisted keys on the whole head, which is the safe direction.

kb-test.sh is 20 cases.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-20 16:16:31 -07:00
Edward Langley 7fd57c06b3 docs: say what to do; attribute what agents post
Two user rulings.

Documentation should not spend itself on history -- that is bloat. Prose docs
now lead with the rule and keep only the line that stops it looking arbitrary;
the worked examples and the how-we-found-it move into doctrine/2 text, which is
what that predicate is for and where a reader goes deliberately rather than
while looking for a command. Trimmed: both skills, context/code/kb-tool.md, and
the kb paragraph in AGENTS.md.

Anything an agent posts to GitHub goes out under the owner's account, so every
comment, review and issue body now opens with a line naming the model and that
an agent wrote it. A PR body's generated-with footer already did this; comments
carried nothing, and an unattributed one reads as his own words to every reader
-- including a reviewer deciding how much weight to give it. The reply on PR #68
has been amended in place.

improvise-xfsb files the structural fix: a GitHub App or machine user so agents
post under their own identity. An attribution line is a convention an agent can
forget; a separate identity cannot be.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-20 16:04:33 -07:00
Edward Langley 661ca76e6d kb: close the directive bypass, and package both tools as skills
Review on PR #68 found two real holes, both reproduced by injection before
fixing and both now regression-tested:

- A DIRECTIVE is callable, so the callable check let one through -- and the
  candidate is validated by CONSULTING it, so a delta of `:- halt(0).` exited
  the check with success before kb_consistent ran. The gate passed by never
  running, and the directive landed in kb.pl. Directives and queries are now
  refused by shape.
- Keys were checked only against the EXISTING file, so two clauses sharing a
  previously-unused key clashed with nothing and the merge dropped the first --
  add discarding part of its own payload. Visible in the tool's own output ("2
  in delta" against one clause added) and nothing compared those numbers.

Also from the review: nothing ran kb-test.sh. A suite that gates the only writer
of kb.pl but is invoked by no workflow is doctrine(an_advisory_gate_is_not_a_gate)
in the PR that adds it. Both suites now run in the kb job.

AGENTS.md now leads with the rule instead of describing the retired procedure
first -- the review read against the earlier head, but it was half right at the
later one too: a skimming reader followed the batching paragraph.

Both tools are packaged as skills, because the failure is not that the scripts
are missing, it is that agents do not know they exist and write their own. Three
hand-rolled PR watchers in one session all reported green while wrong, which is
what scripts/pr-watch and its eight fixture cases exist to stop. One of those
cases -- a head that moves mid-wait -- was untestable until a subshell bug in the
fixture seam was fixed: fetch ran inside "$(...)", so its counter never advanced
and every poll re-read the first fixture.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-20 16:02:57 -07:00
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 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 3c39846c31 fix(hooks): assert the bd on PATH is the bd flake.nix pins (improvise-pauh)
A git worktree does not load the flake pin: its .envrc is unallowed, so a
process there falls through to whatever bd the profile carries. Today the
two agree, which is why nothing has broken; nothing enforces that they
keep agreeing.

The failure mode is silent — a mutating bd of the wrong version empties
the dependency graph in .beads/issues.jsonl and reports success. Section 0
of the session-start hook now reads the pinned version out of flake.nix
(never a second copy, which would rot when the pin moves), compares it to
`bd version`, and prints a banner and exits 2 on any disagreement. An
unreadable pin or an unreadable version fails the same way: an assertion
that cannot answer is not a pass.

It blocks nothing — reading with a skewed bd is safe — and it runs before
the CLAUDE_CODE_REMOTE gate so local worktrees, where the problem lives,
are covered. The cloud path re-checks after installing bd and still
completes its setup before the non-zero exit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
2026-09-19 17:29:29 -07:00
Claude 3fd775d3c6 fix(hooks): guard mktemp so a failure cannot stage at the filesystem root
Review of #7: `TMP="$(mktemp -d)"` was unchecked. With TMP empty,
"$TMP/$DB_NAME" is not a relative path under a temp directory — it is
`/improvise`, an absolute path at the filesystem root, which the hook
then creates and clones into. The cleanup at the end degrades to
`rm -rf ""`, so the real temp directory, if any, leaks too.

The mktemp now sits in the condition that guards the whole block, so a
failure skips the sync with a warning instead of proceeding with a broken
path, and no temp directory is created at all when there is no origin to
sync from. Putting it in the condition also avoids reindenting the body,
which keeps this diff to the thing it fixes.

Verified by stubbing mktemp to fail: the warning is printed, the sync is
skipped, and nothing appears at /improvise. The normal path still syncs
169 issues with the repo clean.

This is the fourth finding in this family on this branch — unchecked
status on an operation whose failure produces a plausible-looking wrong
value rather than an error. The others were bd init leaving an empty
database behind, the unchecked mv that nested the new tracker inside the
old one, and jq's status being swallowed by a process substitution.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
2026-09-19 07:37:24 +00:00
Claude 9aa17ba8b2 fix(agents): pin the bd installer and stop hiding jq failures
Two findings from the review of #7.

The hook piped an unpinned installer from beads' `main` straight into a
shell. That runs code nobody has seen, from a URL whose contents can
change under us, and leaves nothing to inspect when something goes wrong.
It now downloads first and runs the file, so /tmp/bd-install.sh is
readable afterwards, and pins the revision: the peeled commit of tag
v1.3.0, which is the version this work was done against. The comment
carries the ls-remote incantation for bumping it. Byte-identical to
`main` today, so the pin costs nothing now and is the whole point later.

The seeding script read its input with `mapfile < <(jq …)`, and a process
substitution discards the exit status of what ran inside it. A malformed
or unreadable flake.lock therefore produced an empty list, which the next
branch reported as "no github-sourced inputs; nothing to seed" and exited
0 — the script would claim success having seeded nothing. jq's output is
now captured with the status checked, and emptiness is a separate case
from failure.

Verified by exercising the paths rather than reading them:

  malformed flake.lock -> error names the file, rc=1
  valid lock, no github inputs -> "nothing to seed", rc=0
  missing flake.lock -> error, rc=1
  installer download -> /tmp/bd-install.sh matches f45b249 byte for byte

shellcheck -S style clean on both scripts; the real seeding run still
completes with flake.lock unmodified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
2026-09-19 07:26:54 +00:00
Claude 16f6462424 chore(skills): rename design-review to code-review so Copilot picks it up
GitHub's docs are explicit: "if you want to ensure that Copilot code
review will read and use a skill, use a review-focused skill directory
name such as code-review". They also list .claude/skills alongside
.github/skills and .agents/skills as recognised locations, so the skill
was already in a place Copilot reads -- just not under a name that
reliably signals what it is for.

Renaming it means it owns the repo's review entry point, so it should
cover the whole job rather than only conformance. It now opens with two
passes: correctness first, then whether the change fits the design. That
also removes a self-reference, since the old text told the reader to run
the generic /code-review, which is now this.

The description is rewritten to lead with "review a pull request or
diff", which is what a reviewer's relevance matching has to match on.

Note for humans: `/code-review` in a Claude Code session on this repo
will now resolve to this skill rather than the built-in one. The CI
workflow is unaffected -- it invokes the plugin explicitly as
`/code-review:code-review`.

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 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 f0729e9e5b chore(hooks): document the deliberate SC2016 in the env-file write
shellcheck flags the single quotes around the PATH export, but they are
the point: $PATH and $HOME must reach CLAUDE_ENV_FILE literally so they
expand when the session sources it rather than at hook time. Marked with
a disable directive and a comment so the next lint run stays clean and
the intent is not mistaken for a bug.

Both scripts now pass `shellcheck -S style`, run out of the flake's own
pinned nixpkgs via the seeding script this branch adds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
2026-09-19 06:53:34 +00:00
Claude c994f1e8cf Merge Copilot's SC2015 fix into the nix-seeding work 2026-09-19 06:52:40 +00:00
Claude 8cf8f01f12 feat(scripts): make nix develop work in cloud sessions without touching the flake
Nix's `github:` fetcher wants a tarball from
github.com/<owner>/<repo>/archive/<rev>.tar.gz, and that endpoint is not
served in a cloud session. Three things that look like fixes are not:

  - Raising the environment's network access to Full, or allowlisting
    codeload.github.com, changes nothing: GitHub traffic bypasses the
    domain allowlist entirely and goes through a proxy that scopes API
    and release-asset requests to repositories attached to the session.
    An input from any other owner gets a 403.
  - Attaching the dependency repos is refused -- cross-tier adds are not
    supported once the session has sources from another owner.
  - Nix has no setting that makes `github:` inputs use the git transport.

The lock's own integrity machinery is what works. flake.lock records a
narHash per input, and Nix looks a source up in the store and its
substituters before fetching anything. The git and tarball fetchers
produce byte-identical trees for a revision, so prefetching the locked
rev over git+https lands the input at exactly the store path the lock
names:

  lock narHash:  sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=
  git prefetch:  sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=

scripts/nix-seed-flake-inputs.sh does that for every locked input. It
reads flake.lock and never writes it. Nix still verifies each tree
against the recorded hash, so this satisfies the lock rather than working
around it -- a mismatch fails loudly. Rewriting the lock's nodes to `git`
type and carrying the old narHash across would have been the opposite:
that hash is the claim the lock exists to make.

Verified from a fresh clone with a cleared git cache: 15/15 inputs
seeded, then `nix develop` gives rustc 1.98.1, cargo and crate2nix, with
flake.nix and flake.lock unmodified.

Not wired into the session-start hook: it costs minutes and several GB
once the devShell builds, and most work only needs the preinstalled cargo
toolchain.

Corrects three places that claimed nix could not work here --
context/cloud-agents.md, the design-review skill's kb.pl check, and the
hook's environment notes -- all written before the git transport was
tested. The doc also records the two alternatives, a binary-cache
substituter and a self-hosted environment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
2026-09-19 06:52:17 +00:00
copilot-swe-agent[bot] 1bd628dde4 fix(hooks): use if/else for cargo build result check (SC2015)
Co-authored-by: fiddlerwoaroof <808745+fiddlerwoaroof@users.noreply.github.com>
2026-09-19 06:36:31 +00:00
Claude 7936844b6c chore(agents): add a design-review skill grounded in the repo's own doctrine
This repository states what "correct shape" means in more detail than most:
design-principles.md has the architectural intent, repo-map.md carries 31
numbered gotchas, and prolog-sessions/kb.pl holds 71 invariant/doctrine/
decision facts that CI checks as code. A reviewer who has not read all of
that reviews generically, and the generic findings are the shallow ones.

The skill is an index, not a copy. It points at those four sources and
says which parts matter for which kind of diff, because restating the
rules here would let the copy drift from the files that are maintained.

What it adds on top of the pointers is the shape of the review: command
purity, effect classification (kind() has no default, but "present" is not
"correct" -- doctrine(derive_then_verify) records three effects
mis-classified), exhaustive matches over AppMode/Axis/CategoryKind, the
two_questions_one_enum rule for a variant that shares one property but not
another, crate layering straight from forbidden/2, and the invariants no
type carries -- CellKey constructors, IndexMap order, render-only
rounding, stale layout.

The testing section is the one most worth having written down: a bug fix
needs a test that failed before it, and the three doctrines that say when
a green suite is not evidence -- check_the_checker,
populate_before_roundtrip, binding_dump_diff.

Deliberately complements /code-review rather than replacing it. That one
hunts correctness bugs; this one asks whether a change fits the design.

Every kb.pl and gotcha reference was verified to resolve. Merged
origin/main first so the references to gotcha 31 and the removed
Effect::changes_mode() are accurate on this branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
2026-09-19 06:30:39 +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
Claude 9ada03bd8d chore(agents): add a session-start hook for cloud sessions
A fresh Claude Code on the web container starts in a state where three
things are slow or wrong, all of which cost real work this week.

`bd` is not installed, so the existing `bd prime` SessionStart hook and
every bd command in AGENTS.md silently do nothing.

`.beads/issues.jsonl` is a stale export, and an agent with no database
reads it as if it were the tracker. It showed 121 issues against the
remote's 168, with all four P1 bugs OPEN though every one was fixed and
closed. Two beads were implemented from scratch on the strength of it;
both were already closed on the remote and already implemented on a
branch that had not yet merged.

The Rust workspace is uncompiled, so the first `cargo test` pays a cold
build before reporting anything.

The hook installs bd, syncs the Dolt database from the git remote, warms
the build, and prints the handful of environment facts that are not
discoverable from the repo.

Syncing needs care, because `bd init` in this repo rewrites AGENTS.md,
CLAUDE.md and .claude/settings.json, installs Cursor/Codex files, and
commits the lot. `--skip-agents --skip-hooks` stops all of that but it
still commits a .gitignore change, so the hook runs it in a throwaway
directory and copies only .beads/embeddeddolt back. Nothing it does
touches a tracked file, stages anything, or creates a commit.

Also adds a permissions allowlist for the read-mostly cargo, bd and git
commands an agent runs constantly, and context/cloud-agents.md for the
detail behind the hook — including the bd sharp edges worth knowing:
`bd dolt push <remote>` ignores its argument and pushes to origin, and
`bd dolt pull` into a divergent database refuses to merge.

The hook exits immediately unless CLAUDE_CODE_REMOTE=true, so local
checkouts are unaffected. It is idempotent: a second run is a no-op
except for the build warm, and re-running costs 0s.

Validated by running it against a clean clone: tracker syncs to 169
issues, .beads/config.yaml is left alone, no commit is created, and
`git status` shows nothing beyond the hook's own files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
2026-09-19 06:15:01 +00:00
Edward Langley f4e4b75b20 bd init: initialize beads issue tracking 2026-04-11 00:06:49 -07:00