| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
| |
Move the three finished tasks into Resolved: the bin/ai characterization-and-refactor hardening, the inbox-boundary Stop hook, and the before-close-queue convention.
|
| |
|
|
| |
Both built, tested, and pushed this session: the task-boundary inbox Stop hook (soft-nudge) and the 'the list' before-close queue plus colloquialisms convention.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
convention
Home proposed two linked cross-project norms and Craig approved rolling them out. Both now ship in the template so every project inherits them.
A new "Colloquialisms and Expansions" section in protocols.org maps Craig's shorthand to a defined action the agent applies without asking. Two seed entries: "put X on the list" appends to a session-scoped Before-Close Queue, and "tell <project> <message>" drops the message in that project's inbox via inbox-send. The set is extensible.
The queue lives under a "* Before-Close Queue" heading in the session anchor, so it resets when the anchor is archived at wrap. Anything that must outlive the session stays a todo.org task. wrap-it-up Step 1 gets a sub-step that works the queue oldest-first before the Summary, so any resulting edits ride the wrap commit, with unfinished items surfaced in the valediction rather than dropped.
I settled three design calls. The reference lives in protocols.org rather than a per-project notes.org (home's local reference), since a synced section is what makes it a shared norm. The queue lives in the session anchor, as home did. The wrap step sits at the front of Step 1 rather than a new half-step, which keeps the "Steps 1 through 5" framing intact. A documentation-integrity bats guards the wiring and the before-the-Summary ordering against a future edit or sync dropping it.
|
| |
|
|
|
|
|
|
| |
The "check inbox/ at every task boundary" rule was prose-only in protocols.org, so it held only as well as the agent remembered it and a handoff could pass a turn unseen. inbox-boundary-check.sh is a Stop hook that backs the rule: when inbox-status -q reports pending items it blocks the yield once and injects the count, so the agent processes them before returning.
It soft-nudges rather than hard-blocks. On the harness re-entry (stop_hook_active: true) the hook steps aside and lets the turn end, so a mid-task pause to ask a question, or an item the agent genuinely can't process, never wedges the session. A hard block would instead push inbox processing ahead of that clarifying question.
The hook self-skips where it doesn't apply: no inbox/ dir, no inbox-status, or a clean inbox each exit 0 silently, so one global hook covers every project with no config. It prefers the project-local .ai/scripts/inbox-status and falls back to one on PATH. It's wired into the Stop array ahead of ai-wrap-teardown in both the tracked settings.json (which the live ~/.claude/settings.json symlinks to) and the documented snippet. bats covers pending, clean, the re-entry step-aside, no-inbox, and the absent-status degrade.
|
| |
|
|
| |
Netted the 17 uncovered functions (launcher tests 9 to 42), extracted four pure decision cores with Normal/Boundary/Error sets, and dispositioned the footgun audit plus refactor pass. shellcheck clean, shfmt consistent, suite green before and after.
|
| |
|
|
|
|
|
|
| |
I moved the decision logic out of the launcher's I/O-coupled functions so it's unit-testable on plain inputs, the hardening the audit called for. Three pure cores now carry logic that was buried in git and tmux calls: _git_prep_action classifies a repo's state into none/pull/report, _order_windows sorts the window listing, and _match_window_id resolves a name to a window id. _git_is_dirty replaces the staged/unstaged/untracked check that was copy-pasted across three functions.
prep_git_single and auto_pull_if_clean both route their pull-or-report choice through _git_prep_action, so the "never auto-pull a dirty repo" rule lives in one place with its own test instead of two parallel branches. sort_windows keeps its two-pass move (park high, then reassign) but hands the ordering to _order_windows, and find_window_id is now a thin tmux wrapper over _match_window_id.
Each core gets a Normal/Boundary/Error set: the git-prep truth table including diverged and behind-while-dirty, the window ordering including whole-line-not-substring matching. The 42 launcher tests stay green and a live black-box run still prints the right launch command for claude and codex, so the extraction is behavior-neutral. I also ran shfmt (-i 2 -ci) to match the house style the other bin/ scripts follow.
|
| |
|
|
|
|
|
|
| |
Brings the session launcher under characterization tests ahead of the hardening refactor, so the pure-core extractions and footgun fixes land against a green net. The tmux/git-coupled functions can't be reached by running the launcher black-box, so I wrapped dispatch in main() behind a run-vs-sourced guard: run directly it dispatches as before, sourced it exposes the functions to the tests.
The net pins current behavior (record-not-spec) with a Normal/Boundary/Error set per unit: git_status_indicator across no-upstream/ahead/behind/dirty/clean, candidate discovery and filtering, selection parsing, and functional tests over create_window, find_window_id, and sort_windows ordering against a throwaway tmux server. Every tmux call runs on a private socket so nothing touches the live ai session, and the throwaway repos disable git auto-maintenance to dodge the teardown race that flaked rename-ai.
Also cleared the two pre-existing shellcheck warnings on the touched paths: quoted the "@{u}" revspecs (SC1083) and marked the intentionally-literal display tilde (SC2088). Both are behavior-neutral, and the existing runtime tests confirm the launch path is unchanged.
|
| |
|
|
|
|
| |
criteria
The task was phrased as an absence ("find bugs until none visible, refactor until nothing worthwhile remains"), which fails the verifiable gate and can't be :solo:. Rescoped it with the four-move pattern just landed in todo-format.md: bound the surface (the 22 functions, split covered/uncovered), net the behavior with characterization tests (pure cores extracted where tmux/git coupling blocks it), disposition every finding (a per-function footgun audit plus a /refactor pass), and gate on an objective floor (shellcheck, shfmt, green suite, per-function coverage, plus ~3 functional tests over the launch pipelines). The qualifying answer is a dispositioned report, not a claim of zero bugs. Split the interactive runtime picker out of the :solo: scope, since it carries a design call rather than hardening.
|
| |
|
|
| |
The two "refuses when..." tests failed intermittently under full-suite load, always in teardown with "rm: cannot remove .git: Directory not empty." A git command in the setup or body armed background auto-maintenance (git runs maintenance run --auto after a commit), which wrote into .git after the test body and raced teardown's rm -rf. Disabling gc.auto and maintenance.auto in the throwaway repo, before any git command can arm them, removes the background writer. Diagnose-not-mask: kill the writer rather than retry the rm. Verified with 30 repeated file runs plus two full-suite runs, all green.
|
| |
|
|
| |
The Justify gate's ticket-quality check now names the absence-phrased open-ended goal as the specific case where acceptance criteria aren't concrete, and routes it to the measurable-acceptance conversion in todo-format.md before work starts rather than into an implementation with no definition of done.
|
| |
|
|
|
|
|
|
|
|
| |
A task phrased as an absence ("find bugs until none remain," "refactor until nothing worthwhile is left," "clean it up") can't be :solo:, because it fails the verifiable-by-the-agent gate. Absence isn't falsifiable, so "done" is a judgment call, which is what :solo: forbids. This kept surfacing (the ai-launcher-hardening task is the live case) with no written recipe for fixing it.
todo-format.md gains a subsection, right after the :solo:/:quick: definitions, on converting a fuzzy goal into a measurable one: bound the surface (enumerate the units, that's the done-set), net the behavior with characterization tests, disposition every finding (fixed / filed / declined-with-reason, never "looked and it's fine"), and gate on an objective floor (static-analysis clean, suite green, coverage of the enumerated surface). The qualifying answer becomes a dispositioned report, not a claim of perfection, and the honest limit stays honest: "no visible bugs" means "every enumerated path passes its characterization set and clears the audit." Once a task carries those criteria it can carry :solo:.
work-the-backlog's keystone defer item now names open-ended goals as a specific, recognizable failure of test-writability, so a mistagged :solo: task with no writable acceptance test is deferred and routed to get criteria rather than guessed at.
start-work has the same Justify/Approach gate and wants the same pointer, but its skill lives outside rulesets, so that edit is a follow-up in its home.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
quality sweep
The rule already said "write a characterization test before refactoring" but not what one is or how many to write per unit. Two additions close that.
testing.md's "Adding Tests to Existing Untested Code" now defines a characterization test as recording what the code actually does, not what it should (Feathers' assert-wrong-read-the-real-value recipe), and requires the same Normal/Boundary/Error set as any unit rather than one happy-path capture. The reason is the useful part: on a characterization test the negative and boundary cases are the bug-finders, because untested legacy code is weakest at the empty input and the missing upstream, and pinning what it currently does there writes the wrong behavior down where it becomes a visible bug. A pinned case that turns out to be a bug graduates from recording current behavior to asserting correct behavior. In-unit bugs fall to this set. Composition bugs need a functional test.
The refactor-for-testability section gains the framing that when the untestable code is legacy you're hardening, extracting the pure core is the hardening, not a detour around it. A function you can't characterize without mocking tmux or git can't be refactored safely, so "needs too much mocking" is the signal to reshape it, never a license to skip the boundary and error cases.
code-quality.org's passes claim to preserve behavior, so it now states the precondition that makes that true: on untested scope, a characterization net comes first, or "behavior-preserving" is an assertion the green suite can't actually verify.
|
| |
|
|
| |
Both shipped this session (a143679 apkg converter, a14e43b multi-tag reconcile). Converted to dated event-log entries per the depth rule.
|
| |
|
|
|
|
|
|
|
|
| |
Craig wanted a curated "DeepSat Fundamentals" deck: the ~100 most fundamental cards out of the 465-card deepsat.org deck, marked with a second org tag (:fundamental:) so they stay grep-able in the source. Both flashcard-to-anki.py and flashcard-stats.py keyed a card on a heading ending in exactly :drill:, so adding any second tag turned the heading into :fundamental:drill:, which that anchor didn't match. The 100 tagged cards would have silently dropped from the full-deck apkg and been undercounted by stats. That's the passing-gate-skips-your-file failure.
CARD_RE now matches a trailing org tag block, and a heading is a card when drill is among its tags. The other tags ride along as Anki tags next to the section tag. Card bodies are bounded by any L1/L2 heading (HEADING_RE) rather than only a section or a drill heading. flashcard-to-anki.py gains --tag-filter <tag> (emit only cards carrying that org tag, the subset deck) and --guid-salt <s> (a distinct GUID space so the subset imports non-empty instead of colliding with the full deck on GUID and importing empty). No salt is unchanged, so the existing deck's GUIDs and SRS state are untouched. flashcard-stats.py gets the same CARD_RE/HEADING_RE broadening plus a drill-membership guard.
This is the rulesets-canonical reconcile of a stopgap the work project ran locally. I re-derived it against the current canonical rather than copying the preserved files, which predate the #+TITLE deck-name fix (060a938) and would have reverted it.
parse()'s third element is now the Anki-tag list rather than a single tag string. Only build() and the tests consumed it. The existing parse tests move to the list contract, and new tests cover multi-tag parsing, --tag-filter, the guid salt, and the stats count. A bats case guards that a :fundamental:drill: card still counts. The full deck's real 465/100 check needs the work deck and isn't runnable here. Verified end to end instead on a synthetic multi-tag deck (2 cards, --tag-filter fundamental returns 1) through the real genanki path.
|
| |
|
|
|
|
|
|
|
|
| |
The flashcard pipeline was one-directional (org-drill to apkg). A deck curated on the phone, or an orphaned apkg whose org source was never saved, had no way back into the org source of truth. apkg-to-orgdrill.py closes the loop.
An apkg is a zip holding an Anki sqlite collection, so reading it needs no third-party library: stdlib zipfile + sqlite3. genanki is only needed to write apkgs, not read them, so the converter has no runtime deps. It mirrors flashcard-to-anki.py inverted: deck name to #+TITLE, note Front to the ** heading, note Back HTML to the body (<br> to newlines, entities unescaped, the answer <hr> stripped), the note tag to a * section, and a fresh :ID: UUID per card so the output is org-drill-valid.
The entity unescape runs in the order that inverts escape_html, which escaped & first, so & is unescaped last here. Otherwise a literally-escaped < in the source would wrongly collapse to <. Only Front/Back note types convert; a cloze or other model is skipped with a warning rather than silently dropped, and a note referencing media is flagged since the org side has no media path.
17 tests cover the HTML-to-org conversion, the apkg read (single and multiple decks, Default-deck exclusion, non-basic skip, .anki2 vs .anki21, media flag), the org emission, and errors. The round-trip test closes the loop through flashcard-to-anki.py's own parse(): a known org source becomes an apkg fixture, converts back, and the recovered (front, back, tag) tuples match the original. Verified end to end against a real genanki-produced apkg too.
|
| |
|
|
|
|
|
|
| |
interaction.md and desktop-capture.md told the agent to launch a viewer with google-chrome-stable ... & or hyprctl dispatch exec "imv ...". Both files already noted the failure mode: a coding-agent shell reaps an ordinary background child after it returns, so the window dies before Craig sees it.
gui-open (dotfiles a34d479, on PATH) closes that. It detaches through systemd-run --user so the shell can't reap it, resolves the current Hyprland instance after a compositor restart, and confirms a mapped, visible client before returning. Both files now launch through it — gui-open <path> auto-detects image vs HTML, with --image/--browser to force. The separate-workspace guidance stays. gui-open replaces only the launch, which was the fragile part.
Proposed by archsetup after shipping the tool. It's dotfiles-owned, not a rulesets script, so this is guidance only; a machine without it needs a dotfiles pull.
|
| |
|
|
|
|
|
|
| |
The sentry live trial surfaced this. A sentry inbox-zero pass running from the work project parked the whole 19-item roam inbox as a cross-project boundary crossing and refused to tidy it unattended, reading knowledge-base.md's work-denylist as "don't touch roam from work."
That over-reads the rule. The denylist is a confidentiality guard on one thing: promoting a durable fact into a new agents/ node, so work-confidential material stays out of the personal cross-machine store. Roam is a shared resource, not another project's product scope. Reading it and tidying the shared roam inbox are housekeeping any project may do, work included.
knowledge-base.md gains a "Scope of the denylist" paragraph stating that and naming the mis-park as the error it closes. sentry.org's inbox-zero pass and inbox.org's roam mode each get a one-line note pointing at the rule, so a future agent doesn't re-derive the refusal. Only durable-node promotion stays work-denylisted.
|
| |
|
|
|
|
| |
Phase 4. All four phases shipped and the suite is green, so the spec keyword moves DOING to IMPLEMENTED with a dated history line and the Metadata Status mirrors it. The build task and its phase sub-tasks close (the sub-tasks become dated event-log entries per the depth rule).
The overnight live trial is the one piece an agent can't run, so it's filed as a structured manual-testing task: entry gates with Craig present, one fire observed end to end, the wrap-up guard and stop-sentry, and the morning branch review. Its findings become follow-up tasks rather than gating the flip.
|
| |
|
|
|
|
|
|
|
|
| |
Phase 3 of the sentry supervisor: reconcile the existing roam writers and wrap-up so sentry's locks actually guard something, and its shutdown has one enforced entry point.
The roam-write lock only helps if every roam writer takes it, so both writers now do. knowledge-base.md's write recipe and inbox.org core §5 acquire the roam-write lock around their edit and trigger roam-sync instead of committing themselves. That closes a gap the one-git-owner rule already implied but the KB recipe still violated: the recipe told agents to run git add -A && commit && push against a tree that's chronically dirty from live captures, which could sweep an in-flight capture into a stray commit. roam-sync stays the roam repo's only committer. Agents edit-plus-trigger under the lock, and roam-sync.sh's header now states that contract instead of the old "agents commit inline" note.
Both writers degrade as the spec settled: an absent agent-lock proceeds unlocked (today's behavior), and only a present helper reporting the lock busy after its bounded wait defers or surfaces.
wrap-it-up.org gains a Step 0 that refuses while sentry is live. It checks the single-runner lock and points at "stop sentry" rather than archiving the anchor and tearing down the buffer under a still-firing loop. Both files derive the lock name the same way (sentry-<repo-basename>), so the guard and the engine agree. triage-intake.org notes that it also runs as sentry's triage pass under the no-approvals contract, with its trigger phrases unchanged.
|
| |
|
|
|
|
|
|
|
|
| |
Phase 2 of the sentry supervisor. sentry.org is the engine: an interval loop (default hourly) that walks a fixed pass list (roam pull, inbox zero, triage, todo cleanup, task audit, working-files hygiene, spec board, link integrity, git health, prep freshness) and commits each writing pass to a throwaway sentry/<date>-<host> branch. Nothing pushes. The morning teardown (review, squash-merge, delete) stays Craig's.
The file owns the whole engine: the :COMMIT_AUTONOMY: entry ticket, the interactive entry gates (clean tree, green suite, prior-branch check, ff-only reconcile) that run with Craig present, the single-runner and roam-write locks via agent-lock, the probe-work-session-context-commit pass contract, the digest and morning-approval queue, the skip-not-degrade safety model, the conditional fire-end suite, the multi-day stall notification, and the stop-sentry shutdown.
Every one of the spec's ten decisions and twelve review findings is reflected: host-suffixed branch so the two daily drivers don't collide, tmpfs lock homes so a lock never rides roam-sync to the other machine, roam-sync kept as the roam repo's only committer, spine-excluded dirty checks so sentry's own bookkeeping can't trip its safety skip, and the queue-don't-execute rule for every judgment or destructive action.
Phase 3 wires the roam writers (inbox.org, knowledge-base.md) to acquire the roam-write lock and adds the wrap-it-up guard. Until then the engine is documented but the companions don't yet cooperate. INDEX.org lists it under Tasks and planning.
|
| |
|
|
|
|
|
|
|
|
| |
Phase 1 of the sentry supervisor workflow. sentry needs two locks: a single-runner lock so overlapping /loop fires don't stack, and a roam-write lock so agent edits to the shared roam tree serialize. flock can't serve either, because every Bash call an agent makes is its own short-lived shell, so an flock dies with the call that took it.
agent-lock persists the lock on disk between calls. An atomic mkdir is the acquire. A metadata file inside records pid, host, ISO timestamp, and TTL. Staleness is age-based on the meta mtime, so a crashed holder's lock expires instead of wedging every later acquire, and every reclaim surfaces a note rather than clearing silently. refresh re-touches the mtime as a heartbeat, release is idempotent, status reports free/held/stale, path prints the resolved dir. Locks live under XDG_RUNTIME_DIR/agent-locks (tmpfs: host-local, out of every repo, cleared on reboot), with a ~/.cache fallback where no runtime dir exists.
Reclaim claims the stale dir with an atomic rename before removing it, so two acquirers that both see a lock stale can't both win. A plain remove-then-mkdir would let the loser delete the winner's fresh lock and double-acquire, defeating the roam-write lock's purpose.
Nothing calls it yet. 18 bats tests cover fresh win, contention, release, stale reclaim, heartbeat, path resolution, and usage errors.
|
| |
|
|
| |
Craig approved the sentry spec after his deep read. Flipped DRAFT → READY → DOING with dated history lines, mirrored the Metadata status, and decomposed the build task into the four implementation phases under a SPEC_ID-bound parent.
|
| |
|
|
|
|
|
|
| |
Two batched changes to the todo hygiene tooling.
Dated-seal archiving: --archive-done aging now keeps one month in-file (retain default 7 → 31, named as tc-archive-retain-days-default) and a new --seal renames the working task-archive.org to resolved-YYYY-MM-DD.org beside it, letting the next aging start a fresh working file. Dating the sealed file by the seal run rather than the calendar quarter is what avoids mislabeling a task closed late in a quarter and archived after the boundary. The unparseable-CLOSED case is now explicit in the contract: a keyword-complete task with no readable close date ages out too. The sealed file inherits the todo file's gitignore status.
Planning-line strip: --convert-subtasks now drops the whole planning line when it rewrites a done sub-task to a dated event-log entry, not just the CLOSED cookie. A dated-log heading carries its date in the heading, so an active SCHEDULED/DEADLINE left on it pins the finished entry to the agenda as weeks-overdue forever (org renders any active planning timestamp, keyword or not). New lint-org checker dated-log-heading-active-timestamp backstops any that predate this. todo-format.md's sub-task and VERIFY completion rules gain the strip step.
|
| |
|
|
| |
Added claude-templates/bin/install-ai, a thin launcher that resolves its own path through the symlink chain and execs scripts/install-ai.sh. make install's bin loop symlinks it into ~/.local/bin/install-ai, the same mechanism that links ai and agent-page, so the fresh-project bootstrapper now runs as install-ai from anywhere. The symlink always points at the canonical, so no separate dotfiles copy is needed. Three launcher bats cover the wrapper, including invocation through a symlink from another directory (the case a naive dirname "$0"/.. would break).
|
| |
|
|
| |
The elisp bundle installs coverage-summary.el into .claude/scripts/, which is gitignored in code projects, so CI can't run make coverage-summary against it. That's intentional (Craig, 2026-06-28): it stays a developer-run check, not a CI gate. Stated the local-only status in the script's commentary header and in elisp-testing.md's coverage section so the gitignored install reads as by-design rather than a gap.
|
| | |
|
| |
|
|
| |
Filed as [#B] work items with their origin proposals preserved under docs/design/: the todo-cleanup dated-seal archiving model, the dated-log planning-line strip plus its lint backstop, a hook to enforce the task-boundary inbox check, and home's colloquialisms / "the list" before-close-queue convention. The first two both touch todo-cleanup.el and are cross-linked to build as one batch. The last two carry the design worked out this session so they start warm.
|
| |
|
|
| |
The roam KB bare repo moved off the publicly-browsable path to a private one. The origin is now cjennings@cjennings.net:git/roam.git, and the old URL in this file was stale.
|
| |
|
|
|
|
|
|
| |
New stdlib-only upcoming_birthdays.py reads ~/sync/org/contacts.org for :BIRTHDAY: properties and prints a daily-prep block: name, date, days-away, and the age turned when the birth year is known (1900 is the org-contacts unknown-year placeholder, rendered date-only). Birthdays inside 7 days come back flagged so a gift or plan gets prompted. 19 pytest cases cover the window boundaries, leap-day fallback to Feb 28, the placeholder year, and the CLI.
daily-prep gains a Phase A source that runs the script and a Heads-Up line that folds in the block, so contact birthdays the calendar doesn't carry still surface.
Built and tested in the home project. Promoted here because the daily-prep hook lives in this synced workflow and the contacts file is present on every machine.
|
| |
|
|
|
|
| |
Phase C now renders a TASKS / FYI / MISC digest with a two-option close offer. The per-source breakdown and the itemized action menu move to on-request. Phase D becomes the close and runs by default after the digest: file the TASKS items, run mail hygiene on every scanned account without itemized confirmation, advance the sentinel, tear down anything the sweep started. Prose sends under my name and destructive non-mail actions stay gated. The reroute modifier delivers outside-project items to their owners via inbox-send. The stay-open-until-confirmed exit loop is retired. I updated daily-prep's Sub-step 3b to match.
I ratified this in the work project on 2026-07-18 and applied it here as sent from that handoff (inbox 2026-07-18-1730-from-work).
|
| | |
|
| |
|
|
|
|
|
|
| |
Decision 10 was sitting at the bottom of Review findings. It's written in Context/Decision/Consequences shape, unlike the Resolved-notes around it, and the finding at "Per-pass commits skip the pre-commit suite run" points at it by name, so the pointer was there and the target was in the wrong section.
That one misfiling made both cookies lie. Decisions read [10/10] by hand while org would have computed [9/9]. Review findings read [/], uncomputed, and would have resolved to 13, which was 12 findings plus the stray decision. Moving it and letting org recompute gives [10/10] and [12/12], both live.
Nothing was lost. Every decision and finding was present and resolved. But the readiness gate reads those cookies, and a spec about to be graded on them shouldn't carry one that's stale and one that's dead.
|
| |
|
|
|
|
| |
Craig's call, 2026-07-16. This reverses the fable pin from e91073d rather than
drifting into it — the field has flip-flopped four times now, so noting that
this one was chosen.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Installing a second language bundle into a project silently replaced the first one's config. settings.json and githooks are copied with cp -rT (always overwrite), so installing bash over elisp rewired the validate hook to validate-bash.sh and dropped check-parens from pre-commit, leaving validate-el.sh orphaned on disk. The output said [ok] for both. A project could lose its paren check or secret scan and read the install as success.
The guard detects which bundles a project already has, by the same rule fingerprint sync-language-bundle uses, and refuses when the incoming bundle would overwrite a file another one ships. It names each file at risk. FORCE=1 still overrides, and the message says that also re-seeds CLAUDE.md, which is destructive on a customized project.
Only three of the five shared filenames actually collide. gitignore-add.txt is appended and deduped, and CLAUDE.md is seed-only, so both compose across bundles already.
This doesn't decide whether polyglot projects are supported, and the guard shouldn't be read as "no". A non-overlapping pair still installs: bash ships settings.json and githooks with no coverage fragment, python ships only a coverage fragment, so the two compose today. The real line is overlap, not polyglot, and nothing chose it. The open question, along with the identical coverage target names both fragments define, is filed.
home reported this after scaffolding clock-panel with python and typescript, which hit the coverage fragment. The settings.json and githooks cases are worse and hadn't been noticed. No project was damaged: the three bundles that collide aren't doubled up anywhere.
|
| |
|
|
|
|
|
|
|
|
| |
home proposed promoting its subproject pattern (a folded-in standalone project living as a self-contained subdir under a parent's scope) into the rules layer. The pattern reads as sound. I deferred it on evidence rather than merit, and preserved both the proposal and home's worked instance here so the decision has its source.
Two findings drove the deferral. It's N=1: across all 27 agent scopes, home is the only project with subprojects, and all nine came from one fold. There's no second instance to say which parts are general and which are home-shaped. The placement also contradicts the proposal's own argument. Rules load into every session of every project, and the doc argues that layer is a tax paid whether or not it's relevant, with depth belonging one open away. At 282 lines it would be the third-largest rule and add ~11% to the layer, so every unrelated project would carry a one-project convention.
I want to scout which projects would actually get subprojects, and why, before shaping anything. That evidence decides whether the pattern gets dropped or adjusted.
Two things to fix if it ever promotes: the doc cites a privacy-model rule that doesn't exist (the real content is the gitignore-vs-track and public-reachability decision), and about half of it is home's instance rather than rule content.
|
| |
|
|
|
|
|
|
|
|
| |
The secret scan ran every pattern through grep -iE, so AKIA[0-9A-Z]{16} matched any mixed-case 20-char run. Random base64 in an embedded image blob hits that about 6% of the time per 100KB, enough to block a real commit and force --no-verify. takuzu hit it on a PNG sprite. AWS keys are uppercase, sk- keys lowercase, and PEM headers fixed, so those three now match case-sensitively. Only the keyword=value patterns still need -i.
I measured both causes first. Across ~10MB of random base64 the old pattern produced 7 matches and would have false-positived 6 of 100 sprite-sized blobs. Case-sensitive matching produced none.
takuzu's report also proposed skipping any added line containing ";base64,". I left that out. It guards against an uppercase AKIA run surviving the case fix, which never occurred in the sample and runs about one in a million per blob. The cost is real: minified bundles put a whole file on one line, so a data URI and a live key can share it, and skipping the line hides the key. A test covers that.
The three variants share one scan block, so elisp, bash, and go all carry the change. Splitting one grep into two meant a line matching both passes got reported twice, which reads as two separate leaks. awk dedupes it.
|
| |
|
|
|
|
|
|
| |
The residue probe cut off at before:<anchor-YYYY/MM/DD> while the scan started at after:<anchor-epoch>. Gmail's before:<date> excludes the named day, so the anchor day fell between the two queries. Each sweep then advanced the anchor past that window and never looked back, so an evening anchor hid most of a day for good. That's the exact failure the probe was added on 2026-07-08 to prevent.
The probe now cuts off at the epoch, matching the scan. I hoisted the epoch rule to cover both anchored queries once rather than twice. I scoped it to the anchor windows so the date-slice walk's deliberate day resolution doesn't read as a bug.
home caught it: after acting on 38 messages the probe reported zero while a plain is:unread in:inbox still returned two messages from the anchor day. personal-gmail is the only source pairing an anchored scan with a residue probe, so nothing else needed the fix.
|
| |
|
|
| |
Spec for the overnight hygiene supervisor: ten serialized passes, mkdir-atomic locks with heartbeat under the runtime dir, a host-suffixed unpushed branch, interactive entry gates with no report-only mode, and roam-sync kept as the roam repo's only committer. All 10 decisions and 12 adversarial-review findings are resolved in the file; status stays DRAFT pending a deep read. The build task and three deferrals are filed.
|
| | |
|
| |
|
|
|
|
| |
The meeting recorder switched to lossless FLAC on 2026-07-01, and the transcript workflow's scan globbed only .mkv and .m4a, so a .flac recording was silently never found. FLAC now threads through the audio-only path: found by the scan, transcribed natively by AssemblyAI with no ffmpeg extraction, kept on cleanup, and archived with its own gitignore pattern.
Adopted verbatim from work's handoff (2026-07-14-0105), diff-verified against the canonical.
|
| |
|
|
|
|
| |
Both helpers treated any pipe-led line as a table row, so ASCII art in an example block got mangled into a bordered table (a work file took a 1949-line reformat on 2026-07-09). The scanners now track the open block's type and skip its content. Only the matching end marker closes a block, so a literal end_src quoted inside an example block can't re-expose it.
The deeper cause was wrap-org-table's load-time dispatch: it fired when lint-org merely required the library, running the reformatter over files lint-org was only asked to report on. It now dispatches only when named as the entry script. lint-org's CLI is also report-only by default now. Writes require --fix, and the wrap-up workflow and lint command pass it.
|
| |
|
|
| |
The archive sweep moves the finished routing feature and the cancelled ntfy proposal into Resolved, todo.org carries the day's review stamps and dated build entries, and the session record lands under its dated name.
|
| |
|
|
| |
Eleven tasks reviewed: the ntfy two-way-comms proposal is CANCELLED (home tore that channel down on 07-04; the Signal pager task and agent-page are its living successors), the triage-intake phone-push task is re-pointed at agent-page so its send half is buildable now, coverage-summary's docs-only remainder gained quick and solo tags, and the rest were confirmed as-is. Zero top-level tasks remain unreviewed.
|
| |
|
|
| |
Both emacs invocations in the hook now set load-prefer-newer, matching the project-side Makefile and early-init guards. Without it, the PostToolUse hook after a pull that changed a module ran tests against the module's stale .elc and every test failed void-function on the newly-added symbols. From .emacs.d's 2026-07-13 handoff, repro-verified there; inbox/2026-07-13-1712-from-.emacs.d.
|
| |
|
|
| |
The interactive flow now opens with a runtime picker: claude first (Enter-Enter preserves the old two-keystroke habit), codex labeled as ChatGPT, and one local:<model> line per ollama model, queried live with a short timeout so a dead server just drops those lines. The pick feeds the existing runtime map, then the annotated project multi-select runs as before. --runtime and AI_RUNTIME skip the question, single-directory mode is unchanged, and --print-runtimes exposes the choice list for the two new bats tests.
|
| |
|
|
| |
ai --runtime local now launches codex against the machine's ollama (explicit --local-provider=ollama, model from AI_LOCAL_MODEL, default gpt-oss:120b). Verified end to end with a codex exec completion through the local model. The dependency check probes AGENT_BIN now that AGENT_CMD carries flags. This makes codex-over-ollama the de-facto answer to the runtime spec's first-supported-local-CLI blocker.
|
| |
|
|
| |
ollama 0.31.2 with the Vulkan backend forced by systemd override (ROCm first-touch loads were unusable at under 25 MB/s), gpt-oss:120b and qwen3-coder:30b verified at 37.9 and 76 tok/s on 100% GPU, four legacy dense models dropped, the orphaned user store purged. The child's remaining work is purely the instruction-following evaluation.
|
| |
|
|
| |
The Signal pager reconcile found one pager identity, registered in velox's signal-cli, reachable from other machines only by hand-rolled ssh. agent-page wraps that: direct signal-cli on velox, ssh relay over the tailnet from everywhere else, a desktop-fallback hint when the relay fails, and the UUID target baked in so nobody pages the unregistered phone number again. Four bats tests stub ssh/uname/signal-cli to verify command construction; the real path was verified with a live phone push today. protocols.org's Paging Craig section now teaches both channels (notify for the desk, agent-page for the phone) and demotes signal-mcp to a velox-local nicety; page-me.org, work-the-backlog, and the INDEX carry the same two-channel story. Every project inherits on its next startup sync and make install.
|
| |
|
|
| |
Signal pager re-graded C to B: the runtime-portability inventory showed the claude.ai-side signal-mcp is the only paging path, home currently has no live channel at all, and off-Claude agents have none, so the doc-and-reconcile task now gates real capability rather than tidiness. install-ai-on-PATH tagged quick and solo. The launcher-hardening task and the agent-switching fold-in came from today's roam capture. The other five reviewed tasks were confirmed as-is.
|
| |
|
|
| |
session-context-path already carries a runtime segment, the anchor cycle is plain files, and self-inject is harness-agnostic with only its payload Claude-shaped. A codex auto-flush is a documented payload variant whose second injected line carries the resume instruction, replacing the SessionStart hook. Wire it when a codex session first wants it.
|