You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
An operator maps the repository to an existing ClickClack project channel.
Provider payloads cannot create a channel, choose its kind, or change its
visibility.
A supported PR webhook is verified, then treated as a hint to fetch current
authoritative GitHub state.
The adapter finds or creates one root message using a deterministic nonce
such as github:<repository-id>:pr:<number>:root.
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.
Meaningful state transitions become bounded, idempotent thread replies.
Ordinary human and agent discussion remains untouched in the same thread.
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
Creating one ClickClack channel per PR.
Creating, renaming, archiving, or reconciling ClickClack channels from GitHub
payloads.
Private-repository synchronization before ClickClack has a separately agreed
destination authorization contract.
Treating GitHub participants or project members as ClickClack ACLs.
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.
feat: add GitHub project rooms #131 provides useful UX and webhook lessons, but its code and private-content
authorization model are not the foundation of this implementation.
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.
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:
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:
GitHub membership;
new provider-identity schema;
collaboration and linking surface;
channel lifecycle.
PR #131 remains useful design research, but a successor should start from
current
mainand should not reuse its subsystem as an implementation base.Product behavior
For each explicitly configured public GitHub repository:
Provider payloads cannot create a channel, choose its kind, or change its
visibility.
authoritative GitHub state.
such as
github:<repository-id>:pr:<number>:root.draft/open/merged/closed state, review/check summary, and the canonical
GitHub link. Later reconciliation edits that same bot-authored root.
Ordinary human and agent discussion remains untouched in the same thread.
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
mainappears to provide the required core surface already: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 channelsand repository allowlist, and explicit repository-to-channel mapping.
fail before any ClickClack write.
and update for opened/synchronize events.
ClickClack proof included in the same PR.
PR B —
feat(github): reconcile PR lifecycle in existing workroomshistory.
PR C —
feat(github): summarize reviews and checks in PR workroomschurn.
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
channel configuration.
and checks as required by the delivered phase.
pull_requestfirst, followed by boundedpull_request_reviewandcheck_suitehandling in the staged sequence above.GitHub delivery IDs are diagnostic correlation values, not identity.
logs.
Non-goals
payloads.
destination authorization contract.
closing, or otherwise modifying a PR.
service, or new ClickClack room type.
the closed feat: add GitHub project rooms #131 implementation.
Security and privacy contract
A webhook cannot select or downgrade visibility.
public before writing any provider-derived content.
comparison before JSON processing.
installations fail before ClickClack writes.
are never posted into channels or emitted in logs.
are normalized or rejected, and links are accepted only from validated GitHub
URLs.
ClickClack reaction or message into repository authority.
Acceptance criteria
channel and exposes one linkable thread workroom.
and acknowledgement do not create a second root or semantic reply.
workroom without replacing its human discussion.
card backward.
route, and thread history.
workspace/channel bindings, revoked credentials, and missing ClickClack scope
fail without leaking provider content.
server database or plugin access.
runtime proof for the behavior it introduces.
Related work disposition
no longer required for this thread-first MVP.
PR feat: reconcile externally managed channels #126 is not a dependency.
dependency because thread routes already resolve lazily.
not MVP blockers.
routes are complementary existing primitives.
authorization model are not the foundation of this implementation.
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 inthis repository, the same staged contract can move without changing the product
or security boundary.