aboutsummaryrefslogtreecommitdiff
path: root/.ai
Commit message (Collapse)AuthorAgeFilesLines
* chore(ai): refresh session log and workflow-state markerCraig Jennings2026-05-302-1/+70
|
* test(scripts): cover drill-to-anki internals, broadcast, and daily-prepCraig Jennings2026-05-306-1/+411
| | | | | | | | I backfilled the gaps left after the flashcard work landed. drill-to-anki.py had tests only for its two default helpers. I added coverage for the core parser and its pieces: parse (section-to-tag mapping, drawer-only body, blank trimming, multiline join, no-card input), strip_org_metadata (drawer and planning-line stripping, unclosed drawer), section_to_tag, escape_html, and the deterministic stable_id. I also filled the remaining drill-deck-stats / drill-deck-diff-ids branches (missing-title and PROPERTIES-mismatch warnings, the appeared-IDs note path). I added test_cross_project_broadcast.py for the two scripts that had none here: is_broadcastable / discover (SEARCH_ROOTS pointed at a tmp tree) / sender_project / inbox_send_path, plus an ERT suite for daily-prep-agenda.el (dp-iso-date, dp-bucket with the clock pinned, dp-format-entry, and dp-collect end to end on a temp org file). daily-prep-agenda.el needed one change to be loadable under ERT: its batch entrypoint fired on any load. I gated it behind dp--cli-invocation-p, the same readable-files check lint-org.el already uses, so requiring the file for tests no longer runs the extractor. A real invocation with a file argument still fires. A no-argument run now no-ops instead of printing an empty header.
* chore(scripts): add drill-deck stats, diff-ids, and sync wrapperCraig Jennings2026-05-307-24/+670
| | | | | | I incorporated the flashcard-tooling bundle from the work project's deck-review workflow, validated there against a 93-card deck. Three scripts now live under .ai/scripts/: drill-deck-stats.py (pre-rewrite inventory plus a gate that warns on stray *** Answer headers, missing :ID:, non-prompt headings, and #+TITLE jargon like "org-drill"), drill-deck-diff-ids.py (SRS-state preservation check that flags any :ID: lost across a rewrite), and drill-deck-sync (bash wrapper chaining stats, optional diff-ids, then drill-to-anki, writing to ~/sync/phone/anki/ only when the gates pass). The drill-deck-review.org workflow gains a Helper Scripts section and references the scripts from its phases. I reconciled its output-path prose with the drill-to-anki default that just moved to ~/sync/phone/anki/, so it no longer claims the script still defaults to ~/sync/org/drill/. I added tests for both Python scripts (pure logic plus CLI gate behavior) and a bats suite for the wrapper's guard paths. The clean end-to-end sync path stays uncovered since it needs uv-resolved genanki.
* feat(drill-to-anki): default to phone sync dir and basename deck nameCraig Jennings2026-05-302-25/+57
| | | | | | Two default-behavior tweaks from real use. Output now defaults to ~/sync/phone/anki/ instead of ~/sync/org/drill/. The .apkg is a mobile-Anki artifact the phone picks up from its sync dir, while the org source stays in the project. Deck name now defaults to the raw input basename, case preserved. That drops the #+TITLE preference, which leaked tool-name jargon like "DeepSat org-drill flashcards" into the Anki deck list. The --deck and --output flags still override both. I dropped the now-unused title_from_org helper and added a test covering the two defaults. genanki is stubbed in the test since uv resolves it only at runtime.
* chore: sync .ai mirror with canonical (drill-deck-review)Craig Jennings2026-05-302-0/+212
|
* feat(workflows): add drill-deck-review + extend drill-to-anki scriptCraig Jennings2026-05-301-0/+27
| | | | | | | | I added a drill-deck-review workflow that walks an org-drill deck end-to-end: question-form audit on every heading (so the heading is the prompt, not the answer), content-accuracy audit via subagent against project source-of-truth, source rewrite preserving SRS state, regenerate to ~/sync/phone/anki/. The workflow covers three card families (acronym/concept, person, talking-point) and codifies the person-card pattern as "Who is X? Tell me about their Y." where X is a role descriptor that doesn't name the person and Y is the topical anchor from the answer body. The drill-to-anki.py script picks up a new strip_org_metadata helper that drops :PROPERTIES: drawers and SCHEDULED/DEADLINE/CLOSED planning lines from the rendered Anki back. Org-drill needs both in the source for SRS state and review scheduling; Anki cards shouldn't show them. INDEX entry under "On-demand utilities" wires the trigger phrases.
* docs(ai): populate Summary section in archived session recordCraig Jennings2026-05-291-1/+41
| | | | | | | | git mv in the prior wrap-up commit (0237465) renamed the index entry without re-staging the disk content, so the archive landed with an empty Active Goal and missing Decisions, Findings, Files Modified, and Next Steps sections. This follow-up commits the populated Summary that was already on disk at wrap-up time.
* chore(ai): archive session record + lint follow-upsCraig Jennings2026-05-291-0/+0
| | | | | | | Multi-arc session: no-approvals batch, codex backlog triage, page-signal + broadcast infra, voice profile Phase 1 + Phase 2 with SKILL.md split, process-inbox formalization, wrap-it-up inbox sanity check.
* fix(wrap-it-up): inbox sanity check exempts lint-followups.orgCraig Jennings2026-05-291-4/+11
| | | | | | | | | | | | | | | | The inbox sanity check I added in 8424e8f counted lint-followups.org as unprocessed, which surfaced as a false alarm during this session's wrap-up. lint-org writes its judgment items into inbox/lint-followups.org earlier in the same wrap-up workflow by design. The file is a pipeline artifact for the next morning's daily-prep, not a handoff that needs the value gate. The find filter now excludes .gitkeep, lint-followups.org, and PROCESSED-* prefixes. The Validation Checklist line names the same expected-artifacts list explicitly. Smoke test: post-fix count is 0 in the rulesets project (where lint-org just wrote 5 judgment items into inbox/lint-followups.org).
* docs(cross-project-broadcast): codify capability-and-rule cadence guidelineCraig Jennings2026-05-291-1/+14
| | | | | | | | | | | | | | | | | | | | | | | Adds one bullet to When NOT to Use and a new Cadence Guideline section that names the per-commit-broadcast anti-pattern explicitly. The new section lays out the five reasons broadcasts stay capability-and-rule-level rather than commit-level: cost per broadcast across the fleet, signal-to-noise on project-internal commits, the rsync-already-does-the-work observation, aggregation winning over per-commit pings, and the train-projects-to-ignore-inbox risk. The end-of-session bundling guidance lands too. If a session ships several broadcastable changes, bundle them into one broadcast at session end instead of firing one per commit. Source: session-end conversation 2026-05-29 surveying today's cross-project changes. Today's session shipped 13 cross-project items (4 new workflows, 6 workflow updates, 2 new scripts, 1 new bin tool). A per-commit broadcast cadence would have fired 13 inbox files across 23 targets, or 299 total inbox files. One consolidated broadcast (or no broadcast at all, since Craig already coordinated manually) covered the same ground.
* docs(wrap-it-up): add inbox sanity check + checklist lineCraig Jennings2026-05-291-0/+20
| | | | | | | | | | | | | | | | | | | | | | | | Wrap-up never knew about the inbox. After process-inbox.org landed today as the formal workflow for handoff intake, wrap-it-up.org needs to surface inbox/ when it's non-empty so the wrap doesn't silently defer handoffs to next session. This commit adds a new Inbox sanity check sub-step under Step 3 (project hygiene). The check runs find inbox -maxdepth 1 -type f filtered to skip .gitkeep and PROCESSED-* prefixes. A non-zero count surfaces with the file list and a recommendation to run process-inbox.org or explicitly defer each item. A zero count or no inbox/ directory makes the check a silent no-op. It also adds one line to the Validation Checklist: inbox is empty (excluding .gitkeep and PROCESSED-* prefixes), OR each remaining item has an explicit deferral logged in the valediction. This is the only integration gap surfaced today. The other gaps considered (LAST_INBOX_PROCESS marker stamp, sync-check, make status) didn't justify wrap-up changes. The marker is process-inbox's own responsibility. sync-check is project-specific to rulesets. make status doesn't generalize across projects.
* chore(session): pre-stage signal-mcp config + snapshot session-contextCraig Jennings2026-05-291-0/+126
| | | | | | | | | | | | | | | | | | | | | | | | | | This commit is end-of-day housekeeping for the rulesets session that ran 2026-05-28 through 2026-05-29. mcp/servers.json gains the signal-mcp stdio entry pointing at the locally-cloned rymurr/signal-mcp install at ~/.local/share/signal-mcp/. The entry uses Craig's primary +15103169357 as --user-id, which will need updating to the Google Voice number once that signal-cli registration lands (TODO scheduled today). The configuration is staged but not registered. The make install-mcp pipeline was interrupted earlier by the GPG pinentry (Craig on vacation, away from desk). Running make install-mcp at the desktop with the GPG passphrase will complete the registration. .ai/session-context.org captures the live session narrative covering the no-approvals batch (6 :quick:solo: TODOs), the codex backlog triage, the page-signal infra, the voice profile Phase 1 plus Phase 2, the SKILL.md / voice-profile.org structural split, and the inbox processing pass. No wrap-up performed. The session-context stays under that name pending the wrap-it-up workflow when Craig is back at the desk. The substantive work in this session shipped across many prior commits, each with its own focused message.
* chore(intake): file startup-rsync-dirty TODO, stamp :LAST_INBOX_PROCESS:, ↵Craig Jennings2026-05-291-0/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | clear processed inbox The process-inbox workflow walked the four inbox arrivals (one from home, one from jr-estate, two from health): - 2026-05-28-0858 home spec-naming-convention: implemented in bb28cfa. Deleted. - 2026-05-29-0832 jr-estate startup-rsync-carried-dirty: filed as [#B] :feature: TODO under Rulesets Open Work. Three options captured (skip-when-dirty, clean-files-only, clean-ref-based) with my recommendation noted. Deleted. - 2026-05-29-1111 health org-drill-anki initial: superseded by 1114. Deleted without filing. - 2026-05-29-1114 health org-drill-anki updated: implemented in 506ab6e. Deleted. The startup-rsync-dirty TODO also captures an anomaly the jr-estate handoff flagged: even with --delete, two files that DO exist in rulesets canonical (cross-project-broadcast.org, page-signal.org) did NOT propagate. This is likely a timing issue (those files were added after the jr-estate Phase A rsync ran). The anomaly is worth confirming when the TODO is picked up. notes.org Workflow State stamped :LAST_INBOX_PROCESS: 2026-05-29. Inbox now empty.
* feat(scripts): add drill-to-anki.py template script (org-drill to Anki .apkg)Craig Jennings2026-05-291-0/+214
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Generalizes the health-drill-to-anki.py converter into a template script under claude-templates/.ai/scripts/. Every project's startup rsync now picks it up at .ai/scripts/drill-to-anki.py. The converter walks an org-drill file. Top-level * Section headings become Anki tags. Each ** Card name :drill: entry becomes a card with the heading as Front and the body as Back (newlines converted to <br>). Parameterized from the original health-specific version: - Input file is a positional argument (was hardcoded health-drill.org). - Deck name defaults to the org file's #+TITLE if present, else the input basename in Title Case (was hardcoded "Health Drill"). - Output path defaults to ~/sync/org/drill/<input-basename>.apkg (was hardcoded health-drill.apkg in the same dir). - Deck and model IDs are derived from the deck name via SHA-256, so re-running against the same source produces stable IDs. Anki imports update existing cards in place rather than duplicating. Dependencies via PEP 723 inline script metadata. The shebang is #!/usr/bin/env -S uv run --script. First run resolves and caches genanki>=0.13. Subsequent runs are instant. There is no venv to maintain and no PEP 668 friction. Smoke tested against ~/projects/health/health-drill.org. The script wrote 43 cards into the Health Drill deck, matching the original script's output. Inbox source: 2026-05-29-1114-from-health-todo-b-org-drill-anki-export-updated.org. Craig confirmed all projects will likely have org-drill files, justifying template-tier promotion.
* feat(workflows): add -spec.org precondition to spec-review and spec-responseCraig Jennings2026-05-292-0/+32
| | | | | | | | | | | | | | | | | | | | | | | | Both workflows now check that the file under review (or the spec being responded to) ends with -spec.org before proceeding. If it does not, the workflow stops and surfaces the mismatch with the rename suggestion. The suffix is the identifier per Craig's spec-naming convention: every design, decision, or planning document under a project's docs/ directory ends with -spec.org. The .org extension alone is not enough because docs/ holds non-spec org files too (tutorials, frozen inventories, reference material). spec-review.org gained a top-level Precondition section between When to Use and Approach. spec-response.org gained the same Precondition section in the parallel position, with a note that the review-file convention <spec-basename>-review.org means a misnamed spec produces a mis-pointed review file too. Inbox source: 2026-05-28-0858-from-home-spec-naming-convention-apply-to-spec.org. Home renamed two docs to the new convention and asked rulesets to update the template workflows so the next startup rsync in every project picks up the guard.
* feat(signal): page-signal CLI wrapper + workflows + cross-project broadcast ↵Craig Jennings2026-05-294-0/+372
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | helper Three coupled additions ship together. claude-templates/bin/page-signal is a bash wrapper around signal-cli send. It defaults to --note-to-self for safety. The wrapper supports --file for attachments, --to <+number> for outbound (explicit per call, no defaults, no batch), --quiet, and --json. Exit codes: 0 sent, 1 signal-cli failure, 2 usage error, 3 signal-cli not installed. claude-templates/.ai/workflows/page-signal.org carries the discrimination rules and safety rails. When desktop notify covers it, don't reach for Signal. Long-running task completion is the canonical case. Outbound to other contacts requires explicit Craig instruction per send. A known-limitation note covers the current notification gap. signal-cli registered on Craig's primary number means messages don't fire notifications until the pending Google Voice registration lands. claude-templates/.ai/workflows/cross-project-broadcast.org and its helper cross-project-broadcast.py fan out a single message file to every AI project's inbox in one operation. Discovery is fingerprint-based: any directory under ~/code, ~/projects, ~/.emacs.d with both .ai/protocols.org and a top-level inbox/ is broadcastable. Senders are auto-excluded. Verified discovery against 23 broadcastable targets. Makefile's install target gains a general bin/ loop. The previous version hardcoded bin/ai. The new version iterates over every executable under claude-templates/bin/ and symlinks each into ~/.local/bin/. install-hooks (existing Claude hook installer) is unchanged. install-githooks (sync-check pre-commit hook setup, added earlier today) is unchanged. The bin/ loop now picks up bin/page-signal automatically. INDEX entries for both new workflows landed under Tools and meta. No bats tests on the new scripts. page-signal was smoke-tested with a live send. The send succeeded. The notification gap is covered by the workflow's known-limitation note. cross-project-broadcast.py was smoke-tested via --list against the live project set. Tests can be added when the broadcast pattern proves out across multiple use cases.
* feat(open-tasks): archive-done sweep at start of Phase ACraig Jennings2026-05-281-1/+16
| | | | | | | | | | | | | | | | | | open-tasks.org Phase A now runs `emacs --batch -q -l .ai/scripts/todo-cleanup.el --archive-done todo.org` as a pre-step before the parallel read batch. A level-2 task that completed during the session sits as ** DONE under Open Work until something archives it, which means Next Mode would surface it as a "what's next" candidate between cleanups. The pre-step ensures Phase A's read of todo.org reflects current state. The pre-step skips only in an explicit read-only or dry-run context. Default is always to sweep. Common Mistake #13 records "recommending a freshly-DONE task" with the pointer back to the pre-step. The proposal came from a pearl handoff on 2026-05-28.
* feat(workflows): add process-inbox.org with value-gate disciplineCraig Jennings2026-05-283-9/+175
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Generic process-inbox workflow at claude-templates/.ai/workflows/ and mirror. Owns inbox discipline across every project: items are ideas to evaluate, not orders to execute, and earn a place in todo.org or git history only when they pass a three-question value gate. The gate (Phase B): - Advances an existing TODO (look up by topic). - Improves how the project works (architecture, workflows, tooling, rule hygiene). - Serves the project's stated mission (read from notes.org Project-Specific Context). One yes accepts. Three nos reject. Per-source rejection flow (Phase D): - From Craig: state in chat, wait for override. - From another project: write a response via inbox-send naming which gate question failed plus any reconsideration condition. Silent rejection on a handoff is worse than no reply. - From a script or automated system: just delete. Phase B.1 gates filing on priority-scheme presence. If todo.org has a scheme at the top, file with cookie + mandatory type tag + optional effort/autonomy tags. If not, surface a one-sentence nudge to adopt one (or to skip grading and flag the gap in the commit). Phase D within accepts splits implement-now / fold-into-existing / file-as-TODO so the inbox doesn't default to inflating todo.org for every item that passes the gate. Phase E stamps :LAST_INBOX_PROCESS: in notes.org Workflow State if the section exists. startup.org Phase C step 2 now delegates here instead of inlining the inbox-processing language. INDEX entry under Tasks and planning lists the full set of trigger phrases.
* feat(workflows): promote no-approvals.org to template, merge pearl framingCraig Jennings2026-05-283-78/+131
| | | | | | | | | | | | | | | | | | | | | | | Pearl independently built its own no-approvals workflow (handoff in this morning's inbox) and asked rulesets to take the best from both. That's cross-project signal that the workflow earns a place at the template tier. Promoted from =.ai/project-workflows/= to =claude-templates/.ai/workflows/= with a mirror under =.ai/workflows/=. INDEX entry added under "Tools and meta" listing the full set of trigger phrases. Merged from pearl's draft: the prominent "What's Suspended" / "What Stays On" split (same content as the prior "Contract" section, easier to scan), wider trigger phrases (the project-only version had fewer), the mode-resets-when-Craig-switches-topics guard, an explicit destructive-action carve-out as its own bullet rather than buried in the "real question" section, and a subagent-review-gate callout. Kept from the rulesets version: the Session Log update emphasis between items, the real-question include/exclude lists, and the don't-auto-wrap-up guard. A History section at the bottom records the merge for future archaeology.
* chore(audit): task-audit pass + pearl intake from 2026-05-28 morningCraig Jennings2026-05-281-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | All 16 open tasks bucketed and updated. 14 received autonomous Phase C edits (13 type-tag additions per the new scheme plus #15's body refresh for the accumulating pearl signal). Phase D adjudicated two priority bumps and the morning's inbox spillover. Phase E stamped :LAST_AUDIT: in notes.org Workflow State. Autonomous Phase C: - Tagged tasks 1-11 and 13-14 with their type tag (:feature:, :chore:, :spec:), bumped LAST_REVIEWED to 2026-05-28. Tasks 12, 15, 16 already carried type tags. - Refreshed task #15 body to reference the four pearl pattern-catalog notes now in docs/design/ (six worked patterns total). Phase D adjudication: - #15 (cross-project pattern catalog) bumped [#C] to [#B]. Pearl shipped 6 worked examples plus a synthesizing principle and is asking for spec-review iterations. Design questions still open but evidence is past the tipping point. - Filed new [#B] :feature: TODO for the codex Phase 1 race-fix (AI_AGENT_ID + session-context.d/<id>.org), lifted from the broader runtime spec (#16). Phase 1 alone is low-risk and fixes a real correctness issue under simultaneous agents. Pearl intake (4 inbox files from this morning): - Moved 0155 (patterns 4-5) and 0303 (pattern 6) into docs/design/ alongside the prior two pattern-catalog notes. Referenced from #15. - Filed new [#C] :chore:quick:solo: TODO for pearl 0138's --archive-done sweep at the start of open-tasks.org Phase A. - Filed new [#C] :feature:solo: TODO for pearl 0226's spec-review.org Phase 6 implementation-task enumeration. - Deleted 0124. Already implemented this session as the audit-warranted pre-step plus the LAST_AUDIT stamp now live in the workflow files.
* feat(workflows): audit-warranted pre-step + priority and tag enforcementCraig Jennings2026-05-282-18/+102
| | | | | | | | | | | | | | | | | | | Two changes land together because each is broken without the other. open-tasks.org gains a new Phase A.1, evaluated only in Next Mode. The phase reads :LAST_AUDIT: from notes.org and walks five state signals (reminder/task mismatch, passed scheduled date, "waiting on X" matches a shipped X, dead file: link, sub-task >75% DONE coverage). If the temporal threshold of 14 days trips, or any signal fires, Next Mode offers a task-audit run before producing the recommendation. Item 1 in the offer is "run task-audit first" per the recommendation-at-item-1 convention. task-audit.org gains two pieces. Phase C now enforces priority and type-tag presence per the project's legend, applies the [#A] dating rule from that legend, and re-assesses :quick: and :solo: from reconciled facts. Unambiguous calls land autonomously. Ambiguous ones flag NEEDS-USER instead of being guessed. A new Phase E stamps :LAST_AUDIT: on completion.
* docs(todo): add priority and tag schemeCraig Jennings2026-05-281-0/+8
| | | | | | | | | | | | | | | | Adds an explicit [#A]-[#D] legend at the top of todo.org defining what each priority level means in practice. Type and effort tag conventions live alongside, since task-audit enforces both. [#A] now requires a SCHEDULED or DEADLINE date. A task that can't be dated isn't really [#A]. It belongs at [#B]. Tags split two ways. Type tags (:feature:, :chore:, :spec:, :bug:) are mandatory. Effort and autonomy tags (:quick:, :solo:) are optional and orthogonal. Both can apply to the same task. Seeds a Workflow State section in notes.org for the :LAST_AUDIT: marker that open-tasks.org Next Mode reads to decide whether to offer a task-audit run. The marker starts unset. The first task-audit run populates it.
* chore(ai): archive session recordCraig Jennings2026-05-281-0/+38
|
* chore(session): snapshot WIP, session-context, and inbox arrivalsCraig Jennings2026-05-281-0/+172
| | | | | | | | | | Bundles end-of-session state across three concerns. claude-templates/.ai/workflows/open-tasks.org: in-flight restructure of Next Mode into a two-question output (cascade + friction filter), +62 lines. Separate from the iteration-history work that landed in 55adf6e and 684b273. .ai/session-context.org: live session record covering startup through the iteration-history backfill. Captures the no-approvals mode and the full spec-review/spec-response cycle on the working draft. inbox/: five new arrivals from this session: four pearl notes (pattern catalog, prompts-ux, spec-review implementation, prompt collapse) plus one .emacs.d note (whats-next + task-audit). A sixth file, PROCESSED-prefixed, represents mid-handling state on the open-tasks friction-cascade suggestion from earlier in the session.
* feat(workflows): backfill iteration history in spec workflowsCraig Jennings2026-05-282-0/+44
| | | | | | Closes the org-drill backfill TODO. Both workflow files now carry a bottom "Review and iteration history" section with four entries: Draft 1 (linear-emacs origin, 2026-05-23, placeholder timestamp), Review-and-Fold (commit 7f2aea1, 2026-05-23), Requirement addition (commit 55adf6e, 2026-05-28), and this backfill commit. Canonical and mirror updated together. Working directory working/spec-workflows-iteration-history-backfill/ removed on this commit. The spec-response cycle on the working draft (two Codex reviews, three rulesets responses) validated the entry-shape and the content before splice. Codex caught file-history conflation that would have made per-file provenance inaccurate. Craig's direct rationale on the Iteration 1 and Iteration 3 Why lines replaced the original INFERRED markers.
* feat(workflows): add iteration-history requirement to spec workflowsCraig Jennings2026-05-282-8/+43
| | | | | | Specs reviewed under either workflow now carry a bottom =Review and iteration history= section. Each entry is an org subheading with a compound id (timestamp, contributor, role) plus What/Why/Artifacts body fields. The id is opaque. Timestamp, contributor, and role concatenate without implying any decision ordering. spec-review.org adds the gate item, the entry-shape spec, and a "Preserve iteration provenance" principle. spec-response.org adds the matching Phase 4 step and a "history explains provenance" principle. Canonical and mirror updated together.
* feat(workflows): add no-approvals.org project workflowCraig Jennings2026-05-281-0/+78
| | | | Captures the no-approvals mode contract: Craig gives up per-commit approval gates and routine check-ins, Claude keeps /review-code and /voice personal on every commit plus all engineering discipline. Stop only on a real question (recommendation as item 1) or when the planned work is done.
* chore(ai): archive session recordCraig Jennings2026-05-261-0/+97
|
* docs(protocols): gate credential-leak warnings on project type, not the ↵Craig Jennings2026-05-261-0/+2
| | | | | | | | | | credential A session false-alarmed on a leak risk when restoring a credentials doc into a tracked .ai/ file. The reasoning was wrong: a tracked secret is only a public-leak risk where the repo can reach a public remote, which means code projects on public GitHub, the ones that already gitignore .ai/. Personal and documentation projects push to a private single-user repo on cjennings.net, so tracked credentials in their .ai/ files are fine and expected. I added the rule next to the existing "should .ai/ be committed?" decision in protocols.org, since it's a direct corollary of the same code-vs-personal split. The "is this a leak?" question now resolves on which kind of project and remote it is, not on the mere presence of a credential in a tracked file. Origin: an elibrary session raised the false alarm and Craig corrected it.
* docs(protocols): document .ai/project-scripts/ for project-owned scriptsCraig Jennings2026-05-262-1/+3
| | | | | | | | A project script dropped into .ai/scripts/ gets wiped on the next startup, because that dir syncs from the template with rsync --delete. There was no documented home for a project's own scripts, the script-side counterpart to .ai/project-workflows/. I added .ai/project-scripts/ to the Directory Architecture table and noted in startup.org that it sits outside the synced set, like project-workflows/. A script a workflow imports lives there. Naming: a Python module imported via sys.path needs an importable name (underscores), while a CLI-invoked script can stay kebab-case like the template tooling. No mechanism change. Startup Phase A only rsyncs protocols.org, workflows/, and scripts/, so project-scripts/ is already sync-safe. This just documents it.
* feat(notify): default page notifications to --persistCraig Jennings2026-05-263-8/+12
| | | | | | I default page notifications to --persist so a page that fires while I'm away from the desk waits for me instead of auto-dismissing after a few seconds. page-me and status-check already persisted every page. I added --persist to the rest: the alarm, reminder, and meeting-alert examples in protocols.org, the long-running-process completion ping, and the cross-agent-watch message notification. I documented --persist as the default for any page meant to get attention, with a low-value informational ping as the only exception.
* refactor(workflows): split triage-intake into engine + source pluginsCraig Jennings2026-05-267-91/+481
| | | | | | | | The triage-intake workflow had every source baked into one file, so adding or changing a source meant editing the workflow itself. I replaced it with a source-agnostic engine plus per-source plugins named triage-intake.<source>.org. The engine carries the anchor/sentinel logic, the four-bucket model, the Phase A-D orchestration, the todo.org persistence convention, and the exit criteria. Each source's scan, classify, render, and action knowledge moved into its own plugin. Four general plugins ship in the template: personal-gmail, personal-calendar, cmail, and github-prs. Project-specific sources live in the project's .ai/project-workflows/ and are never synced. Phase 0 globs both directories so a project source can't silently drop out of the sweep. I taught INDEX.org and the startup workflow-discovery drift check the namespace. A file matching <engine>.*.org is a plugin of that engine, not an orphan, and gets no trigger entry of its own. A "run the triage-intake workflow" request routes to the engine, never to a plugin.
* fix(inbox-send): preserve dots in copied filenamesCraig Jennings2026-05-262-1/+55
| | | | | | send_file ran filenames through slugify(), which flattens dots to hyphens. That corrupts the engine.plugin.org plugin-namespace convention: triage-intake.personal-gmail.org arrived as triage-intake-personal-gmail.org, which breaks the engine's triage-intake.*.org glob and the routing that depends on the first dot. I added slugify_filename() for filename stems. It keeps dots, hyphens, underscores, and case, collapses only whitespace runs to hyphens, and truncates on a separator boundary. The prose --text path still uses slugify().
* chore(ai): archive session record + regen lint follow-upsCraig Jennings2026-05-251-0/+74
|
* chore(ai): archive session record, regen lint follow-ups, fix dangling todo linkCraig Jennings2026-05-241-0/+184
| | | | The claude-memory clone was removed this session, so the todo entry's file: link to it would dangle — switched to plain text.
* chore(ai): resync workflow and script mirror with canonicalCraig Jennings2026-05-235-2/+376
| | | | The .ai/ mirror lagged claude-templates/.ai/ for three workflows (task-audit, task-review, triage-intake) and two scripts (screenshot.py and its test) — earlier commits updated the canonical copies without resyncing the mirror in the same commit. The startup rsync caught it up; this commit tracks the result so the two stay identical.
* feat(workflows): add spec-review and spec-response workflow pairCraig Jennings2026-05-233-0/+353
| | | | | | | | These two workflows form a reviewer side and an author side for taking a design spec to implementation-ready. spec-review judges a spec against a readiness gate, reads the code before critiquing, evaluates across dimensions, assigns a rubric (Ready / Ready-with-caveats / Not-ready / Needs-research), and writes a <spec>-review.org file when it isn't ready. spec-response consumes that file: it decides accept / modify / reject for every recommendation, weaves the accepts into the spec body, records the modifies and rejects with reasons in a "Review dispositions" section, and reconciles tensions when coupled specs get reviewed together. The review file is the contract between them. Both were validated by a real run on 2026-05-23 before landing here. I then reviewed them against established practice and tightened five things. The readiness gate now covers security/privacy and observability, since a spec shouldn't pass with those undefined. Phase 1 is a fast triage and Phase 3 is the authoritative gate after the code read. Finding severity maps to blocking power. A rejection goes back to the reviewer instead of standing as a unilateral call. And the response loop has an explicit termination condition. I added both to INDEX.org under a new "Specs and design" section with trigger phrases, cross-linked as a pair, and kept the canonical and mirror copies identical.
* chore(ai): archive session record, sweep completed tasks, queue follow-upsCraig Jennings2026-05-221-4/+20
| | | | Archived the session record. Moved six completed tasks from Open Work to Resolved: the 2026-05-04 audit-pass parent, the two commits.md overlay tasks, the make-remove feature, the mcp/ install-pipeline doc, and the wrap-it-up GitHub-host quick fix. Queued the one lint judgment and the task-review staleness note in the inbox for next-session processing.
* feat(workflows): add task-audit content-reconciliation workflowCraig Jennings2026-05-222-0/+99
| | | | A task audit reconciles each open task's recorded content against reality (sessions, email, chat, ticketing, calendar, recordings) and fixes the stale facts. That's distinct from task-review, which grooms relevance and priority. The two compose: review keeps the list lean, and audit keeps the survivors factually honest. Registered it in the workflow INDEX with its trigger phrases.
* chore(todo): close add-tests audit items (1 fixed, 1 moot)Craig Jennings2026-05-221-0/+69
|
* docs(workflows): document GitHub-family assumption in wrap-it-up Step 3.5Craig Jennings2026-05-221-1/+5
| | | | | | Step 3.5's Linear ticket-state sweep uses gh to find the merged PR for a Dev-Review ticket, which assumes a GitHub-family host. That holds today because DeepSat, the only Linear-using project, lives on GHE where gh talks to the API. I added a note flagging the assumption rather than rewriting the step to be provider-agnostic. A future Linear project on GitLab, Gitea, or Bitbucket would need a different PR lookup, but none exists yet, so documenting the boundary beats building for a host we don't have.
* feat(startup): sync language bundles per project on session launchCraig Jennings2026-05-221-0/+2
| | | | | | | | | | Startup synced the .ai/ templates into the current project every session but never checked the language bundle (elisp, python) installed in .claude/. Bundle drift went unnoticed until someone re-ran make install-lang by hand: a generic rule added to claude-rules/ after the last install, or a changed validator hook. scripts/sync-language-bundle.sh closes that gap. It fingerprints which bundle a project has by the presence of the language's own rule files (elisp.md, python-testing.md), then reconciles against the canonical source: auto-fix for rulesets-owned files (.claude/rules/*.md, .claude/hooks/*, githooks/*), surface-only for settings.json, which a project may have customized. CLAUDE.md is left alone. It's seed-only in install-lang and project-owned afterward, the same reason diff-lang skips it. Startup Phase A step 12 calls it for the current project, guarded so older checkouts that lack the script still boot. It writes only under .claude/ and githooks/, disjoint from the .ai/ rsync paths, so the parallel batch stays safe. A script rather than a make target keeps the Makefile-parse layer off the boot path. The absolute rulesets path it depends on is the same one the rsyncs already carry. Tested: 11 bats cases (no-bundle, clean, drifted rule/hook auto-fixed, surfaced settings.json asserted unmodified, absent CLAUDE.md not flagged, python detection, $PWD default, bad path). A smoke run against a copy of a real elisp project's .claude/ caught a perpetual "CLAUDE.md missing" alarm, which is what drove dropping CLAUDE.md from the surface set.
* chore(inbox): queue wrap-up lint and task-review follow-upsCraig Jennings2026-05-211-0/+52
| | | | The lint sweep re-flagged the line-2070 misplaced-heading false positive (a ** inside verbatim markers in a DONE body), and the staleness check counted 12 top-level tasks unreviewed past 30 days.
* feat(workflows): tag <=30min tasks :quick: during task reviewCraig Jennings2026-05-211-1/+8
| | | | | | I added a per-task effort check to the task-review walk. If a task looks like 30 minutes or less and isn't already tagged, mark it :quick: on the heading. When the heading and body don't make the effort clear, ask instead of guessing. A mislabeled :quick: defeats the point, since the tag exists so Craig can grab a genuinely small task in a spare moment. I edited the canonical claude-templates copy and the synced project mirror together, so the next startup rsync won't revert them.
* chore(ai): archive session record and task-review todo cleanupCraig Jennings2026-05-201-0/+162
| | | | Archive the DONE task-review implementation and the cancelled OV-1 skill from Open Work to Resolved. The follow-ups file picks up one lint judgment and the review-habit staleness line for the next daily-prep.
* feat(workflows): add task-review list-hygiene habitCraig Jennings2026-05-205-27/+223
| | | | | | | | | | The new task-review.org workflow is the daily habit that retires the old date-coverage scan. It surfaces the oldest-unreviewed top-level tasks, walks them one at a time, and records each outcome — keep, re-grade, kill, mark DOING, or edit — stamping :LAST_REVIEWED: as it goes. It's a pure Claude workflow, no elisp. open-tasks.org displays the list; this one changes it. task-review-staleness.sh gains a --list mode that emits the N oldest-unreviewed tasks (line, review date, heading), oldest first, so the workflow walks a deterministic batch instead of eyeballing todo.org. Never-reviewed and unparseable-date tasks sort oldest. Seven new bats cases cover ordering, the count limit, exclusions, and output format; count mode is unchanged. startup.org gains the matching nudge. Phase A counts tasks unreviewed for >7 days and Phase C surfaces one line when that count is non-zero, pointing at the workflow. It lives in the template startup.org rather than the project-only startup-extras layer, so every project picks it up the same way it picks up the wrap-up health check. The INDEX entry is added with the "task review" triggers the rename freed up.
* refactor(workflows): rename task-review.org to open-tasks.orgCraig Jennings2026-05-202-9/+13
| | | | | | | | The list-and-pick-next workflow was named task-review.org, but "task review" better describes a list-hygiene habit that re-grades and prunes tasks, not one that just displays them. I'm freeing the task-review.org name (and the "task review" trigger) for that habit, which lands next. This workflow goes back to open-tasks.org — the name it carried before it merged with whats-next.org. Its content and INDEX entry drop the "task review" trigger and point at task-review.org for the hygiene habit. Behavior is unchanged; only the name and the routing phrases move. The rename touches both the canonical workflow and the project mirror.
* docs(workflows): swap wrap-up date-coverage scan for task-review health checkCraig Jennings2026-05-201-23/+10
| | | | | | | | The date-coverage scan flagged every open [#A]/[#B] task with no DEADLINE or SCHEDULED, on the assumption that high-priority work needs a date. That assumption is wrong (research and watch-list tasks are legitimately dateless), so the scan generated dismiss-or-paper-over cleanup at every wrap-up. The replacement watches the daily task-review habit instead. It calls task-review-staleness.sh todo.org 30 and, when the count is non-zero, writes one summary line to the follow-ups file: N top-level tasks unreviewed for >30 days. There's no per-task dump, because the per-task walk is the review habit's job. Staleness, not datelessness, is the signal worth surfacing. The change lands in both the canonical workflow and the project mirror.
* test(scripts): add task-review-staleness.sh + bats harnessCraig Jennings2026-05-202-0/+232
| | | | | | | | First component of the daily task-review habit from docs/design/task-review.org. The staleness count is the shared primitive both the wrap-up health check (threshold 30) and the startup reminder (threshold 7) call, so it lives in one tested script rather than being reimplemented in each workflow. The script counts top-level todo.org tasks whose review has gone stale: depth-2 headings with a TODO/DOING/VERIFY keyword and an [#A]/[#B]/[#C] cookie, where LAST_REVIEWED is missing, unparseable, or older than the threshold. Age uses a strict greater-than, so a task reviewed exactly N days ago is still fresh. Today normalizes to local midnight before the diff, and the day count rounds to the nearest day, so a DST hour can't push a boundary task across the line. Twelve bats cases cover the normal, boundary, and error categories. Dates are generated relative to the current date rather than hardcoded. The script path resolves as the sibling-of-parent of the test file, so the suite runs identically from the canonical claude-templates tree and the rsync'd project mirror. Makefile test target now globs .ai/scripts/tests for bats alongside scripts/tests.
* chore(ai): archive session record and wrap-up lint followupsCraig Jennings2026-05-201-0/+65
|