blocks

Blocks interaction

Low-level primitives for talking to Blocks: resolve a workspace, open or read a session, collect GitHub review evidence, classify status, and wait with a visible bound. This is the plumbing other skills call — for the review-until-clean loop on a pull request use request-blocks-review.

When it fires

Symptoms: ask Blocks, start a Blocks session, what is Blocks doing, await the Blocks response

Loaded only when you name it. It never volunteers.

Install it

npx skills add crissmoldovan/agent-skills --skill blocks

Blocks REST Sessions API or Blocks GitHub integration; direct sessions require a workspace-scoped API key, and GitHub status requires authenticated gh.

What to say to make it fire

These are the skill’s own published asks, word for word. You do not type a command — you say one of these, and the agent recognises it.

  • Resolve the Blocks workspace for this repository. If it is not uniquely known, ask me for a workspace URL or ID. Start a session and visibly await its final message for up to ten minutes.
  • Get Blocks status for PR 42 against this request baseline. Include comments, reviews, all inline comments, and distinguish acknowledgement from completion.

The skill, in full

Blocks interaction

Generic Blocks interaction primitives for other skills and workflows. This skill owns workspace resolution, REST sessions, GitHub evidence collection, status classification, and visible bounded waits. It does not own code-review policy, fix findings, decide severity policy, or loop a PR to green; use the public request-blocks-review skill for that workflow.

When to Use

  • “Ask Blocks,” “start a Blocks session,” “what is Blocks doing?”, or “await the Blocks response.”
  • Another skill needs Blocks workspace/session/status/wait primitives.
  • A caller needs to distinguish acknowledgement from terminal GitHub evidence.

Workspace Resolution

Blocks REST credentials are workspace-scoped. Before a direct REST call:

  1. Prefer a configured repository → workspace mapping.

  2. Otherwise parse the workspace UUID after /app/ from a Blocks workspace, settings, or session URL such as:

    https://blocks.team/app/<workspace-id>/settings/api-keys
    
  3. If neither yields exactly one workspace, ask the user to confirm a workspace URL or ID. Never choose whichever installed key happens to work.

  4. Use a neutral local profile name. Credentials resolve from BLOCKS_API_KEY_<PROFILE> or, on macOS, Keychain service blocks-api-key-<profile>. Never print keys or pass them as CLI arguments.

Direct REST Sessions

The documented base is https://api.blocks.team/rest/v1:

Action Endpoint/helper
Create POST /sessions
Inspect GET /sessions/{id}
Follow up POST /sessions/{id}/messages
Wait Poll returned _links.final_message.href

A final-message page with empty items is pending. A non-empty assistant final_message is terminal for that turn. Blocks documents no session status enum, blocking wait endpoint, completion webhook, or GitHub-comment-to-session mapping. Treat returned _links URLs as opaque and validate their official origin/path/filter contract before use.

node skills/blocks/scripts/blocks-session-cli.mjs create \
  --profile <workspace-profile> --agent claude --message "..." \
  --wait --timeout 600

GitHub Status

GitHub-originated review evidence remains GitHub-authoritative:

  • top-level PR comments;
  • formal reviews;
  • all paginated inline review comments;
  • check runs on the head commit — a completed Blocks PR Review check;
  • PR open/closed state;
  • Blocks dashboard links included in comments.

Read every channel, not the one you expect. Which one a finished review arrives on depends on how the integration is configured: one repository gets a summary comment naming the head, another gets only help text and reports the verdict as a check. Reading comments alone leaves a completed, clean, zero-finding review looking like reviewing until the wait times out. A completed check means the review finished, never that it was clean — what it found is still decided by the inline comments, or a false clean would ride in on a green check.

Always compare against a baseline containing timestamp and stable IDs. Help text, eyes reactions, queue messages, and “taking a look” are nonterminal. Return one of requested, reviewing, clean, findings, or pr_closed.

Blocks states completion in a top-level comment as often as in a formal review. A post-baseline comment that names its own completion is terminal even when no review was submitted; its wording then chooses the state — outstanding findings mean findings, a fixed or empty finding list means clean. Acknowledgement wording still wins: a comment that merely quotes the words a finished review would use remains nonterminal, and so does a partial pass that makes the claim and then withdraws it (“reviewed up to packages/guards/ … I need another pass”).

Completion is a past-tense claim, not a phrase. Do not match a fixed list of openers. This classifier once demanded the literal “reviewed PR” or “review complete”, and a real verdict opening “Reviewed `67b6d36` and its documentation-only diff” matched nothing — a caller waited forty minutes for a verdict that had arrived in four seconds. Recognise the claim, then let two things veto it: courtesy wording, and an admission of unfinished work.

clean must be earned; ambiguity means findings. A verdict is clean only when it states its own emptiness — no/zero findings, Findings: none, a table row reporting 0, LGTM with nothing contrasted against it. A verdict that merely fails to mention findings is not clean. The three ways to be wrong do not cost the same: a false findings costs a reader the seconds it takes to open the comment, a false nonterminal costs a timeout, and a false clean accepts an unreviewed or unclean head — the only one of the three that merges. Bias every uncertain case away from clean.

Never decide from a bare mention. “No new severity ≥7 findings” contains the word findings; so does a /findings path segment in a dashboard URL, and so does a quoted verdict from an earlier round that the next sentence retracts. Scope every mention — negated, resolved, quoted, or outstanding — instead of testing whether the noun appears. Watch for the inversion too: “zero of these findings have been addressed” is the strongest possible findings statement and reads, to a naive stripper, like the weakest. A corrective negation scopes over its second half only. In “this was an environment setup issue, not a test failure” the not clears “a test failure”; “an environment setup issue” is set aside only because it also names the reviewer's own sandbox — “this is not a test failure but a real bug” still reports the bug.

node skills/blocks/scripts/blocks-review-cli.mjs status \
  --repo <owner/repo> --pr <N> --requested-at <ISO>

A clean verdict is not acceptance. status also reports whether the verdict can actually be acted on, and refuses for a named reason when it cannot. Three things must hold at once: the state is clean, the verdict covers the head under consideration, and CI succeeded on that same commit.

Both halves were learned from real failures. A review names the commit it read ("Reviewed PR #29 at a0eef8b"); every push moves the branch, and a verdict for the previous head says nothing about the current one while reading exactly like one that does. Separately, gh pr checks reported pass while the run underneath belonged to the previous head, because the new run had not registered yet — so key CI on the sha, never on the check name. A run that has not finished is not a pass.

A verdict that names no commit is dated against the head rather than refused: one posted after the head was committed cannot have read an earlier one. Refusing it for its wording would be the same mistake one layer up.

The latest verdict decides, and acceptance asks that same verdict. Within the --requested-at window the most recent finished verdict chooses the state — an older clean round never outvotes a newer one with findings — and head coverage is read from that verdict alone, never from whichever comment happens to name the head. When they came from different comments, a stale clean was accepted for a head it never read (cue-ui#94). A latest verdict naming an older head is refused even if an earlier one named the current head; re-request the review. Help text and acknowledgements are never a verdict, however recent.

Visible Bounded Wait

Mirror Trigger.dev's active-wait contract, not its transport:

  • keep one tracked operation active;
  • surface waiting progress;
  • combine caller cancellation with a hard deadline;
  • stop on terminal evidence;
  • return latest current state on timeout;
  • propagate non-timeout failures.

Trigger.dev can subscribe to a run stream. Blocks REST and GitHub review status use documented polling, so poll conservatively rather than busy-waiting.

Usage Examples

Resolve the Blocks workspace for this repository. If it is not uniquely known,
ask me for a workspace URL or ID. Start a session and visibly await its final
message for up to ten minutes.
Get Blocks status for PR 42 against this request baseline. Include comments,
reviews, all inline comments, and distinguish acknowledgement from completion.

Pitfalls

  • Workspace key success does not prove it is the intended workspace.
  • Dashboard URLs are not REST links; use only returned _links for API calls.
  • Never send a follow-up merely to check status; follow-ups can interrupt work.
  • Never silently hide a wait by redirecting all progress to a file.
  • A timeout is nonterminal and must return current state explicitly.

Verification

  • Workspace resolved uniquely or the user was asked to confirm URL/ID.
  • Credential profile is neutral and workspace-scoped.
  • Returned REST links passed strict official URL validation.
  • GitHub evidence uses timestamp plus stable-ID baseline and pagination.
  • Wait is visible, cancellation-aware, and deadline-bounded.
  • Timeout does not claim completion or failure.