fix(push): preserve namespaced rule destinations - #654
Merged
jeff-r2026 merged 1 commit intoSep 20, 2026
Merged
Conversation
No additional issues found in the reviewed diff. Tests were not run, per instruction. |
10 tasks
SaulMoro
added a commit
to SaulMoro/teamai-cli
that referenced
this pull request
Sep 22, 2026
…ent#649) `--role`/`--project` only ever placed new skills, so a rule pushed with `--project front-app` landed at `rules/<name>.md` and a new agent at `agents/<name>.yaml` — both of which `pull` ships to every member. The flag also collapsed into the project's `skills` namespace, which is the wrong directory for a rule: a rule is namespaced on the `knowledge` axis, and the manifest allows the two to differ. Each pushable type now resolves from its own axis (skills → `skills`, rules → `knowledge`, agents → `agents`), and the destination is printed rather than chosen silently. Where the named project declares no namespace for a type being pushed, the command fails and names it instead of writing to the shared root. Only new resources already at the shared root are placed; anything the scanner namespaced keeps its path (Tencent#654), and an open PR's recorded destination still wins so a force-push never moves a resource. Also resolves a root-level local rule against its namespaced team copy, so a rule that was placed on an earlier push is not re-pushed to the shared root once it merges.
SaulMoro
added a commit
to SaulMoro/teamai-cli
that referenced
this pull request
Sep 22, 2026
…ent#649) `--role`/`--project` only ever placed new skills, so a rule pushed with `--project front-app` landed at `rules/<name>.md` and a new agent at `agents/<name>.yaml` — both of which `pull` ships to every member. The flag also collapsed into the project's `skills` namespace, which is the wrong directory for a rule: a rule is namespaced on the `knowledge` axis, and the manifest allows the two to differ. Each pushable type now resolves from its own axis (skills → `skills`, rules → `knowledge`, agents → `agents`), and the destination is printed rather than chosen silently. Where the named project declares no namespace for a type being pushed, the command fails and names it instead of writing to the shared root. Only new resources already at the shared root are placed; anything the scanner namespaced keeps its path (Tencent#654), and an open PR's recorded destination still wins so a force-push never moves a resource. Also resolves a root-level local rule against its namespaced team copy, so a rule that was placed on an earlier push is not re-pushed to the shared root once it merges.
SaulMoro
added a commit
to SaulMoro/teamai-cli
that referenced
this pull request
Sep 22, 2026
…ent#649) `--role`/`--project` only ever placed new skills, so a rule pushed with `--project front-app` landed at `rules/<name>.md` and a new agent at `agents/<name>.yaml` — both of which `pull` ships to every member. The flag also collapsed into the project's `skills` namespace, which is the wrong directory for a rule: a rule is namespaced on the `knowledge` axis, and the manifest allows the two to differ. Each pushable type now resolves from its own axis (skills → `skills`, rules → `knowledge`, agents → `agents`), and the destination is printed rather than chosen silently. Where the named project declares no namespace for a type being pushed, the command fails and names it instead of writing to the shared root. Only new resources already at the shared root are placed; anything the scanner namespaced keeps its path (Tencent#654), and an open PR's recorded destination still wins so a force-push never moves a resource. Also resolves a root-level local rule against its namespaced team copy, so a rule that was placed on an earlier push is not re-pushed to the shared root once it merges.
SaulMoro
pushed a commit
to SaulMoro/teamai-cli
that referenced
this pull request
Sep 22, 2026
…tion win The staleness guard added last round was defeated by the pull it recommended: `pull` advances lastPullRev without deploying an inactive namespace, so the next push saw an unchanged canonical and wrote the stale rendering anyway. It also never fired right after the first PR merged, when the file did not exist at lastPullRev. The guard is gone, and the cause with it. `pull` now delivers an agent whose placement record names it, so the local copy tracks the team file and the ordinary comparison is valid — the inactive case stops being special instead of needing its own machinery. A stem an ACTIVE namespace already claims is left alone, since agents deploy flattened and the active one is what is deployed here; the scan follows the same order, treating the record as a fallback rather than an extra candidate. Pending-PR reuse matched on type and name alone, so an open PR for a different resource of the same name captured a push that named another namespace and force-pushed into that review. Neither silent answer is safe, so the flag the user typed decides, the open PR is left untouched, and the collision is reported. This supersedes the original Tencent#331/Tencent#654 rule that a PR's destination always won; that rule still holds whenever no destination is named.
SaulMoro
added a commit
to SaulMoro/teamai-cli
that referenced
this pull request
Sep 22, 2026
…ent#649) `--role`/`--project` only ever placed new skills, so a rule pushed with `--project front-app` landed at `rules/<name>.md` and a new agent at `agents/<name>.yaml` — both of which `pull` ships to every member. The flag also collapsed into the project's `skills` namespace, which is the wrong directory for a rule: a rule is namespaced on the `knowledge` axis, and the manifest allows the two to differ. Each pushable type now resolves from its own axis (skills → `skills`, rules → `knowledge`, agents → `agents`), and the destination is printed rather than chosen silently. Where the named project declares no namespace for a type being pushed, the command fails and names it instead of writing to the shared root. Only new resources already at the shared root are placed; anything the scanner namespaced keeps its path (Tencent#654), and an open PR's recorded destination still wins so a force-push never moves a resource. Also resolves a root-level local rule against its namespaced team copy, so a rule that was placed on an earlier push is not re-pushed to the shared root once it merges.
SaulMoro
pushed a commit
to SaulMoro/teamai-cli
that referenced
this pull request
Sep 22, 2026
…tion win The staleness guard added last round was defeated by the pull it recommended: `pull` advances lastPullRev without deploying an inactive namespace, so the next push saw an unchanged canonical and wrote the stale rendering anyway. It also never fired right after the first PR merged, when the file did not exist at lastPullRev. The guard is gone, and the cause with it. `pull` now delivers an agent whose placement record names it, so the local copy tracks the team file and the ordinary comparison is valid — the inactive case stops being special instead of needing its own machinery. A stem an ACTIVE namespace already claims is left alone, since agents deploy flattened and the active one is what is deployed here; the scan follows the same order, treating the record as a fallback rather than an extra candidate. Pending-PR reuse matched on type and name alone, so an open PR for a different resource of the same name captured a push that named another namespace and force-pushed into that review. Neither silent answer is safe, so the flag the user typed decides, the open PR is left untouched, and the collision is reported. This supersedes the original Tencent#331/Tencent#654 rule that a PR's destination always won; that rule still holds whenever no destination is named.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ResourceItem.relativePathwhen pushing rules back to the team repository.rules/tree.Fixes the namespaced-rule push portion of #649.
Root cause
RulesHandler.scanLocalForPushalready records destinations such asrules/frontend/scoped.md, butpushItemignored that field and reconstructed every destination asrules/<name>.md. Editing a namespaced rule therefore silently moved it into the shared root on push.Validation
rules/frontend/scoped.mdwas never created.npx vitest run src/__tests__/rules.test.ts: 45 passed.npm run typecheck: passed.npm run build: passed.git diff --check: passed.pushItemorrelativePathhunk.