Skip to content

fix(push): preserve namespaced rule destinations - #654

Merged
jeff-r2026 merged 1 commit into
Tencent:mainfrom
dvd233:codex/fix/namespaced-rule-push-649
Sep 20, 2026
Merged

jeff-r2026 merged 1 commit into
Tencent:mainfrom
dvd233:codex/fix/namespaced-rule-push-649

Conversation

@dvd233

@dvd233 dvd233 commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve ResourceItem.relativePath when pushing rules back to the team repository.
  • Keep the destination constrained to the repository's rules/ tree.
  • Add regression coverage for a namespaced rule edited locally.

Fixes the namespaced-rule push portion of #649.

Root cause

RulesHandler.scanLocalForPush already records destinations such as rules/frontend/scoped.md, but pushItem ignored that field and reconstructed every destination as rules/<name>.md. Editing a namespaced rule therefore silently moved it into the shared root on push.

Validation

  • Regression red on origin/main: the new namespaced-destination test failed because rules/frontend/scoped.md was never created.
  • npx vitest run src/__tests__/rules.test.ts: 45 passed.
  • npm run typecheck: passed.
  • npm run build: passed.
  • git diff --check: passed.
  • Open-PR audit: fix(pull): report actual sync outcome instead of team-repo item count #597 touches the same source file only for pull/install-directory behavior; it has no pushItem or relativePath hunk.

@jeff-r2026 jeff-r2026 self-assigned this Sep 19, 2026
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] PR description lacks real-CLI end-to-end verification. The Validation section lists unit tests, typecheck, build, and diff checks, but no actual teamai CLI push workflow. The repository’s review rules require an e2e/real-CLI verification record before merge, covering the affected namespaced-rule push behavior.

No additional issues found in the reviewed diff. Tests were not run, per instruction.

@jeff-r2026
jeff-r2026 merged commit 9dcd23e into Tencent:main Sep 20, 2026
10 checks passed
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.
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.

2 participants