Files
boxmaker/docs/plans/M4b/04-brokerd-answer-post.md
T
kyleandClaude Opus 5.5 2d415fc1be Plan M4b: approvals over Mattermost in 15 tasks
Skeletons for new files only; existing files change by exact edits, so no task overwrites an
earlier one's work. Each task's tests were run against a reference built in order from master,
with the gate at each step and mutations checked; each skeleton compiles and fails its tests, and
every todo!() comment was read beside its signature. The reference is kept off this machine.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 03:34:21 -07:00

3.9 KiB

M4b task 04: an answer records its post; the table says what is pending

Branch: m4b (run git switch m4b; git status --short must be empty, otherwise stop) Commit subject: brokerd: answers carry the Mattermost post; Table::contains

Goal

When an approval is answered from Mattermost, the audit record must name the approver and the post that answered (the Approval record has had a post field since M3a, always None so far). And the courier (task 06) needs to ask the approval table whether an approval is still pending.

ledger.rs is at 499 lines, so first Answer and Answered move to approvals.rs, beside Verdict and Entry; ledger re-exports them, so every crate::ledger::Answer still works.

Files

  • Copy: crates/brokerd/tests/ledger_post.rs, and new versions of three given tests, crates/brokerd/tests/broker_pending.rs, broker_sequence.rs and ledger_answer.rs (the only change in each: post: None, in every Answer::Approved and Answer::Refused)
  • Modify: crates/brokerd/src/approvals.rs, crates/brokerd/src/ledger.rs, crates/brokerd/src/admin.rs, docs/implementer-log.md

The changes, exactly

1. Move. Cut enum Answer and struct Answered from ledger.rs, with their doc comments, and paste them into approvals.rs, before pub struct Entry. In ledger.rs, add pub use crate::approvals::{Answer, Answered}; next to use crate::approvals::Verdict;. In approvals.rs, use proto::{DenyReason, PendingApproval, Timestamp}; becomes use proto::{DecisionRecord, DenyReason, PendingApproval, Timestamp};.

2. post. In approvals.rs, Answer becomes:

/// An owner's answer to a pending call.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Answer {
    /// `by` is "bxctl" or a Mattermost user id; `post` the Mattermost post that answered.
    Approved {
        by: Option<String>,
        post: Option<String>,
    },
    Refused {
        by: Option<String>,
        post: Option<String>,
        reason: Option<String>,
    },
    Expired,
}

In ledger.rs, answer, step 3 becomes:

        let (answer, by, post, reason) = match &answer {
            Answer::Approved { by, post } => {
                (ApprovalAnswer::Approved, by.clone(), post.clone(), None)
            }
            Answer::Refused { by, post, reason } => (
                ApprovalAnswer::Refused,
                by.clone(),
                post.clone(),
                reason.clone(),
            ),
            Answer::Expired => (ApprovalAnswer::Expired, None, None, None),
        };

and in step 6, the AuditEvent::Approval field post: None, becomes post,.

In admin.rs, the two answers bxctl gives get post: None, after their by: Some(BY.to_string()),.

3. contains. In approvals.rs, impl Table, before take:

    /// Is approval `id` still pending? Only a hint: it may be taken the moment after.
    pub fn contains(&self, id: u64) -> bool {
        self.lock().contains_key(&id)
    }

Steps

  • 1. Copy. git switch m4b, then cp docs/plans/M4b/files/crates/brokerd/tests/ledger_post.rs docs/plans/M4b/files/crates/brokerd/tests/broker_pending.rs docs/plans/M4b/files/crates/brokerd/tests/broker_sequence.rs docs/plans/M4b/files/crates/brokerd/tests/ledger_answer.rs crates/brokerd/tests/
  • 2. See it fail. cargo test -p brokerd --test ledger_post. Expected: it does not compile (post is not a field of Answer; contains does not exist).
  • 3. Make the three changes. Run cargo fmt --all.
  • 4. See it pass. cargo test -p brokerd. Expected: every suite passes; ledger_post 3 passed. wc -l crates/brokerd/src/ledger.rs is under 490.
  • 5. Run the gate. make gate. Expected last line: gate: ok.
  • 6. Log and commit. git add crates/brokerd docs/implementer-log.md Cargo.lock && git commit

Done when

  • cargo test -p brokerd passes; make gate prints gate: ok.