Skip to content

fix(rules): deliver team rules through each tool's own channel - #957

Open
SaulMoro wants to merge 15 commits into
Tencent:mainfrom
SaulMoro:fix/946-rules-delivery
Open

SaulMoro wants to merge 15 commits into
Tencent:mainfrom
SaulMoro:fix/946-rules-delivery

Conversation

@SaulMoro

@SaulMoro SaulMoro commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Team rules
├─ Native formats → Kiro, Qoder/CN, CodeBuddy/WorkBuddy, OMP, JoyCode project
├─ User files     → ZCode, dsh, Pi, JoyCode, OpenClaw
├─ Project hooks  → ZCode, dsh; Pi extension
└─ No project channel → Hermes, OpenClaw; init/doctor explain why

Deliver each tool's rules in its own format or scope-specific channel. Fix OpenCode globs and Hermes' user-only SOUL.md writes. OpenClaw dispatches emitted events; doctor checks what tools read. Upgrade pulls repair unchanged revisions and keep edited copies.

README capability changes

Every changed check and the new JoyCode row, matching all five README variants.

Agent Capability Before After Included by
Codex rules ✓ ✓* #946
OpenCode rules ✓ ✓* #946
Pi Coding Agent rules ✓ ✓* #946
Hermes rules — ✓* #946
OpenClaw rules ✓ ✓* #946
DeepSeek Harness rules — ✓* #946
ZCode rules — ✓* #946

✓* means the rules reach the tool but apply always, without path scoping (scoped inline rules include path hints for the model). The ✓ → ✓* changes clarify always-on delivery; OpenClaw's old ✓ counted copies in .openclaw/rules, which it never read.

JoyCode now has a complete README row:

New agent README columns After
JoyCode skills, rules, docs, env, agents, learnings, codebase, teamwiki ✓
JoyCode hooks, mcp, models, usage, sessions, dashboard —

For JoyCode, native path scoping applies in project scope; the user rules.txt block is always on.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature causing existing behavior to change)
  • Documentation only
  • Refactor / internal cleanup

Test Plan

Live mock-model evidence:

Check Before After
OMP 18.2.1, project root Unscoped and namespaced rules absent Each appears once; scoped rule stays in the rulebook with globs/description
OpenCode 1.18.21, user scope User rules absent; project decoy loaded User and namespaced rules each once; decoy absent

Pi 0.99.2 also carried each user/project rule once across two turns. OpenClaw startup/new/reset dispatched in a live gateway.

Real CLI after npm run build, at 2c70f265 (rebased onto main), sandbox HOME, git provider, project scope with Claude, Kiro and OpenClaw enabled:

# Fixture: team rule rules/fe/style.md (paths: src/**/*.ts); an older release's
# claudemd block in the project's .openclaw/workspace/AGENTS.md
$ teamai pull
✔ [project] Synced 1 rule(s)
ℹ [project] Removed teamai instruction blocks from <sb>/proj/.openclaw/workspace/AGENTS.md: replacement instructions are ready
$ cat .kiro/steering/fe.style.md
---
inclusion: fileMatch
fileMatchPattern: ["src/**/*.ts"]
---
$ teamai doctor
  ✔ Rules delivered to kiro
  ✔ No team instruction blocks are left in files no tool loads them from
✔ All checks passed!

Verified: flat Kiro steering file in Kiro's format; the old OpenClaw project block stripped (the path the rebase had to reconcile); doctor passed. Kiro and OpenClaw themselves were not run.

Real CLI for the Codex review round, at 2428b2cf, user scope with OMP and OpenCode:

# Team deletes rules/fe/ after a pull; the member had edited ~/.config/opencode/rules/fe/style.md
$ teamai pull
⚠ Kept <sb>/home/.config/opencode/rules/fe/style.md: teamai no longer delivers fe/style here, but you changed this copy. Delete it when you no longer need it.
$ cat ~/.config/opencode/opencode.json
{ "instructions": ["<sb>/home/.config/opencode/rules/*.md"] }        # fe/*.md reclaimed (it stayed before)

# Member edits the delivered OMP flat copy be.api.md
$ teamai uninstall --agent omp --force
⚠ Kept <sb>/home/.omp/agent/rules/be.api.md: you edited it after teamai delivered it. Delete it once you have saved what you need.
✔ Removed 1 rule files

At 24746434, Pi in user scope and a team rule paths: **/*.ts (unquoted):

$ teamai pull --force
$ grep -A1 "Applies to" ~/.pi/agent/AGENTS.md
Applies to files matching: **/*.ts          # missing before: the strict parse dropped the scope
Use strict types.
$ teamai doctor
  ✔ Team rules are inlined in Pi AGENTS.md

Related Issues

Fixes #946.

#952 is merged; this branch is rebased onto origin/main with its carrier commit dropped, so the diff is #946 only.

Reconciling #946 with #952's per-member instruction targets (the reconcile commit plus in-place conflict resolutions):

Where #952 on main #946 Kept
instruction-targets.ts Sync instructionTargetFile; retires a moved claudemd; cleans a retired file only once its writers are reached Async targets (OpenClaw workspace, $DSH_HOME) Async throughout. OpenClaw's default workspace is not retired (its default profile reads it); a tool with no file and no hook channel here (OpenClaw in a project) does not hold cleanup back; a hook tool whose hook is not installed still does
uninstall.ts Removes OpenCode's instructions entry only when pull recorded adding it Removed it with the context file; deleted an empty .opencode/opencode.json #952's recorded ownership. The empty-file deletion stays for rules globs only; a test now pins that a $schema-only file stays after the instructions entry goes
doctor-delivery.ts Skips a hook check when there are no instruction blocks Pi's extension also carries project rules Pi's check stays while the project has team rules
rules.ts Reclaims a reserved teamai-context rule copy by its render Rule renders per tool, with earlier formats Reclaim checks every delivered render
#945 every-shape test Every installed tool with a claudemd gets blocks OpenClaw installs by workspace and has no project file Fixture creates the workspace; OpenClaw exempt in project scope
Usage guides (en/zh) Pi hooks and uninstall wording; Codex mcpProject (#954) Pi rules, uninstall team-rules bullet, toolPaths migration note Both

Notes for Reviewers

History: dependency base → nine slices → review fixes. /code-review against origin/main after the rebase:

Axis Fixed in 2c70f265 Left as is
Standards Hermes/OpenClaw ✓* in all five READMEs; inverse uninstall test; doctor resolves team rules once; one RulesHandler in the uninstall plan; ruleChannelNotes early return; scope-accurate pull warning; CHANGELOG typo Judgement-call refactors: per-tool tool === 'openclaw'/'pi' checks beside the tables, a tool's rule channel spread over four tables, rules.ts size, shared frontmatter shape across the render modules. Each is a cross-cutting move better done on its own
Codex review (P1 ×2) An OMP flat copy on record counted as teamai's even after the member edited it, so uninstall deleted the edit; ownership now needs the recorded hash or the render, and remove/uninstall keep and name an edited one. OpenCode user globs of a namespace the team deleted were no longer owned, so pull and doctor left them loading kept copies; ownership now also covers every namespace directory at a revision this checkout pulled. Both fixed in 2428b2cf, each with a failing-first test —
Codex review, round 2 (P1 ×2) Pull deleted the superseded flat file (fe.style.md beside the author's placed style.md) without proof it was teamai's, so a member's own file of that name went every pull; it now goes only while it holds what was recorded or the render, and an edited copy is kept and named. inlinedRulesText parsed paths: strictly, so an unquoted paths: **/*.ts lost its Applies to files matching line in every inline channel; it now uses the tolerant team-rule parse. Both fixed in 24746434, each with a failing-first test —
Spec The no-channel guard released a hook tool whose hook was not installed (Codex in a project without ~/.codex); now narrowed, with a failing-first test Cursor/CodeBuddy/JoyCode parser tests run only with TEAMAI_RULE_PARSER_BUNDLES, so they skip in CI; byte contracts stand in

Door: Two-way. Code can be reverted; restoring legacy file layouts requires a pull. Migration removes only proven, unedited team copies.

Blast Radius: Rules. Delivery paths, renders, hook configuration, and diagnostics across tools.

Retained limits, verification gaps, and excluded work
  • Kiro CLI ignores path scoping. OMP reads project rules only from the root.
  • Copilot CLI ≥1.0.89 also reads Claude's rules; OMP can read namespaced Copilot rules twice.
  • A WorkBuddy-only project creates .codebuddy, making CodeBuddy count as installed.
  • ZCode/dsh lose hook text at compaction. dsh requires --patch and may miss the first request. Codex project hooks require trust.
  • OpenClaw message and auto-reset mappings have fixture tests only.
  • Only OMP's parser runs in CI; Cursor, CodeBuddy and JoyCode parser tests need TEAMAI_RULE_PARSER_BUNDLES and ran locally only.
  • Not run live: Kiro, Qoder/CN, CodeBuddy, WorkBuddy, JoyCode, ZCode, dsh, and Hermes. Qoder Desktop/CN subdirectory discovery remains an accepted, unverified assumption. Cursor IDE was not checked.
  • Local real-CLI runs use the git provider; additional providers remain with CI.
  • Outside this scope, with no new issues opened: ZCode's 32 KB hook-output cap; dsh skills still ignore DSH_HOME.

@SaulMoro
SaulMoro marked this pull request as draft October 2, 2026 00:08
@jeff-r2026 jeff-r2026 self-assigned this Oct 2, 2026
@SaulMoro
SaulMoro force-pushed the fix/946-rules-delivery branch from b9e9378 to daf0817 Compare October 2, 2026 05:21
@SaulMoro SaulMoro changed the title [After #947, #952] fix(rules): deliver team rules through each tool's own channel [After #952] fix(rules): deliver team rules through each tool's own channel Oct 2, 2026
@SaulMoro
SaulMoro force-pushed the fix/946-rules-delivery branch from daf0817 to f87b4d7 Compare October 3, 2026 06:33
@SaulMoro SaulMoro changed the title [After #952] fix(rules): deliver team rules through each tool's own channel fix(rules): deliver team rules through each tool's own channel Oct 3, 2026
@SaulMoro
SaulMoro marked this pull request as ready for review October 3, 2026 06:33
@jeff-r2026

Copy link
Copy Markdown
Collaborator

This branch has merge conflicts with main. Please rebase onto the latest main and resolve the conflicts so review can continue.

…ets (Tencent#946)

- Keep OpenClaw's default workspace AGENTS.md out of the retired files: its
  default profile still reads it.
- Strip a retired file of a tool with no file and no hook in this scope
  (OpenClaw in a project) without waiting for a replacement.
- Keep doctor's Pi extension check while the project has team rules, even
  with no instruction blocks.
- Adapt the Tencent#945 every-shape test to OpenClaw's workspace install probe and
  its lack of a project file; drop the Tencent#946 uninstall test Tencent#945's recorded
  entry ownership superseded.
- Hold back a hook tool's retired instruction file while its hook is not
  installed; only a tool with no project channel (OpenClaw) releases it.
- Mark Hermes and OpenClaw rules always on (✓*) in every README.
- Pin that a project uninstall keeps a .opencode/opencode.json left with
  only $schema once the recorded instructions entry goes.
- Resolve the team rules once for doctor's hook checks; use one
  RulesHandler in the uninstall plan; return early from ruleChannelNotes
  outside a project; name only the files a project pull touches when the
  team rules cannot be resolved; drop a stray CHANGELOG period.
@SaulMoro
SaulMoro force-pushed the fix/946-rules-delivery branch from f87b4d7 to 2c70f26 Compare October 3, 2026 07:04
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
  • [P1 blocking] Preserve edited OMP copies during uninstall — src/resources/rules.ts:644 treats any delivery record as proof that the current flat file is owned. If a member edits a delivered fe.style.md and runs teamai uninstall, ownedFlatCopies() schedules it for unconditional deletion, losing the edit. Require the current hash to match the recorded hash, as other uninstall cleanup does.
  • [P1 blocking] Reclaim globs for deleted OpenCode namespaces — src/resources/opencode-config.ts:112 recognizes namespace globs only from currently desired or currently existing team-rule directories. When the last fe/* rule is deleted or renamed, the previously generated absolute .../rules/fe/*.md entry is no longer considered owned, so pull and doctor leave it in opencode.json. An edited copy preserved in that directory therefore remains loaded after the team stops delivering it.

…namespace globs (Tencent#946)

- A flat copy on record is teamai's only while it holds what was recorded
  or the render; remove and uninstall keep and name an edited one.
- OpenCode's user rules globs own every namespace directory a rule landed
  in at a revision this checkout pulled, so pull and doctor reclaim the
  glob of a namespace the team deleted.
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] Preserve a personal flat-name file before deleting supersedes — src/resources/rules.ts:436. With a placement record mapping style to rules/fe/style.md, Kiro/OMP deliver the author’s copy as style.md and set supersedes to fe.style.md. If the member independently created fe.style.md, every pull deletes it without checking the delivery ledger or rendered content. Apply the same ownership/edit verification used for movedFrom before removal.
  • [P1 blocking] Use the tolerant parser when generating inline rule hints — src/resources/rules.ts:1388. inlinedRulesText() uses splitFrontmatter(), while the new native renderers use teamRuleData() specifically to support common unquoted globs such as paths: **/*.ts. For that input, inline channels receive the body without the promised Applies to files matching hint, causing Codex/ZCode/DSH/Pi/Hermes/OpenClaw to treat a scoped rule as globally applicable.

Resolved

  • The earlier OMP uninstall finding is fixed by verifying the recorded hash or rendered content.
  • The earlier OpenCode deleted-namespace glob finding is fixed by considering namespaces from previously pulled revisions.
  • The PR description includes a representative real-CLI verification at head 2428b2cf, so its testing record is sufficient.

…n inline rules (Tencent#946)

- A superseded flat copy (fe.style.md beside the author's style.md) goes
  only while it holds what was recorded or the render; an edited one is
  kept and named, a member's own file is left alone.
- inlinedRulesText reads paths: through the tolerant team-rule parse, so
  paths: **/*.ts keeps its 'Applies to files matching' line.
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.

[bug] Team rules miss most tools: each tool's own rules format, else a file only it reads, else a hook or extension in project scope

2 participants