Skip to content

Team Collaboration

coo1white edited this page Jun 8, 2026 · 1 revision

Team Collaboration (v0.1.32)

The human-decision layer: a host-attested actor plus append-only approvals/comments/handoffs, with a review gate that STACKS ON the verifier gate. Shipped in v0.1.32. Repo doc: docs/team-collaboration.7.md.

Before v0.1.32 there was no review/approval/comment/handoff/identity concept anywhere in CW. The foundations already existed — trust-audit recorded every decision with an actor, candidate selection carried selectedBy, role policies existed, and commits were verifier-gated. This release adds the human-decision layer ON TOP of those mechanisms, without changing them and without taking ownership of source truth.

The design mantra for this layer:

Attested, never authenticated.
Append-only, never overwritten.
The review gate stacks; it never bypasses.
Fail closed on authority and quorum.
Policy is data; the kernel is mechanism.

The Borrowed Idea: Provenance-Linked Records, Not a Mutated Record

CW models the human decision the way git models history: a correction is a NEW record that supersedes an earlier one, the superseded record stays in the log, and the original artifact is never edited in place. "Who approved what" is a provenance LINK, not a field overwrite — exactly as candidate.selection records both a CandidateSelection and an audit event rather than stamping the candidate. The mechanism is the verifier-gated commit and the trust-audit log; the policy (required approvals, authorized roles, self-approval) is data layered on top.

attested actor -> append-only record -> audit link -> derived review state -> stacked gate

Mechanism vs Policy

  • Kernel (mechanism): the verifier-gated commit and the append-only collaboration log. resolveCommitGate accepts a commit only when a verified verifier node, a scored+verified candidate, and a complete acceptance rationale are present.
  • Policy (data): requiredApprovals, authorizedRoles, allowSelfApproval, requireAttestedActor, appliesTo. Written by review policy to run.collaboration.policy, enforced — never owned — by the kernel.

The per-run .cw/runs/<id>/state.json is the SINGLE source of truth; collaboration records are an APPEND-ONLY log, never mutated in place. There is no side channel — the collaboration IS the durable, inspectable state, consistent with CW's no-hidden-dashboard-database rule.

1. Identity Is Attested, Not Authenticated

An Actor is host-attested provenance, not an authenticated principal. CW records WHO acted; it does not verify a password, a token, or a signature — that is a host trust-boundary concern. normalizeActor maps an actor input to one of three provenances: host-attested (the host vouched, --attested), operator-recorded (supplied unverified), or unattributed (no identity supplied). An absent identity becomes the explicit unattributed actor — { kind: "unattributed", id: "unattributed", attested: false } — never a fabricated one. Spoofing is recorded honestly as whatever provenance the host attested, not hidden. This extends the existing trust-audit actor string and the v0.1.29/v0.1.31 attestation pattern.

2. Approvals and Rejections Are Append-Only and Provenance-Linked

approve and reject append an ApprovalRecord to run.collaboration.approvals. Each record carries the actor, the decision, the durable target it attaches to, an optional rationale, the role the actor claims, and auditEventIds linking it to a collaboration.approval/collaboration.rejection event in the trust-audit log. The approved artifact (candidate/commit/selection) is NEVER edited in place. A correction is a NEW record carrying supersedes (git-style); the superseded record stays in the log, no longer counts, and the original is unchanged. A target is one of run | task | candidate | selection | commit | node, and "who approved which candidate/commit" is answered by filtering the append-only records by target.

3. The Review Gate Stacks on the Verifier Gate

The verifier-gated commit is the MECHANISM. A review gate is POLICY layered on top. reviewGateErrors runs INSIDE resolveCommitGate, AFTER the verifier checks, and can only ADD errors — required approvals from authorized roles — never remove the verifier's. The same call guards candidate selection in selectCandidate. Data flow for a gated commit:

  1. resolveCommitGate resolves the candidate/selection and runs every verifier check; if any fail the commit is blocked as before.
  2. If a ReviewGatePolicy applies to commit (or selection), reviewGateErrors derives the review state over the approvals targeting the commit AND its underlying selection/candidate (you approve the candidate; the commit honors it).
  3. If the review state is not approved, a single review-gate-missing-approvals StateNodeError is appended, listing exactly which approvals are missing; commitState throws CommitGateError, recorded as append-only feedback.
  4. Only when BOTH gates pass is the commit written — stamped with a CommitReviewProvenance recording WHO approved the very artifact that shipped.

Because the review errors are appended after the verifier errors and never replace them, an approval can never turn an unverified result into a committed one: an approved-but-unverified candidate is still blocked by the verifier gate.

4. Fail Closed on Authority and Quorum

deriveReviewState is a pure, deterministic projection of the append-only records plus a policy. It counts ONLY approvals that are, all at once: from an attested identity (when requireAttestedActor), from a role in authorizedRoles (or *), and not a self-approval (when allowSelfApproval is false; "self" is the candidate's producing worker and its selector). Distinct counted approvers must reach requiredApprovals. Anything short is not auto-passed:

  • approved — requirement met (or the target is not gated)
  • pending — gated, no blocking reject, fewer than required counted approvals
  • blocked — recorded approvals exist but none count (authority/self)
  • unattributed — the only recorded approvals are from unattributed actors
  • rejected — an authorized, attested reject is a blocking veto

Every disqualified approval is surfaced with its reason (unattributed, unauthorized-role, self-approval, superseded), so a reader can audit why an approval did not count. A target requiring N approvals with fewer recorded is BLOCKED, and the block records exactly what is missing.

5. Comments and Handoffs Are State, Not Chat

A comment appends a CommentRecord to a durable target with an actor, a thread id, and an audit link; threads are ordered by createdAt and never edited in place. A handoff appends a HandoffRecord — an explicit ownership transfer with a from-actor, a to-actor, and a reason — and the current owner of a run/task is DERIVED from the latest handoff, never an overwritten field.

6. Policy as Data, Kept Out of the Kernel

review policy <run-id> writes a ReviewGatePolicy to run.collaboration.policy: requiredApprovals (0 = no gate), authorizedRoles (* = any), allowSelfApproval, requireAttestedActor, and appliesTo (target kinds). The default — absent policy or requiredApprovals: 0 — requires no approvals, so pre-v0.1.32 runs and any run without a policy behave exactly as before. The policy is data; the kernel only enforces the mechanism.

7. One Source, Every Surface

Each collaboration verb is declared once in src/capability-registry.ts, so cw <cmd> --json is schema-identical to cw_<cmd> and passes the parity gate. The read-only review status and comment list are byte-for-byte identical across CLI and MCP (the payload-identity probe strips only ISO timestamps; the only now-derived field in a review report is generatedAt). The v0.1.30 Workbench renders the review timeline and per-target approval state read-only as a sixth panel, embedding the review status payload verbatim. The v0.1.31 metrics report adds derived approval-rate, time-to-approval, handoff-count, and reviewer-count, all from recorded timestamps — deterministic over a fixed snapshot.

Backward Compatible by Construction

Every collaboration field is additive and optional. Old run state loads unchanged, and a run with no policy (or requiredApprovals: 0) behaves exactly as before — the review gate adds no errors. Backward compatibility holds the same way it does in Execution Backends: the human-decision layer lives in run.collaboration, never in the result envelope or the verifier mechanism it stacks on.

Commands

cw review policy <run-id> --requiredApprovals N --authorizedRoles a,b --appliesTo commit,selection [--allowSelfApproval] [--requireAttestedActor]
cw approve <kind> <run-id> <target-id> --actor <id> --role <role> --attested [--rationale <text>]
cw reject  <kind> <run-id> <target-id> --actor <id> --role <role> --attested [--rationale <text>]
cw comment add <kind> <run-id> <target-id> --actor <id> --body <text>
cw comment list <run-id> [--json]
cw handoff <kind> <run-id> [target-id] --from <id> --to <id> --reason <text>
cw review status <run-id> [--json]

<kind> is one of run | task | candidate | selection | commit | node. Approve a candidate (or selection), then commit --candidate/--selection; the commit gate honors the candidate's approvals and records who approved the shipped commit.

Why It Matters

CW is the base system; workflow apps are userland. The human decision becomes durable, attested, append-only state — never a hidden dashboard, never a bypass of the verifier gate. A reviewer's approval is recorded honestly with its provenance, can be superseded but never erased, and can only ever ADD a gate on top of mechanical verification — so "a human signed off" can never overrule "the verifier failed."

See Also

Clone this wiki locally