Repository navigation
Agents
Mirrored from
CLAUDE.md(content sha256c0d3e2f7bdc5) byscripts/Sync-Wiki.ps1. Edit the source, not this page -- the next sync overwrites it.
BachelorPad+ — a Rust/Slint text editor. Notepad when you want it. More when you need it.
This file is the entry point, and it is deliberately short. Everything it says is a rule you need before you touch anything; everything else is one link away. If this file grows past a screen or two it has stopped doing its job.
- Ask the open questions before doing anything else. See "How a session opens", below. This is the rule most often skipped and it is the one that costs the most.
-
project/WORK_QUEUE.md— what is ready. Take the top item. -
prompts/rosettas/R011_WIRE_CAPABILITY_INTO_SHELL.md— required before touchingcrates/bp-ui/. It holds ten traps that have each cost somebody a session. -
project/NEXT_SESSION.md— what matters most, which is not the same list as what is ready.
You do not need to ask for any of these. They were granted once, on 2026-08-22, precisely so that a session spends its time on work rather than on round trips.
Do not ask which item, and do not ask whether to start. If the queue is empty, say so and stop — that is a real result and it means the next work needs a decision, not effort.
Take the whole item, including its documentation. "Whoever finishes an item updates the record as part of it" is not optional; a fix that lands without the record moving is how every stale sentence in this repository got there.
Hosted CI runs the gate on every push (ADR-0091).
Commit on the current feature branch once the hooks' -Quick pass, push, then
watch the run and report its result -- a red run is the failure it says it
is, not noise. Do not ask first.
When hosted CI cannot run -- a GitHub outage, or Actions blocked because a bill is unpaid -- the local gate stands in, and its green is the permission as it always was:
./scripts/Invoke-LocalCI.ps1 -Linux # ~7 min. Background it.Green on both legs, then commit, push and report.
Two things this authority does not cover, and both need a human:
- merging to
main, or opening a pull request into it — that is decision D13; -
git push --force, a rebase of pushed history, or anything that rewrites a commit somebody may already have.
If a row in docs/product/MENU_MAP.md would promise something this product
cannot keep, say so and remove it. Write the ADR; the ADR is the whole
point, because the decision is more valuable than the row would have been.
Two rows have gone this way and both were right:
- DOI Lookup — finding an identifier and resolving one are different acts, and only the first works offline (ADR-0006). ADR-0044.
- Research question, evidence, findings, methods, datasets — five rows that were the section headings of a research paper, written into the plan by the scaffold commit and never designed. ADR-0046.
A name that has sat in a plan long enough starts to read like a specification. The tell: ask where the name came from. If the answer is the commit that scaffolded the repository rather than a decision that chose it, it is a sketch that has been read as a plan ever since.
project/DECISIONS_NEEDED.md is a queue of questions only Daniel can answer.
Its top section is written to be pasted straight into a message.
Ask all of it in your first response, then start work anyway. Do not wait for the answer, and do not stop halfway through an item to ask one thing. The whole reason the file exists is that across five sessions, work has been blocked by decisions far more often than by effort — and the reason questions sit unanswered is that a session opens by reading rather than by asking.
If a question turns out to have an obvious default, it is not a question. Pick the default, say which you picked, and delete the row.
One home per kind of fact. A fact repeated in four files is a fact that will be updated in three. When you need to mention something owned elsewhere, link to it — do not restate it.
| File | Owns | Does not hold |
|---|---|---|
README.md |
What works today, for a reader who has never seen the project | Lessons, plans, or why a thing was decided |
ROADMAP.md |
Phase status, and what is blocked on what | Anything about a single feature |
DECISIONS.md |
The ADR index, and every lesson learned | Anything not yet decided |
docs/decisions/ADR-*.md |
One decision each, with its reasoning | Status of the work that implements it |
docs/product/MENU_MAP.md |
Every menu row and its state | Implementation detail |
docs/architecture/ARCHITECTURE.md |
Crates, modules, sizes, seams | Feature descriptions |
project/DECISIONS_NEEDED.md |
Open questions for a human | Answers — those move to an ADR and the row is deleted |
project/WORK_QUEUE.md |
What is ready to take, and collision rules | Why it matters |
project/NEXT_SESSION.md |
What matters most, and the manual-pass checklist | Anything a permanent file owns |
prompts/rosettas/R011... |
The traps in bp-ui
|
Anything about a specific feature |
Lessons go in DECISIONS.md and nowhere else. README may name one in a
clause and link; it must not retell it.
./scripts/Invoke-LocalCI.ps1 -Linux # the real one, both legs, ~7 min
./scripts/Invoke-LocalCI.ps1 -Quick # what pre-commit and pre-push run
./scripts/Install-GitHooks.ps1 # once per clone
./scripts/Drive-Window.ps1 -Kill # launch the app and photograph itHosted CI is the gate; this one is the fallback
(ADR-0091). .github/workflows/ci.yml runs
Invoke-LocalCI.ps1 itself on Windows and Ubuntu, on every push and pull
request, so there is one gate run in two places and the two cannot disagree. Run
it here when GitHub is unavailable or Actions is blocked, and before a push if
you want the answer in minutes rather than after the push. ADR-0016's premise --
"GitHub Actions is not available" -- became false silently
(ADR-0082), which is trap 6 for the second time;
ADR-0082's "private, so metered" then became false the same way when the
repository went public.
Every commit on main should have a green hosted run behind it. Keep it that way.
-
Background it, and do not edit files while it runs. The Linux leg
re-runs
cargo fmt --checkfrom scratch, so a mid-run edit fails the run for a reason that has nothing to do with your change. -
cargo fmt --allbefore you start it, always. -
Never pipe the gate through
tail,headorSelect-Object. A failure's detail is above the summary, so truncating the output discards the only thing a red run is worth reading. It happened once, and a failing test could not be named afterwards. - The hooks run
-Quickonly, and that is deliberate: hosted CI supplies the rest. When hosted CI is unavailable, the full-Linuxleg is yours to run and authority 2 depends on it -- a push made then, with its Linux leg never run, is a push that skipped half the gate.
- Windows 10, Windows 11 and Linux are equal targets (ADR-0001)
- Core editing never depends on cloud or AI services (ADR-0006)
- No custom cryptography — compose vetted primitives (ADR-0011)
- Notebook content never auto-runs (ADR-0011, ADR-0025)
- File renames and moves need explicit user approval
- No new dependency without saying so and giving the reason. Dependency choices are ADR material here.
- Keep the UI thread non-blocking
The repository has a strong and consistent voice. Match it.
- Comments explain why, not what. A comment restating the code is worse than no comment.
-
--rather than an em dash inside code comments. - Test names are full sentences:
fn a_no_op_must_not_create_an_undo_entry(). - British spelling in prose: behaviour, recognised, initialise.
- Every module has a
//!header saying what it owns and what it deliberately does not.
Named here, explained in DECISIONS.md:
-
A function reading the real profile directory means
cargo testwrites there. The fix is a field onAppState, not a function. -
Code taking a
Platformparameter must not letstd::pathanswer for it.bp_platform::paths::{file_name, join}exist for this. - A claim in a comment is not a property of the code. Ask: what would fail if this stopped being true? If nothing, the comment is a wish.
- Two claims about the same function coexist for as long as nothing asks. Trap 3 from the other side — when trap 3's answer is "nothing would fail", the test beside the comment is free to say the opposite, and probably does.
And four more, about verification and about where an answer hides (ADR-0049, ADR-0050, ADR-0056):
-
"Unique" must mean no earlier run either. A temp path from a process id and a counter is unique within a run and reused by the next one that gets that pid.
crate::testpathsis the one home. -
A claim in the record is not a property of the repository. D13 sat for five sessions on a premise one
gitcommand disproved. A question that has waited several sessions should have its premise checked before it is asked again. -
A round trip through one build says nothing about another build. Seal and open agreeing with each other is weaker than it reads, and it is what almost every format test actually asserts. The test that means something is a byte vector somebody committed before the change.
tests/integration/tests/what_an_earlier_build_wrote.rsholds one for each of the four files this product leaves on a disk;fuzz/tests/envelope.rsholds.bpadx's. -
Before asking a question, grep for its answer. Trap 6 pointed the other way: a decision recorded in a type is still a decision, and it is the kind least likely to be re-read, because nothing about a queue of questions suggests looking in
src/. D16 -- what should compute an embedding -- was described for two sessions as the largest unasked question in the project whilebp_security::Embeddingscarried its three answers, a mapping for all four profiles, and a doc comment reading "Phase 10 reads this" (ADR-0056).
And a ninth, which cost thirteen working menu rows (ADR-0080):
-
A constraint blamed on a dependency deserves the same scepticism as one blamed on the record — and it is cheaper to check. "
TextInput's caret is unreadable from here" was in five places, had shaped four ADRs, and was false.cargo checkdisproved it in fifty-five seconds.Two things make this its own trap rather than a variant of 6:
-
The answer was in
bp-ui's own prose.cursor_label's doc comment said "reachable only through a property marked internal and undocumented" — accurate, and the local decision built on it was right. "Reachable only through an internal property" and "not reachable" are not the same sentence. A local decision not to use a capability reads, three files away, as proof the capability does not exist. - A true sentence next to an unverified one lends it credibility it has not earned. ADR-0018 did check Slint's source — for composition, where it found a real refusal that still stands — and the caret claim rode beside it for eight sessions. The tell: one claim cites a file and line, the one next to it cites nothing.
-
The answer was in
And the one no test can catch: what a toolkit does with what it is handed.
Five defects have lived in that seam, including a signing passphrase typed
into the open document in plain text. 1,812 passing tests did not see it.
project/NEXT_SESSION.md §3 has the manual checklist; some of it needs a
person, and it says which parts and why.
Generated from dboles99/bachelorpadplus_rust@feat/phase-01-foundation-ui by scripts/Sync-Wiki.ps1. Licence: GPL-3.0-only.