Skip to content

Workflow Rituals

:pa recognises five trigger phrases and runs the corresponding playbook. You don't have to use these exact words — anything close enough works — but the explicit triggers are predictable.

standup (or morning)

When you start a session and today.md is empty, or you say standup explicitly, the AI:

  1. Reads the current week plan, today.md (if any), recurring ops, and your open OpenProject work packages
  2. Proposes a 3–7 item stack ordered by priority. Each item: WP ID, subject, one-phrase rationale ("in this week's focus", "blocking ACME release", "carryover from yesterday")
  3. Waits for your confirm
  4. On accept, writes .bnerd/pa/scheduling/today.md

Example:

You: morning

bnerd (chief-of-staff):

Today's stack — confirm or edit:

  1. #7695 Test ticket — carryover from yesterday
  2. #7821 Migrate observability for ACME — in this week's focus
  3. #7902 Rotate gitlab-runner certs — recurring Tuesday duty
  4. #7950 Review monitoring dashboards (Verena) — team support

Write today.md? [y/n]

done (or done — short note)

The core walking-through-the-day flow. When you say done:

  1. The AI identifies the top unchecked item
  2. Calls op_timer_status — if a pickup timer is running for this WP it stops it (using the elapsed time as the logged hours). Otherwise it asks you for hours.
  3. If you supplied a one-liner ("done — fixed Squid TLS"), drafts a short audit-quality WP comment from it
  4. Single confirmation: "Close #1234, log 0.75h, post comment '…'?"
  5. On accept, fires:
    • op_timer_stop or op_log_time (whichever applies, depending on step 2)
    • op_update_work_package (status → closed)
    • op_add_comment (only if you gave a one-liner)
    • mission_propose_today (with this item ticked)
  6. Surfaces the next: "Next: #5678 — Migrate observability for ACME. Pick it up?" — and on accept, calls op_timer_start for it.

The free-text fields auto-tag via bnerd.ai; the close + log are recorded in ~/.bnerd/openproject-audit.jsonl.

Pickup timers

:pa wraps OpenProject's native ongoing time entries via three tools:

  • op_timer_start work_package=… project=… — creates a running OP time entry. Visible in the OP web UI from this moment.
  • op_timer_stop — finalises it: computes elapsed from startTime → now, PATCHes ongoing=false plus hours. Returns the finalised entry as JSON.
  • op_timer_status — reads the active timer (or returns {"active": false}).

Single timer per user — starting a second one returns a structured error so nothing is overwritten silently. None of the three are confirm-gated; they're high-frequency interactions you trigger explicitly. If op_timer_stop sees an elapsed of less than a minute or more than 24 hours it refuses; pass hours=<decimal> to override the elapsed calculation and finalise anyway. The same override also dismisses a stuck timer whose startTime is corrupt or empty.

Because state lives in OpenProject, killing :pa mid-task is fine: next session, op_timer_status shows the same running entry (with the real elapsed since you started).

Example:

You: done — fixed Squid TLS, verified end-to-end with curl

bnerd:

7695 — close + log 0.75h + comment:

Resolved: Squid TLS configuration corrected; curl end-to-end verification passed.

— via bnerd.ai

Confirm? [y/n]

replan (or replan, fire just came in for ACME)

When priorities shift mid-day:

  1. AI re-reads OpenProject for what changed since today.md was written
  2. Considers the new context you gave it
  3. Proposes a rewritten unchecked tail — preserving every checked item above the cursor
  4. Single confirm → today.md is rewritten in place

The checked entries are facts and are never touched. You can replan as many times per day as you want; the time you've already logged is in OpenProject and survives any re-plan.

debrief (or eod)

End of day:

  1. Summarises today: closed N WPs (list), logged X hours, deferred Y to tomorrow (list)
  2. Mentions any unread notifications worth following up on
  3. If today's date doesn't match what's in today.md, calling mission_propose_today with the new date auto-archives yesterday's stack to .bnerd/pa/scheduling/archive/YYYY-MM-DD.md

weekly (or monday)

Once a week — usually Monday — the orchestrator runs:

  1. AI reads team capacity (mission_team), active deployments (mission_deployments), recurring ops, the strategic ROADMAP, decisions accepted in the last 14 days, and last week's archived plan
  2. Proposes current.md for the new week with one focus item per team member or workstream + a risks list
  3. Single confirm → mission_propose_week (auto-archives the prior week)

current.md is a markdown+YAML document at .bnerd/pa/scheduling/current.md; you can edit it by hand any time, the AI honours edits.

triage slack (optional)

When the Slack integration is enabled, you can hand the inbox to the AI:

  1. Type triage slack, process my inbox, or "what's in my slack"
  2. AI calls slack_inbox_list, fetches context for substantive items via slack_get_thread, and either drafts replies (slack_propose_reply → preview → slack_send_reply with one final confirm) or proposes tickets (pa_propose_ticket with the thread excerpt as the description body)
  3. From :pa, the r hotkey drafts a reply to the most-recent inbox event; t turns the latest thread into a ticket

If Slack isn't configured the tools error with a clear "not configured" message — the AI won't pretend they exist.

Ad-hoc mission edits (no ritual required)

Outside the rituals above you can ask :pa to update mission-repo files directly: bump a team member's capacity, draft a new decisions/ ADR, refresh a client brief, add an entry to ops/recurring.md, or sketch a new section in ROADMAP.md. The assistant uses mission_patch_file for surgical edits (preferred — only the changed lines move) and mission_write_file for new files or full rewrites. Both tools are sandboxed: paths are rejected if they resolve outside the mission repo, and today.md / current.md remain reserved for the typed mission_propose_* tools so their schema and auto-archive logic stay intact.

Every edit is confirm-gated, so even instructions like "just update Tobias's capacity to 36" still get a single integrity prompt before the file changes on disk.

Aren't these just prompts?

Yes. The rituals are encoded in the :pa system prompt — there's no special state machine in code. That's deliberate: the playbook is in plain language, you can override it ("forget the standup, just answer this"), and refining the rituals is a markdown edit, not a code change.

The system prompt is at pkg/ai/chat/prompt_pa.md.