Skip to content

Add GitHub PR workrooms as durable threads in project channels #129

Description

@PollyBot13

Direction

A pull request is more than a diff. It accumulates intent, checks, review
findings, decisions, agent work, and the reason the final code exists. Those
facts are currently split between GitHub, chat, local agent sessions, and CI
logs.

The desired experience is:

A ClickClack channel represents a project or repository. Each GitHub pull
request gets one durable thread in that channel where humans and agents can
work with its current state and important transitions close at hand.

The thread root is a live PR card. Its replies form the workroom. Merge, close,
and reopen update the same workroom instead of creating, archiving, or replacing
channels.

GitHub remains authoritative for repository state, reviews, checks, and merge
decisions. ClickClack owns the durable conversation and its visibility. The
adapter is a deliberately thin, read-only bridge.

This direction is inspired by the open-source
Buzz “branch as room” product story. It adapts
that user experience to ClickClack's existing API-first architecture; it does
not add Git hosting, a workflow engine, or provider authority to ClickClack.

Why this issue was reframed

The original proposal used one managed ClickClack channel per pull request.
That made external channel identity, reconciliation, naming, archive behavior,
and visibility part of the required core contract.

PR #131 explored a richer GitHub Projects room subsystem. Maintainer review
closed it because the approximately 11,000-line patch combined too many product
surfaces while leaving a central authorization boundary unresolved: private
repository content could reach workspace-public ClickClack channels, and source
participants are not an access-control list for the destination channel.
Maintainer guidance explicitly kept the narrower product and security decision
in this issue.

A thread-first model preserves the useful product idea while removing the two
largest risks:

  • one configured ClickClack project channel can organize many PR workrooms;
  • the adapter does not create channels or derive ClickClack visibility from
    GitHub membership;
  • existing message nonce recovery provides durable external identity without a
    new provider-identity schema;
  • existing message editing keeps the root PR card current;
  • existing flat threads, idempotent replies, and public routes provide the
    collaboration and linking surface;
  • closing or reopening a PR changes the workroom state without changing the
    channel lifecycle.

PR #131 remains useful design research, but a successor should start from
current main and should not reuse its subsystem as an implementation base.

Product behavior

For each explicitly configured public GitHub repository:

  1. An operator maps the repository to an existing ClickClack project channel.
    Provider payloads cannot create a channel, choose its kind, or change its
    visibility.
  2. A supported PR webhook is verified, then treated as a hint to fetch current
    authoritative GitHub state.
  3. The adapter finds or creates one root message using a deterministic nonce
    such as github:<repository-id>:pr:<number>:root.
  4. The root message is a concise PR card containing title, author, base/head,
    draft/open/merged/closed state, review/check summary, and the canonical
    GitHub link. Later reconciliation edits that same bot-authored root.
  5. Meaningful state transitions become bounded, idempotent thread replies.
    Ordinary human and agent discussion remains untouched in the same thread.
  6. Merge or close updates the card and adds one final transition reply. Reopen
    restores the same root and history.

Every delivery reconciles current state rather than applying an assumed event
order. Duplicate and out-of-order deliveries therefore converge instead of
rolling the workroom backward. The adapter recovers roots and replies through
ClickClack's public nonce APIs and keeps no second authoritative copy of PR or
conversation state.

Implementation boundary

The GitHub App/webhook runtime should live in a small companion adapter rather
than in the ClickClack server. This follows the repository precedent of keeping
provider connectors outside core while upstreaming only reusable, generic
primitives.

The implementation PRs below are intended for that companion adapter and will
be linked from this issue when opened. Each PR will reference this issue and
include its own focused tests and real-behavior proof. No implementation has
started as part of this reframe.

Current main appears to provide the required core surface already:

  • create a channel root with a deterministic nonce;
  • recover a root by nonce after interruption or restart;
  • edit the bot-authored root;
  • create idempotent thread replies;
  • fetch the bounded thread;
  • resolve a canonical route for the thread root;
  • consume durable workspace events when ClickClack-side observation is useful.

No ClickClack core PR is assumed. If an end-to-end proof identifies a concrete
missing generic primitive, that gap should receive its own narrowly scoped issue
and PR rather than expanding the adapter patch.

Proposed delivery sequence

Working titles are listed now so related work can be coordinated and automation
can track the intended sequence. Links and exact titles will be updated when a
PR opens.

  • PR A — feat(github): create public PR workrooms in configured channels

    • GitHub App/webhook skeleton, raw-body signature verification, installation
      and repository allowlist, and explicit repository-to-channel mapping.
    • Public repositories only; private, unknown, or visibility-ambiguous sources
      fail before any ClickClack write.
    • Read-only authoritative PR fetch.
    • Deterministic root nonce, interrupted-create recovery, root-card creation
      and update for opened/synchronize events.
    • Duplicate delivery, invalid signature, restart recovery, and physical local
      ClickClack proof included in the same PR.
  • PR B — feat(github): reconcile PR lifecycle in existing workrooms

    • Draft/ready, merged/closed, and reopened transitions.
    • Deterministic transition-reply nonces and current-state fingerprints.
    • Out-of-order delivery convergence without modifying human replies.
    • Exact-head proof that close and reopen retain the root ID, route, and thread
      history.
  • PR C — feat(github): summarize reviews and checks in PR workrooms

    • Bounded review and completed-check summaries.
    • No job-by-job, commit-by-commit, comment, or full timeline mirroring.
    • Deduplication and supersession rules that keep the thread useful under CI
      churn.
    • Exact-head noisy-event and bounded-rendering proof.

Possible later providers—issues, deployments, incidents, tickets, or agent
assignments—should reuse the adapter-side work-item model only after the PR
workroom proves the contract. Dedicated per-item channels can remain a later
option for work that genuinely needs a separate audience or lifecycle.

V1 scope

  • One reference GitHub App adapter deployed outside the ClickClack server.
  • Public repositories only.
  • Explicit installation, repository, ClickClack workspace, and destination
    channel configuration.
  • Read-only GitHub permissions for repository metadata, pull requests, reviews,
    and checks as required by the delivered phase.
  • pull_request first, followed by bounded pull_request_review and
    check_suite handling in the staged sequence above.
  • Deterministic ClickClack nonces for the root and semantic lifecycle replies;
    GitHub delivery IDs are diagnostic correlation values, not identity.
  • Sanitized and length-bounded Markdown, validated GitHub URLs, and secret-safe
    logs.
  • Fake-provider tests plus real local ClickClack lifecycle proof.

Non-goals

Security and privacy contract

  • The destination ClickClack channel is operator-configured and already exists.
    A webhook cannot select or downgrade visibility.
  • V1 verifies from authoritative GitHub state that a configured repository is
    public before writing any provider-derived content.
  • Private, unknown, removed, or visibility-ambiguous repositories fail closed.
  • Webhook signatures are checked over the raw request body with constant-time
    comparison before JSON processing.
  • Invalid signatures, oversized bodies, unsupported events, and unconfigured
    installations fail before ClickClack writes.
  • GitHub App credentials, webhook secrets, ClickClack tokens, and raw payloads
    are never posted into channels or emitted in logs.
  • Provider text is untrusted Markdown: fields are bounded, control characters
    are normalized or rejected, and links are accepted only from validated GitHub
    URLs.
  • GitHub remains authoritative. The adapter is read-only and cannot turn a
    ClickClack reaction or message into repository authority.

Acceptance criteria

  • Opening a configured public PR creates exactly one root card in the configured
    channel and exposes one linkable thread workroom.
  • Replay, concurrent duplicate delivery, and restart between ClickClack write
    and acknowledgement do not create a second root or semantic reply.
  • Title, head, draft, review, check, and lifecycle changes update the same
    workroom without replacing its human discussion.
  • Out-of-order deliveries reconcile current GitHub state rather than rolling the
    card backward.
  • Merge and ordinary close are distinct; reopen retains root ID, canonical
    route, and thread history.
  • Invalid signatures, unknown repositories, private repositories, wrong
    workspace/channel bindings, revoked credentials, and missing ClickClack scope
    fail without leaking provider content.
  • The adapter uses documented public ClickClack API/SDK contracts and needs no
    server database or plugin access.
  • Each staged PR carries focused automated tests and inspectable exact-head
    runtime proof for the behavior it introduces.

Related work disposition

Remaining coordination question

The recommended path is to begin with PR A in a companion adapter repository,
link it here, and let its end-to-end proof reveal whether any ClickClack core
change is actually necessary. If maintainers prefer an examples/ location in
this repository, the same staged contract can move without changing the product
or security boundary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal priority bug or improvement with limited blast radius.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:securityThis issue is about security boundaries, credentials, authz, sandboxing, or sensitive data.issue-rating: 🌊 off-meta tidepoolIssue quality rating does not apply to this item.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions