Skip to content

chore(pai): make the PR review procedure shared and executable - #143

Draft
Soushi888 wants to merge 10 commits into
devfrom
chore/shared-review-workflow
Draft

Soushi888 wants to merge 10 commits into
devfrom
chore/shared-review-workflow

Conversation

@Soushi888

@Soushi888 Soushi888 commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

SoushAI analysis. Drafted by Soushi's AI assistant, reviewed and posted by @Soushi888.

Intent

Our review workflow works, but its method is not legible to anyone but the person running it. That is a fair objection independent of trust: a step that gates a merge should be something either maintainer can read and run, not a judgement whose procedure lives in one person's own tooling.

This makes the review procedure a repo artifact, adds the conflict-resolution rule we were missing, and cleans up pai/ so shared content and per-tool packaging stop being tangled.

Changes

1. A shared review procedure

pai/shared/skills/nondominium-review/ defines the order the six REVIEW.md areas get walked, the CONTRIBUTING.md merge criteria, the conflict-resolution check, and a fixed verdict block (APPROVE / CHANGES REQUESTED / BLOCKED) with per-area results, so two reviewers produce comparable output. It restates nothing: REVIEW.md stays the single home for what gets flagged.

Invocable as /nondominium-review on every tool we use, with nothing installed from anywhere else:

Tool How
Cursor Type /, pick nondominium-review. Skills are Cursor's slash-command mechanism; the skill lands in .cursor/skills/ and .agents/skills/.
Claude Code /nondominium-review [PR], from .claude/commands/.
Any Agent Skills client The skill in .agents/skills/ is the command.

One rule in it has already cost us something: no approval while the pipeline is running. Our CI stages are chained, so early green says nothing about e2e. An approval posted on partial green had to be retracted on #132. That is now written down rather than remembered.

2. Merge conflict resolution policy (CONTRIBUTING.md)

The governing question is which side was trying to change meaning, and which was only trying to restate it. Presentational changes yield to semantic ones, always. When both sides are semantic it is a decision between two authors, not a merge mechanic. A resolution is backed by evidence (the Sweettest target passes, or the resolved doc is re-read against its REQ-*), not asserted.

Names what counts as fundamental here: REQ-* meaning, enforced validation and permitted state transitions, Valueflows field names, NDO layer boundaries.

The sharpest case is in the hotspot table: #[hdk_entry_types] derives type indices from declaration order, so tidying an integrity enum alphabetically while resolving invalidates every entry already on the DHT. The cosmetically nicer resolution is the one that breaks the data.

3. pai/ split into shared sources and harness adapters

pai/shared/ holds everything describing the project: conventions.md, rules/ (was cursor-rules/, whose content was never Cursor-specific), and skills/ (was claude/skills/, though every skill already fanned out to all three trees). pai/harnesses/ holds only what one tool needs of its own, today Claude Code's settings.json and commands/. Cursor gets no directory because its adapter is a pure transform in nix/cursor-pai.nix.

The test for where a file belongs: would its content change if we dropped support for a tool? If no, it is shared.

4. Supporting changes

  • .github/workflows/lint.yml reports rustfmt status on every PR. Non-blocking on purpose, explained below.
  • bun run pai:sync copies pai/shared/skills/ from the working tree for the inner loop. Nix only sees git-tracked files, so a new skill silently does not materialize until committed, and nothing errors. Nix stays the authority; this is for iteration.
  • pai/README.md rewritten for the new structure. It also removes a description of a LoadProjectDocs.hook.ts SessionStart hook: a full section, two diagrams and a file-map entry for a file that does not exist and that settings.json never wired. The @ mentions in .rules are the whole mechanism.
  • REVIEW.md §4 correction. It told reviewers to check for await_consistency(&[&cell_a, &cell_b]). That does not compile: 0.6.0 needs the timeout wrapper and the array by value.
  • CONTRIBUTING.md CI section corrected. It said tests were "non-blocking while the test suite is being stabilized", which stopped being true once sweettest and e2e became chained required jobs.

Decisions

Option Rejected because
Blocking cargo fmt --check 37 of 62 Rust files are unformatted, several touched by every open branch. A blocking gate fails on day one and forces a workspace reformat that conflicts with in-flight work. Advisory now; flipping it is uncommenting one line, once a dedicated formatting PR lands with nothing else open.
continue-on-error: true on the lint job Keeps the workflow green but still marks the job failed, so the PR shows a red X and gh pr checks reports a failure. Our own review procedure treats a failing check as a merge blocker, so the advisory job would have blocked reviews it was never meant to gate. The step now always exits 0.
Adding cargo clippy here Never run against this workspace. Adding a gate whose output nobody has seen is how CI starts getting ignored. Its own PR, results read first.
Copying the review checks into the skill Two copies of a checklist drift apart within a month. Procedure in the skill, content in REVIEW.md.
A separate Cursor command file Unnecessary. Cursor has no .cursor/commands/; skills are its slash-command surface, and we already distribute there.
Leaving pai/human_ai_collaboration.md at the pai/ root It arrived from #132 and is harness-agnostic reference material, so it classifies as shared by the same test as everything else. Moved into pai/shared/ with no edits to the document. @TiberiusB, say the word if you want it elsewhere.
Moving pai/nondominium.code-workspace Editors resolve a workspace file by its path. Listed in the README tree instead.

How to test

nix develop
ls .cursor/skills/nondominium-review .agents/skills/nondominium-review .claude/skills/nondominium-review
ls .claude/commands/          # nondominium-review.md
ls .cursor/rules/             # all 12 .mdc still generate from the new paths
bun run pai:sync              # copies shared skills from the working tree

Then run /nondominium-review 143 on this PR and check the verdict block renders.

Note that nix only sees git-tracked files, so a locally added skill must be committed before nix develop picks it up.

Documentation

  • pai/README.md — rewritten: architecture, per-tool invocation, two skill sources, the one-home rule, editing workflow
  • CONTRIBUTING.md — review procedure and invocation table, merge conflict policy, corrected CI description, updated source table
  • REVIEW.md — content/procedure split noted; §4 signature corrected
  • pai/shared/skills/nondominium-review/SKILL.md — new

Related

  • Follow-up: chore/rustfmt to clear the formatting backlog, then make lint.yml blocking
  • Follow-up: clippy in its own PR once its output has been reviewed
  • Follow-up: sweettest (person) flaked on run 33278757427 (get_hrea_agents_returns_matching_rea_agent, person_create_populates_hrea_agent_hash, both panicking in holochain's sweet_conductor_handle.rs). The same job passed on an earlier run against identical Rust; every commit here is markdown, nix and config. Worth its own issue: a flaky gate is the first thing that will make someone override the wait-for-green rule.
  • pai/human_ai_collaboration.md moved into pai/shared/ (move only, no content edits)

@Soushi888
Soushi888 requested a review from TiberiusB August 29, 2026 22:31
Adds a nondominium-review skill under pai/, distributed by the existing nix
pipeline to .claude/, .cursor/ and .agents/ so any collaborator's assistant
runs the same procedure. It routes to REVIEW.md and CONTRIBUTING.md rather
than restating them, matching the nondominium-domain skill's discipline.

Adds an advisory lint workflow reporting rustfmt status, documents the two
sources a skill can come from, and corrects the await_consistency call shape
in REVIEW.md, which named a signature that does not compile.
continue-on-error keeps the workflow green but still marks the job failed, so
every PR showed a red X and gh pr checks reported a failure. Our own review
procedure treats a failing check as a merge blocker, so the advisory job was
blocking reviews it was never meant to gate. The job now always exits 0 and
the blocking form is one commented line away.
The governing question when resolving is which side carried semantic intent
and which only restated it. Presentational changes yield to semantic ones, a
conflict where both sides are semantic is a decision between two authors
rather than a merge mechanic, and a resolution is backed by evidence instead
of asserted.

Names what counts as fundamental here (REQ meaning, enforced validation, VF
field names, NDO layer boundaries) and the two hotspots the open branches
actually conflict on. The integrity enum case is the rule at its sharpest:
hdk_entry_types derives indices from declaration order, so tidying the enum
while resolving invalidates every entry already on the DHT.

The review skill now treats conflict resolution as its own check.
pai/shared/ now holds everything that describes the project: conventions,
rules (was cursor-rules/, whose content was never Cursor-specific), and
skills (was claude/skills/, though every skill already fanned out to
.claude/, .cursor/ and .agents/). pai/harnesses/ holds only what one tool
needs of its own, which today is Claude Code's settings.json. Cursor gets
no directory because its adapter is a pure transform in nix/cursor-pai.nix.

The test for where a file belongs: would its content change if we dropped
support for a tool? If no, it is shared.

Adds bun run pai:sync for the inner loop. Nix flakes only see git-tracked
files, so a new skill silently does not materialize until it is committed;
this copies pai/shared/skills/ from the working tree so an uncommitted edit
is visible immediately. Nix stays the authority: it owns the pinned flake
inputs and is what CI runs.

Also removes the pai/README.md description of a LoadProjectDocs.hook.ts
SessionStart hook. No such hook exists and settings.json wires none; the
@ mentions in .rules are the whole mechanism. The docs described a second
injection path that was never built.
A slash command any contributor gets from the repo itself, with nothing
installed from anywhere else. It is deliberately thin: it points at the
shared nondominium-review skill and adds no judgement of its own, so the
procedure a reviewer follows does not depend on which editor they opened.

Lives under pai/harnesses/claude/ because /name is a Claude Code mechanism
rather than a portable one. The existing rsync copies the whole harness
tree, so a command needs no flake.nix change.

If a general-purpose review skill is installed on the machine, the command
tells it to contribute forge mechanics only: the check order and the verdict
shape stay the project's. A review that silently applied another repo's
conventions is the thing this is meant to prevent.
The previous commit claimed /name was a Claude Code mechanism and gave
Cursor nothing. That was wrong. In Cursor, skills ARE the slash-command
mechanism: typing / lists the skills in .cursor/skills/ and .agents/skills/
and attaches the chosen one. Cursor has no .cursor/commands/ directory.

So the portable command is the skill, which we already distribute to all
three trees, and Claude Code is the single harness needing an adapter file
because its /name reads .claude/commands/ instead. Tibi already had the
command in Cursor; what was missing was saying so.

CONTRIBUTING.md now gives the invocation per tool, and pai/README.md
explains why one harness needs a file and the others do not.
pai/human_ai_collaboration.md arrived from #132 at the pai/ root. It reads
as harness-agnostic, so pai/shared/ is the likely home, but it is Tibi's
document and moving it as a side effect of this refactor is his call to
make, not mine.
@Soushi888
Soushi888 force-pushed the chore/shared-review-workflow branch from 5fc7d3c to 993a38d Compare August 30, 2026 01:02
Found by running /nondominium-review against this PR: every commit here
uses a pai scope that the CONTRIBUTING.md scope list does not declare, so
the procedure's own merge-criteria check failed on the branch introducing
it. Adding the scope rather than renaming the commits, since pai/ is a real
area of the repo now.
It arrived from #132 at the pai/ root, which the new structure does not
use. The content is harness-agnostic reference material, so it classifies
as shared by the same test as everything else there: its content would not
change if we dropped support for a tool.

Move only, no edits to the document. Nothing referenced the old path except
the README note now replaced by the listing.

Authorised by @Soushi888.
@Soushi888
Soushi888 force-pushed the chore/shared-review-workflow branch from 5b1ba9b to adafe0b Compare August 30, 2026 01:28
pai/nondominium.code-workspace is the last thing at the pai/ root. Listed
rather than moved: editors resolve a workspace file by its path, so it is
not ours to relocate for tidiness. The diagram now shows everything that
is actually there, which is the point of a diagram.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant