Skip to content

connect: admit boosts on the agent's own posts as a boosted trigger - #877

Open
jeremy wants to merge 3 commits into
mainfrom
connect-boosted
Open

jeremy wants to merge 3 commits into
mainfrom
connect-boosted

Conversation

@jeremy

@jeremy jeremy commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Problem

Someone boosts a comment, Campfire line or post the agent wrote, and the agent never hears about it. Basecamp does deliver the boost: Boost#track_boost_event tracks a boosted event on the boosted recording, and Event::Addressees addresses it (reason boosted) to the boostee. But basecamp connect's admission matrix only knew mentioned, subscribed, assigned and completed, so boost.created was discarded as not_in_matrix. People boost an agent's question and wait, and the agent waits too.

Card: https://app.basecamp.com/2914079/buckets/46292715/card_tables/cards/10396224165

Change

boost.created now admits under a new boosted trigger (internal/connector/admission/matrix.go, boost.go, verdict.go):

  • Addressing. A boost is admitted only when it is on the recording itself and the recording's creator is the agent. A boost on someone else's recording is discarded as not_addressed.
  • Trust. The booster is gated like a mention's performer, in every trust mode, and the rule is not OperatorOnly. That means the operator and allowlisted people as operators, and project members (after the membership read) as participants. Someone outside the trust set is discarded at the gate, before any read. The agent's own boosts are discarded as agent_authored. So are other agents' boosts on the push lane. Delegated boosts are discarded as delegated. An --allow-agent agent gets in by mention only, so its boosts are discarded as not_addressed. Two agents that allow each other can't boost each other into a loop.
  • Reads. boost.created names no recording type, and the SDK's Summarize refuses to route it (ErrNoRecordingType). So the boost is read first (GET /boosts/:id, a new Reads.Boosts). That read gives the boosted recording's type, which Summarize then routes by, and the boost's content. If the boost is gone, is missing its recording or booster, or names a different recording, the event is blocked as read_failed (retained, as for any read that doesn't answer). A booster other than the trusted performer is discarded as untrusted_author.
  • What's handed off. requester_id is the booster, role is the booster's role, and requester_name is the booster's name. acknowledge is false: a boost is a reply, not a request. owner is false. content is the boost itself; recording is the agent's post it answers.
  • Where it's answered. Where the boosted recording lives. A boost on the agent's comment replies in that comment's thread and joins its conversation key (recording:<parent>). A boost on a chat line goes to its Campfire. Anything else replies on the recording itself.
  • Docs. The step-25 comments now say boosted is built and queued is still pending. skills/basecamp-connect/SKILL.md lists boosted among the triggers. For boosted it skips the acknowledgement and the card move, defaults to silence, and says a boost is never an operator's go.

RequiresServed: true

A boost in a project connect.json doesn't serve is discarded at the gate, before any read, rather than blocked as no_route. Only mentioned and assigned are blocked in an unserved project, and they get a holding reply ("this project isn't served yet"). That reply exists because someone asked the agent for something and is waiting. A boost asks for nothing, so a holding reply to a thumbs-up would answer nobody. subscribed and completed, the other non-request triggers, use true for the same reason. It also keeps boosts in unserved projects from spending two reads each.

Deliberately not done

  • queued and edited. The rest of step 25 and step 28 are out of scope.
  • Boosts on a recording's events (e.g. a boost on a completion in a to-do's history). details.boosted_event_id is non-null for these. Basecamp addresses such a boost to whoever performed the event, not to the recording's author, so the creator check would be wrong for it. Telling whether the agent performed that event would take an events read this rule doesn't make. These are discarded as not_addressed, from the details alone, at no read cost.
  • published_by vs. creator. bc3's boostee for a recording is published_by || edited_by. The summary carries creator, and the two differ only for a draft someone else published. A message the agent drafted but someone else published would be admitted although Basecamp addressed the boost to the publisher. One the agent published from someone else's draft would be missed. None of the agent's comments or lines are drafts.
  • Owner. A boost from the owner is not owner. Whether an operator's boost on an agent's explicit ask should count as the go is a separate policy question; the skill says it doesn't.

What can now reach the agent that couldn't before

A boost on a recording the agent created, in a served, in-scope project, given by a person the trust settings already let mention the agent. The recording must not be drafted or trashed, and the boost must not be delegated. That's the whole set, and each case is intended. Cost: each such boost takes one boost read plus one summary read. For a chat line, the summary read includes the SDK's Campfire discovery. In project trust mode this applies to every member's boost on anything in a served project, not just the agent's posts, because the creator is only known after the summary read.

Tests

  • internal/connector/admission/boost_test.go covers:
    • admission on the agent's comment: trigger, requester, role, owner, content = boost, reply, conversation key, and the summary ref routed by recording type
    • chat line and message reply shapes
    • a boost on someone else's recording is not admitted
    • untrusted, allowlisted and project-member boosters
    • agent, delegated and allowed-agent boosts are discarded with zero reads
    • unserved project, event boost and bad details settle with zero reads
    • boost read failures
    • a trashed post is stale
  • TestABoostOnTheAgentsCommentIsHandedOffAsAReply runs end to end: details survive the ledger, then admission, then the request line.
  • An SDK test checks that NewSDKReads().Boosts reads /boosts/:id and returns the recording type.
  • Mutation-checked: dropping the creator check, the booster check, RequiresServed or the event-boost discard each fails a test.
  • bin/ci passes on Linux. Locally on macOS, lint flags internal/connector/driver/proctime_darwin.go, a darwin-only file this PR doesn't touch.

Summary by cubic

Lets basecamp connect hear when someone boosts a comment, Campfire line, or post the agent wrote. boost.created was previously rejected as not_in_matrix, so boosts sat unanswered even though Basecamp addresses them to the recording's author.

Adds a boosted trigger that admits a boost only when it lands on the recording itself and the recording's creator is the agent. The booster is trusted like a mention's performer in every trust mode; an agent's own, delegated, and --allow-agent boosts stay discarded. Since boost.created names no recording type, admission reads the boost first (GET /boosts/:id), which supplies the type the summary read routes on and the boost's content, handed off as the request. The reply goes where the boosted recording lives: a comment's thread, a line's Campfire, or the recording itself. acknowledge and owner are false, and the rule requires a served project so boosts in unserved projects are discarded rather than held (a holding reply to a thumbs-up answers nobody).

Details that lack a boost id, or whose boosted_event_id key is absent, are an invalid pointer and discarded before any read; only an explicit null is read as a boost on the recording. A boost on one of a recording's events is discarded as not_addressed: Basecamp addresses that to the event's performer, not the recording's author. An Agent booster is refused as agent_authored at the boost read — the poll lane carries no actor type, so an allowlisted Agent passes the gate, and the read is where its type shows.

Out of scope

  • queued (rest of step 25) and edited (step 28) triggers.
  • published_by vs. creator mismatches, and whether an operator's boost on an explicit ask counts as a go.

Written for commit f1bbe9a. Summary will update on new commits.

View guided diff Turn on auto-fix

Copilot AI balanced review requested due to automatic review settings October 12, 2026 16:10
@jeremy
jeremy requested a review from a team as a code owner October 12, 2026 16:10
@jeremy

jeremy commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

🤖 @codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-13T07:43:58.097465Z f1bbe9a Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added tests Tests (unit and e2e) skills Agent skills labels Oct 12, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 14 files

You’re at about 90% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread internal/connector/admission/boost.go Outdated
Comment thread skills/basecamp-connect/SKILL.md Outdated
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: b2f7937d6e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jeremy

jeremy commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

🤖 @codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 699cd5de5d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jeremy

jeremy commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

🤖 Review summary, head 699cd5d

  • CI: 24 checks pass. Two fail: Security and Trivy Security Scan. Trivy's Go-module vulnerability scan exits 1 with no change to go.mod here, and the Security workflow also fails on main (run at 06:46 UTC today, head 40e4757) and on other branches. It's pre-existing and not caused by this PR. bin/ci passes on Linux.

  • Codex: reviewed 699cd5d, no major issues.

  • cubic: reviewed b2f7937 and raised two findings. Both are fixed in 699cd5d and their threads resolved:

    • a missing boosted_event_id is now invalid_pointer, not a boost on the recording
    • the skill summary now says only requests that ask for something are acknowledged

    cubic has not re-reported on 699cd5d.

  • Copilot: no review on this PR (quota likely exhausted).

  • Open threads: 0.

  • For a human: whether an operator's boost on an agent's explicit ask should count as the "go". The skill currently says it does not, and owner is false for boosts.

@jeremy

jeremy commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

🤖 @cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

🤖 @cubic-dev-ai review

@jeremy I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 14 files

You’re at about 92% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread internal/connector/admission/boost.go
jeremy added a commit that referenced this pull request Oct 12, 2026
A boost on the agent's own post arrives as a boosted reply (#877), and the
skill treated it as approving nothing. An agent asking the operator for a
go had to wait for a mention.

The agent now declares an ask when it posts one, with `basecamp connect ask
<url|id>`, recorded in the ledger's new asks table. Nothing reads a post's
text to decide it is an ask. The operator's boost on a declared ask, and
only the operator's in every trust mode, is the go: admission names the
boosted post on the operator's verdict, and the ledger takes the ask's go
in the verdict's own transaction, so one ask gets one go. A second boost,
or one removed and given again, is an ordinary boosted reply; the event
that took the go keeps it when decided again; a go closed unwritten
(before this run, unreadable) is given back. Asks do not expire.

The request line carries "go": true on that request. The skill says the
go approves what the ask names and nothing else: not a merge or deploy the
ask did not name, and never a connector change.
@jeremy

jeremy commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

🤖 @codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: dd1b6b7348

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jeremy

jeremy commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

🤖 @cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

🤖 @cubic-dev-ai review

@jeremy I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 14 files

You’re at about 94% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

View guided diff | Turn on auto-fix | Re-trigger cubic

@jeremy

jeremy commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

🤖 Head dd1b6b7. Codex and cubic reported no issues on this head. CI: everything passes except Security and Trivy Security Scan, which fail on main too; that failure predates this PR and is being fixed separately in #886 (fix-security-ci). Once that merges, this branch will be rebased onto main.
Review threads: 3 resolved (3 fixed).

jeremy added a commit that referenced this pull request Oct 12, 2026
A boost on the agent's own post arrives as a boosted reply (#877), and the
skill treated it as approving nothing. An agent asking the operator for a
go had to wait for a mention.

The agent now declares an ask when it posts one, with `basecamp connect ask
<url|id>`, recorded in the ledger's new asks table. Nothing reads a post's
text to decide it is an ask. The operator's boost on a declared ask, and
only the operator's in every trust mode, is the go: admission names the
boosted post on the operator's verdict, and the ledger takes the ask's go
in the verdict's own transaction, so one ask gets one go. A second boost,
or one removed and given again, is an ordinary boosted reply; the event
that took the go keeps it when decided again; a go closed unwritten
(before this run, unreadable) is given back. Asks do not expire.

The request line carries "go": true on that request. The skill says the
go approves what the ask names and nothing else: not a merge or deploy the
ask did not name, and never a connector change.
@jeremy

jeremy commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

🤖 @codex review

@jeremy

jeremy commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

🤖 @cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

🤖 @cubic-dev-ai review

@jeremy I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 14 files

You’re at about 98% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

View guided diff | Turn on auto-fix | Re-trigger cubic

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: f9ae3776bb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jeremy

jeremy commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

🤖 Head f9ae377, rebased onto main at f238b79 now that #886 has merged. Every check passes, Security and Trivy Security Scan included. Codex and cubic reported no issues on this head. No review threads are open.
Not reported on the head: Copilot (requested, has never reported on this PR) — f9ae377

Basecamp addresses a boost to the person whose recording was boosted, but
the admission matrix knew only mentioned, subscribed, assigned and
completed, so boost.created was discarded as not_in_matrix and the agent
never heard a boost on its own comment, line or post.

boost.created now admits under boosted when the boost is on the recording
itself and the recording's creator is the agent. The booster is trusted
like a mention's performer in every trust mode (not operator-only); agent,
delegated and allowed-agent boosts stay discarded. The boost is read first,
for the boosted recording's type (the summary read routes by type, and
boost.created names none) and for its content, which is what is handed
off. The request is answered where the boosted recording is: a comment's
thread, a line's Campfire, otherwise the recording. Acknowledge is false,
owner is false, and the rule requires a served project.

A boost on one of a recording's events is discarded as not_addressed
before any read: Basecamp addresses that to the event's performer, which
this rule does not read. queued (the rest of step 25) and edited (step 28)
are not built.
…he recording

A missing key said nothing about where the boost landed, but decoded as
null and passed as a boost on the recording itself. Require the key, and
read only an explicit null as the recording. Also qualify the skill's
summary: only requests that ask for something are acknowledged.
The poll lane carries no actor type, so an Agent person the operator
wrongly put in the allowlist passes the gate. The boost read names the
booster's personable type; refuse an Agent there as agent_authored, as
authorTrusted refuses an Agent author.
jeremy added a commit that referenced this pull request Oct 13, 2026
A boost on the agent's own post arrives as a boosted reply (#877), and the
skill treated it as approving nothing. An agent asking the operator for a
go had to wait for a mention.

The agent now declares an ask when it posts one, with `basecamp connect ask
<url|id>`, recorded in the ledger's new asks table. Nothing reads a post's
text to decide it is an ask. The operator's boost on a declared ask, and
only the operator's in every trust mode, is the go: admission names the
boosted post on the operator's verdict, and the ledger takes the ask's go
in the verdict's own transaction, so one ask gets one go. A second boost,
or one removed and given again, is an ordinary boosted reply; the event
that took the go keeps it when decided again; a go closed unwritten
(before this run, unreadable) is given back. Asks do not expire.

The request line carries "go": true on that request. The skill says the
go approves what the ask names and nothing else: not a merge or deploy the
ask did not name, and never a connector change.
@jeremy

jeremy commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

🤖 Rebased onto main at d742884 (head f1bbe9a), because main had moved and this PR showed CONFLICTING.

What conflicted: only skills/basecamp-connect/SKILL.md. main (#890 and the compaction work before it) moved the subagent's instructions into skills/basecamp-connect/subagent.md. It also added a test capping the per-request part of SKILL.md at 14,000 characters, the compaction cut, and main itself sits at 13,995.

How I resolved it: I took main's files and re-applied this PR's skill text where it now lives.

  • SKILL.md: the boosted trigger; acknowledge and owner naming it; the frontmatter. To stay under the cap I tightened two parenthetical phrases in the request-line field list without changing their meaning: "Anything else, missing included, is a participant's" and "Missing means false". The per-request part now ends at 13,998 characters.
  • subagent.md: acknowledgement and card moves skip boosted; a boost approves nothing; the boosted trigger, with what recording, content and requester_name are on a boost.

No code conflicted. go test ./internal/connector/..., the skill layout test, skill drift, sync-skills, eval patterns and surface checks pass locally.

@jeremy

jeremy commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

🤖 @codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: f1bbe9a8c4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jeremy

jeremy commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

🤖 Head f1bbe9a, rebased onto main at d742884. The conflict and its resolution are explained here: #877 (comment)

  • CI: 26 checks pass and 5 are skipped, Security and Trivy Security Scan included.
  • Mergeable: yes.
  • Reviews: Codex reviewed this head.
  • Threads: none open.
    Not reported on the head: Copilot (requested, has never reported on this PR) — f1bbe9a

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skills Agent skills tests Tests (unit and e2e)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant