@bompus/house-rules 0.13.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,22 @@
1
1
  <h1 align="center">
2
2
  <picture>
3
3
  <source media="(prefers-color-scheme: dark)" srcset="docs/assets/banner-dark.png">
4
- <img src="docs/assets/banner-light.png" alt="house-rules" width="100%">
4
+ <img src="docs/assets/banner-light.png" alt="house-rules: Working rules and skills for AI coding agents" width="100%">
5
5
  </picture>
6
6
  </h1>
7
7
 
8
8
  <p align="center">
9
9
  <a href="https://github.com/bompus/house-rules/actions/workflows/check.yml"><img src="https://github.com/bompus/house-rules/actions/workflows/check.yml/badge.svg" alt="CI status"></a>
10
- <a href="https://github.com/bompus/house-rules/releases/latest"><img src="https://img.shields.io/github/v/release/bompus/house-rules" alt="Latest release"></a>
11
- <a href="LICENSE"><img src="https://img.shields.io/github/license/bompus/house-rules" alt="MIT license"></a>
10
+ <a href="https://github.com/bompus/house-rules/releases/latest"><img src="https://badgen.net/github/release/bompus/house-rules" alt="Latest GitHub release"></a>
11
+ <a href="https://www.npmjs.com/package/@bompus/house-rules"><img src="https://badgen.net/npm/v/@bompus/house-rules" alt="Latest npm version"></a>
12
+ <a href="LICENSE"><img src="https://badgen.net/badge/license/MIT/blue" alt="MIT license"></a>
13
+ </p>
14
+
15
+ <p align="center">
16
+ <a href="rules/core.md">Core rules</a>&nbsp;·
17
+ <a href="#modifiers">Opt-in modifiers</a>&nbsp;·
18
+ <a href="#writing-your-own-sections">Your own layer</a>&nbsp;·
19
+ <a href="examples/project/AGENTS.md">Each repo's AGENTS.md</a>
12
20
  </p>
13
21
 
14
22
  A practical starting point for ordinary development with AI coding agents.
@@ -291,7 +299,7 @@ changing the configuration.
291
299
  | `hr-ordering-tests` | Enumerate event orderings through the real code to find race bugs. |
292
300
  | `hr-plain-prose` | Make text people read plain and specific. |
293
301
  | `hr-progress-report` | Report current task activity, milestone completion and remaining wall-clock time. |
294
- | `hr-pr-followup` | Address conflicts, feedback and failing checks for a PR; honor landing authority; CodeRabbit review gate. |
302
+ | `hr-pr-followup` | Address conflicts, feedback and failing checks for a PR; landing authority; CodeRabbit and cubic gate. |
295
303
  | `hr-read-reddit` | Read Reddit feed output while supported; RSS support ends November 13, 2026. Partial web-search fallback. |
296
304
  | `hr-read-x-links` | Read the full content of X posts. |
297
305
  | `hr-research-shared-links` | Research a shared link or repository in depth, compare it with your setup and rate each idea. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bompus/house-rules",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "Composable working rules and skills for AI coding agents",
5
5
  "homepage": "https://github.com/bompus/house-rules#readme",
6
6
  "license": "MIT",
@@ -8,6 +8,38 @@ Give jobs finite timeouts. Cancel an older pull-request run only when a newer
8
8
  run for the same pull request covers the same required checks; preserve required
9
9
  default-branch and release evidence.
10
10
 
11
+ ### Cancel superseded pull-request runs
12
+
13
+ Key the `concurrency` group on the workflow and the pull request number, and
14
+ cancel only for pull-request events:
15
+
16
+ ```yaml
17
+ concurrency:
18
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
19
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
20
+ ```
21
+
22
+ - Concurrency groups are shared by every workflow in the repository, so keep a
23
+ value unique to the workflow in the key. `github.workflow` is the workflow's
24
+ name, so it is unique only while workflow names are; otherwise use a fixed
25
+ prefix. Without a unique value, one workflow's run can cancel another's.
26
+ - Keep the pull-request key stable across pushes: never a commit SHA or run ID
27
+ for a pull-request run, which differs on every push, so nothing is cancelled.
28
+ A workflow that runs only on pull requests can use `cancel-in-progress: true`.
29
+ A reusable workflow inherits the caller's `github.workflow`, so give caller and
30
+ callee different keys.
31
+ - Cancellation needs a newer run in the same group. When a path filter means a
32
+ push starts no run, the older run finishes. Add a cleanup workflow only when
33
+ run history shows older runs finishing after pushes that started no
34
+ replacement; it must not check out pull-request code.
35
+ - To skip checks on draft pull requests, add `github.event.pull_request.draft != true`
36
+ to each check job's `if`, including reusable-workflow callers and aggregate
37
+ jobs. A job guard stops a runner being allocated; a step guard does not. Add
38
+ `ready_for_review` and `converted_to_draft` to the `pull_request` types, so
39
+ checks start when review begins and a replacement run can cancel old work.
40
+ Use this only when the repository's policy says draft pull requests do not
41
+ need these checks.
42
+
11
43
  ### Choose the concurrency boundary
12
44
 
13
45
  Independent jobs can run on separate runners. A job's steps normally run in
@@ -15,6 +15,16 @@ or readiness condition in the task plan. Backlog items do not authorize
15
15
  implementation or landing. Do not hold a ready batch for unrelated, unapproved
16
16
  or indefinite work.
17
17
 
18
+ Before any decision to publish, check every package for commits past its latest
19
+ release tag, and recommend a new release first when there are any. This covers
20
+ public and private packages alike, and a republish of an existing tag, which
21
+ ships the tagged content without the later commits. State how many commits are
22
+ unreleased and what they change, and name the version the repository's scheme
23
+ gives the combined changes. A package that has never been tagged has no
24
+ baseline, so say that and treat the publish as its first release. The
25
+ recommendation is not permission to tag, publish or announce, and it does not
26
+ hold a ready batch for unrelated work.
27
+
18
28
  Before any release or public announcement, audit every public-facing
19
29
  material against the intended revision or artifact and current verified state:
20
30
  the README and package READMEs, comparisons, performance metrics, graphics and
@@ -19,8 +19,8 @@ the review body too: findings outside the diff arrive there, not as threads.
19
19
  Let an enabled automatic review start; do not comment to request one. When none
20
20
  starts, check the integration's status instead of tagging the bot again. A bot's
21
21
  config file in the repository does not show that it is enabled, and an existing
22
- check on the head must report completion before you merge (for CodeRabbit, a
23
- successful status whose description starts with "Review completed").
22
+ check on the head must report completion before you merge. A skipped or
23
+ cancelled review is not a completed one.
24
24
 
25
25
  During integration, never switch, reset or remove the session's own checkout: no
26
26
  `checkout` or `switch` to another ref, no `reset` or branch force-move, and no
@@ -55,8 +55,8 @@ Name the exact revision, failed or unfinished gates and unresolved decisions.
55
55
  Clean CI alone is not evidence that every review finding was handled.
56
56
 
57
57
  When the repository or user selects these GitHub helper policies, use
58
- [the CodeRabbit review gate and one-thread resolver](references/review-helpers.md).
59
- Its review-status checks cover CodeRabbit only; its thread check covers any
58
+ [the CodeRabbit and cubic review gate and one-thread resolver](references/review-helpers.md).
59
+ Its review-status checks cover CodeRabbit and cubic only; its thread check covers any
60
60
  reviewer and its review-body check any bot. It does not replace other required
61
61
  checks or the caller's judgment. The same reference describes the landing helper scripts (`merge-pr.sh`,
62
62
  `verify-landed.sh`, `check-landing.py`).
@@ -14,12 +14,26 @@ bun scripts/check-pr-review.ts 123 --repo OWNER/REPO
14
14
 
15
15
  The gate exits 0 when its selected feedback checks pass, 1 for open feedback,
16
16
  and 2 when coverage cannot be verified. It checks unresolved threads, pending
17
- CodeRabbit review, skipped review statuses and a missing check when the head
18
- contains `.coderabbit.yaml`. It also checks bot review bodies with outside-diff,
19
- nitpick or duplicate findings. A completed CodeRabbit CheckRun must conclude SUCCESS;
20
- failed, cancelled, skipped or missing conclusions block the gate. A CodeRabbit status also requires SUCCESS
21
- and a description starting with "Review completed". It does not
22
- check every CI result or authorize landing.
17
+ CodeRabbit or cubic review, skipped review statuses and a missing check when
18
+ the head contains `.coderabbit.yaml`. It also checks bot review bodies with
19
+ outside-diff, nitpick or duplicate findings. A completed CodeRabbit CheckRun
20
+ must conclude SUCCESS; failed, cancelled, skipped or missing conclusions block
21
+ the gate. A CodeRabbit status also requires SUCCESS and a description starting
22
+ with "Review completed"; that is how the gate tests the general squash rule that an
23
+ existing review check must report completion. It does not check every CI result or
24
+ authorize landing.
25
+
26
+ The check named exactly "cubic · AI code reviewer" on the head commit is held to the
27
+ same rule: while it runs, the gate refuses, and a completed one must conclude
28
+ SUCCESS. A NEUTRAL or skipped conclusion means cubic did not review the head
29
+ ("Incremental review could not start", "Automatic AI review not started after
30
+ branch rewrite", "AI review line limit reached"), so the gate refuses it too.
31
+ That pull request stays blocked until someone comments `@cubic review` or the
32
+ limit clears, and the refusal says so. A head commit with no cubic check is not blocked,
33
+ because cubic's `cubic.yaml` is optional and its absence does not show that cubic is
34
+ expected. `--wait` does not wait for a cubic check that has not posted yet.
35
+ In a repository that uses cubic, confirm right after a push that its check has appeared
36
+ before landing.
23
37
 
24
38
  If a review is missing or incomplete, inspect the integration and the repository's
25
39
  review policy. The helper does not determine automatic-review enablement or
@@ -42,7 +56,7 @@ judges whether the explanation answers the findings.
42
56
  Connections are paginated. Partial responses, malformed fields, repeated
43
57
  cursors and head changes fail closed. A final head read does not make review
44
58
  feedback an atomic snapshot. `--wait --timeout 30` polls only while CodeRabbit
45
- runs, or briefly while its expected check has not appeared.
59
+ or cubic runs, or briefly while CodeRabbit's expected check has not appeared.
46
60
 
47
61
  ## Resolve one review thread
48
62
 
@@ -1,16 +1,22 @@
1
1
  #!/usr/bin/env bun
2
2
  // Refuses to land a pull request while review feedback is open: an unresolved review thread, a
3
- // review bot (CodeRabbit) whose check is still running, or a bot review whose body carries findings
3
+ // review bot (CodeRabbit or cubic) whose check is still running, or a bot review whose body carries findings
4
4
  // GitHub cannot show as threads ("Outside diff range comments", "Nitpick comments", "Duplicate comments") with no human
5
5
  // explicit acknowledgement for that review and the current head. Run only when the repository
6
6
  // selects this gate. It also refuses when CodeRabbit's status on the
7
7
  // head commit says anything but "Review completed": CodeRabbit marks a skipped review (for example
8
8
  // on a non-default base) as success with "Review skipped: …". When the head commit has
9
- // `.coderabbit.yaml` but no CodeRabbit check, it refuses too. Missing or incomplete reviews
9
+ // `.coderabbit.yaml` but no CodeRabbit check, it refuses too. A cubic check ("cubic · AI code
10
+ // reviewer") on the head commit must also complete with SUCCESS: a running one refuses, and so does
11
+ // any other conclusion, NEUTRAL and skipped included, because NEUTRAL means cubic did not review the
12
+ // head ("Incremental review could not start", "AI review line limit reached"). That PR stays blocked
13
+ // until someone comments `@cubic review` or the limit clears. A head commit with no cubic check is
14
+ // not blocked, since cubic's `cubic.yaml` is optional and its absence does not show that cubic
15
+ // is expected, and --wait does not wait for one to appear. Missing or incomplete reviews
10
16
  // require inspection of the review integration and the repository's selected review policy.
11
17
  // bun scripts/check-pr-review.ts <pr> [--repo <owner>/<name>] [--wait [--timeout <minutes>]]
12
18
  // Exit 1 while feedback is open, 2 when gh fails. Without --repo, gh resolves the repository from
13
- // the current checkout. --wait polls every 30 s while CodeRabbit's review is still running, and for
19
+ // the current checkout. --wait polls every 30 s while CodeRabbit's or cubic's review is still running, and for
14
20
  // up to 3 minutes while the head commit has no CodeRabbit check yet (CodeRabbit posts it a little
15
21
  // after a push), then reports as usual; after --timeout minutes (default 30) it reports what is
16
22
  // still open.
@@ -30,13 +36,17 @@
30
36
  import { spawnSync } from "node:child_process";
31
37
  import { parseArgs } from "node:util";
32
38
 
33
- const REVIEW_BOTS = /coderabbit/i;
39
+ const CODERABBIT = /coderabbit/i;
40
+ const CUBIC = /^cubic · AI code reviewer$/i;
41
+ const isReviewBot = (name: string) => CODERABBIT.test(name) || CUBIC.test(name);
34
42
  const REVIEW_BOT_CONFIG = ".coderabbit.yaml";
35
43
  const BODY_FINDINGS = /Outside diff range comments|Nitpick comments|Duplicate comments/;
36
44
  const REVIEWED = /^Review completed/;
37
45
  const FOLLOW_UP = "check the review integration and repository review policy";
38
46
  const RUNNING = "review still running: ";
39
47
  const NO_CHECK = "no CodeRabbit check on the head commit";
48
+ // A neutral cubic result means it did not review the head; a person has to ask again.
49
+ const CUBIC_RETRY = "comment `@cubic review` or wait for its limit to clear";
40
50
  const POLL_MS = 30_000;
41
51
  const NO_CHECK_GRACE_MS = 180_000;
42
52
 
@@ -364,25 +374,26 @@ export function openFeedback(pr: ReviewState): string[] {
364
374
  const head = pr.commits.nodes[0]?.commit;
365
375
  const contexts = head?.statusCheckRollup?.contexts.nodes ?? [];
366
376
  const hasConfig = head?.tree.entries.some((e) => e.name === REVIEW_BOT_CONFIG) ?? false;
367
- if (hasConfig && !contexts.some((ctx) => REVIEW_BOTS.test(ctx.context ?? ctx.name ?? ""))) {
377
+ if (hasConfig && !contexts.some((ctx) => CODERABBIT.test(ctx.context ?? ctx.name ?? ""))) {
368
378
  open.push(`${NO_CHECK}: ${FOLLOW_UP}`);
369
379
  }
370
380
  for (const ctx of contexts) {
371
381
  const name = ctx.context ?? ctx.name ?? "";
372
382
  const running =
373
383
  ctx.state === "PENDING" || (ctx.status !== undefined && ctx.status !== "COMPLETED");
374
- if (!REVIEW_BOTS.test(name)) {
384
+ if (!isReviewBot(name)) {
375
385
  continue;
376
386
  }
377
387
  if (running) {
378
388
  open.push(`${RUNNING}${name}`);
379
389
  } else if (ctx.name !== undefined && ctx.conclusion !== "SUCCESS") {
390
+ const retry = CUBIC.test(name) ? `; ${CUBIC_RETRY}` : "";
380
391
  open.push(
381
- `${name} review concluded ${ctx.conclusion ?? "(missing conclusion)"}: ${FOLLOW_UP}`,
392
+ `${name} review concluded ${ctx.conclusion ?? "(missing conclusion)"}: ${FOLLOW_UP}${retry}`,
382
393
  );
383
394
  } else if (
384
395
  ctx.context !== undefined &&
385
- (ctx.state !== "SUCCESS" || !REVIEWED.test(ctx.description ?? ""))
396
+ (ctx.state !== "SUCCESS" || (CODERABBIT.test(name) && !REVIEWED.test(ctx.description ?? "")))
386
397
  ) {
387
398
  open.push(
388
399
  `${name} did not review the head commit ("${ctx.description ?? ""}"): ${FOLLOW_UP}`,