Skip to content

fix(pull): deliver each member's team instructions without the shared AGENTS.md (#945) - #952

Open
SaulMoro wants to merge 41 commits into
Tencent:mainfrom
SaulMoro:fix/945-member-instruction-targets
Open

SaulMoro wants to merge 41 commits into
Tencent:mainfrom
SaulMoro:fix/945-member-instruction-targets

Conversation

@SaulMoro

@SaulMoro SaulMoro commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes #945.

 pull(member, project)
   resolve culture, claudemd and recall for the member's roles
-  overwrite shared instruction files
+  deliver through each installed tool's file or session hook
+  confirm each resolved block before retiring its old managed copy

 HTTP SessionStart
-  read the prompt cache concurrently with sync
+  sync, then read the cache for the first prompt

 failed cleanup or OpenCode removal
-  acknowledge success or discard ownership
+  preserve cache, manifest and ownership for retry

Claude uses .claude/rules/teamai-context.md; Cursor uses an always-applied .mdc; CodeBuddy and WorkBuddy share their project rule. OpenCode loads its context through instructions. Codex, Pi, Oh My Pi and Hermes project delivery uses hooks or extensions. Configured tools without rules retain their member-owned claudemd fallback. Authored text is preserved. Doctor, bilingual guides, affected README translations and skill-data follow the delivery behavior.

Per-file outcomes replace warning-text matching. Retired shared files are cleaned only after every installed former writer has replacement delivery; excluded tools' current and retired files remain untouched and are omitted from doctor's stale check. HTTP cleanup verifies all former writers against the current cached prompt across commands and preserves culture/recall blocks. Project uninstall preserves global Pi/OMP/Hermes adapters and Codex hooks for other projects and retains project state while an enabled global hook tool still uses it. OpenCode ownership is saved before activation; a failed state write prevents activation and a failed config write retains retry intent. Existing member-listed entries remain member-owned. Failed OpenCode config removal retains shared state even for full or last-tool uninstall.

Evidence

  • Before: Regression tests reproduced lost instructions after a failed replacement, success ACKs after failed HTTP cleanup, lost OpenCode removal ownership, deleted global adapters, and an empty first HTTP prompt.
    After: The preceding five CI findings and additional valid adversarial findings are fixed. Tests cover retry after repair, excluded tools, malformed files remaining unchanged, OpenCode registration and recall ownership, and member-owned files surviving uninstall. Earlier PR findings remain resolved.

The latest three CI findings are also fixed: unreadable culture cannot be retired after another block succeeds; separate HTTP deliveries jointly authorize cleanup only when destinations match the current prompt; and project uninstall recognizes globally installed Pi/OMP/Hermes/Codex without local tool directories. Seven regression cases failed on the preceding commit and pass with these fixes. Two additional HTTP cases cover changed prompts and rejected replacements.

The latest CI findings are fixed: project uninstall preserves global Codex-family hooks and records only the project exclusion; OpenCode activation cannot precede durable ownership; excluded Claude retired files no longer fail doctor. Five focused cases and both new real-CLI scenarios failed before the repair and pass now.

The adversarial review's global OpenCode plugin uninstall case already exists unchanged on main and remains outside this PR. Its claim that surrounding member text is deleted was not reproduced; the narrower deletion of an emptied tracked member file was reproduced and fixed.

Validation of the tree committed as ebc86bb9c3b393dd6facf206cb636449b0ec175c:

  • npm run build, npx tsc --noEmit, and npm run lint: passed.
  • npx vitest run: 371 files passed; 7,451 tests passed, one skipped.
  • npx vitest run --config vitest.e2e.config.ts src/__tests__/e2e/instruction-targets.test.ts: 37 real-CLI scenarios passed after build. These execute node dist/index.js with isolated fixtures and cover role selection, migration failure/retry, exclusions, two-project Pi uninstall, OpenCode activation, unreadable culture migration/retry, WorkBuddy uninstall with global-only Pi remaining, and targeted/full project Codex uninstall with another project still receiving instructions.
  • npm run test:e2e: 81 files passed, 3 skipped; 489 tests passed, 26 skipped, including the first HTTP SessionStart scenario that failed in CI.
  • Bounded fixture-removal retries were insufficient in the later Linux CI run. The latest repair below waits for the real detached workers to exit before tearing down the HTTP server and fixture.
  • No commands or flags changed. Additional provider/agent combinations remain CI coverage.

Representative built-CLI verification makes culture.md unreadable on the first upgraded pull, repairs it, then retries. A second scenario uninstalls project WorkBuddy while global-only Pi remains enabled:

unreadable culture: old culture retained; resolved shared instructions delivered and retired
culture repair: replacement delivered; retired culture removed on the next pull
WorkBuddy project uninstall: project state preserved; global-only Pi still receives member instructions
Codex project uninstall, targeted and full: global hooks unchanged; second project receives its PRODUCT role instructions; removed project receives none

Based on origin/main bae48e5c, with no unrelated commits. The prior rebase preserved main's Codex hook work. PR #957 is unchanged.

Own adversarial review against main, and the CI review of it

The own review found that a project uninstall kept the global adapters even on the last install. Its first fix (e779562d) removed them when no user config or other project partition existed; the CI review showed that misses HTTP-only installs and self or legacy projects, whose config cannot be enumerated. Fixed on the current head:

Finding Fix Test
P1: project uninstall could remove global Pi/OMP/Hermes/Codex adapters and pushed agent hooks another install still uses A project uninstall keeps them and its summary names each one, with teamai hooks remove (which removes them) as the step to run first when no other install uses them uninstall.test.ts (Pi, OMP, Hermes, Codex)
P2: pull --dry-run omitted retired-file cleanup the real pull does after installing a missing adapter The preview counts a channel the hook reconcile would install as ready, unless a member's same-named file or a disabled Hermes plugin keeps it closed e2e previews the cleanup a real pull does…

Real CLI, project-only machine with Pi:

$ teamai uninstall --force
ℹ  Kept for other teamai installs on this machine (user scope, HTTP agent or other projects):
     <sandbox>/home/.pi/agent/extensions/teamai-hooks.ts
   If none of them uses these, cancel and run `teamai hooks remove` here first: it removes them.
extension after uninstall: present
after teamai hooks remove: absent; after uninstall: absent

npx tsc --noEmit, npm run lint, npx vitest run (7,455 passed, 1 skipped), and the new dry-run e2e case. Full e2e left to CI.

CI repair at 300edfbd

empty-plan project uninstall
  preview exclusion
  dry-run returns without writes
  declined confirmation returns without writes
  confirmation or --force persists exclusion

first HTTP Codex SessionStart fixture
  download and ACK the prompt
  wait for detached pull/plugin workers to exit
  close HTTP server, then remove fixture

The P1 empty-plan finding is valid and fixed for Pi, OMP, Hermes and all Codex variants. Six unit regressions failed before the fix. The real-CLI test verifies byte-identical project config after dry-run/cancellation and exclusion only after --force, with its global adapter intact.

CI run 37059360027 failed twice with teardown ENOTEMPTY despite five retries. The test preload now inherits the fixture's output pipes in detached workers, so the CLI helper's close event waits for all workers. PID start/exit records assert that none is still running before cleanup; this assertion failed before the fixture repair and passes now. The real background jobs still execute.

Validation of 300edfbd842f2d31979f3eb412c7022759d8bac9: build, type check and lint passed; npx vitest run passed 7,461 tests, one skipped. After build, npx vitest run --config vitest.e2e.config.ts src/__tests__/e2e/instruction-targets.test.ts passed all 39 real-CLI scenarios; the first HTTP SessionStart passed five additional repetitions with the worker-exit assertion.

real Pi uninstall: dry-run and cancellation keep config unchanged; --force records exclusion and keeps global adapter
real Codex SessionStart: empty HTTP cache → download ACK success → FIRST-HTTP-PROMPT-SENTINEL in first output

Full E2E on this head is left to CI. Bilingual usage docs and setup skill-data describe the exclusion gate. No commands or flags changed.

Current CI findings

HTTP hook with a resolved tool exclusion
  skip sync and cached prompt injection
  keep HTTP-only delivery when no Git config exists

native project file retains a TeamAI block
  hook skips that block only
  cleanup succeeds → hook resumes delivery

retired file has incomplete or repeated markers
  doctor fails the stale-block check and names the repair

Both P1 findings and the P2 finding are valid and fixed. Hook delivery respects Codex's AGENTS.override.md and OMP's .omp/AGENTS.md precedence. Git and HTTP prompt delivery use the same native-file check; replacement readiness still uses the complete resolved blocks so suppression cannot prevent cleanup.

Before the repair, 14 new unit regressions failed. They cover HTTP exclusion for Pi, OMP, Hermes and the Codex family, retained native blocks, and malformed retired markers. HTTP-only delivery remains covered. Two older Codex assertions that required duplicate native/hook blocks now assert suppression and resumption after cleanup.

Validation of 45dcd2171efa5204cc72b57a3a1a2d5fa528a5b8: build, type check and lint passed; npx vitest run passed 7,476 tests, one skipped. After build, npx vitest run --config vitest.e2e.config.ts src/__tests__/e2e/instruction-targets.test.ts passed all 40 real-CLI scenarios.

blocked WorkBuddy replacement: native prompt retained; Pi hook omits that block and still delivers culture
WorkBuddy repair: legacy block removed; Pi hook resumes the developer prompt
real Codex SessionStart: empty HTTP cache → download ACK success → FIRST-HTTP-PROMPT-SENTINEL in first output
after project Codex uninstall: SessionStart/SubagentStart inject no cached HTTP prompt and send no HTTP sync

The first HTTP SessionStart fixture continues to wait for detached workers before teardown. Bilingual usage docs and skill-data describe retained-block suppression, doctor marker repair and HTTP exclusions. No commands or flags changed. The full provider/agent E2E matrix remains CI coverage.

Merge Danger

Door: two-way, with managed-block migration on pull

Reverting restores the old delivery implementation; an older client must rewrite any retired blocks. Migration preserves authored text and retains old blocks until replacement delivery is confirmed.

Blast Radius: team-wide

Older clients can write member-specific blocks back into shared files, so teams should upgrade together. Pi, Oh My Pi and Hermes project delivery requires their extension or plugin. Hermes limits its project section to 4,000 characters. Claude Code plus Copilot CLI can still read Claude's rule blocks twice.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/instruction-targets.ts:112 — Retired paths are hard-coded instead of derived from the previous configured toolPath.claudemd. For example, a team using toolPaths.claude.claudemd: ".claude/custom.md" will receive the new .claude/rules/teamai-context.md, but the old role-specific blocks remain in .claude/custom.md; Claude loads both, so the cross-member contamination this PR fixes persists. The same issue affects other moved targets with configurable legacy paths.
  • [P1 blocking] src/instruction-targets.ts:68 — teamai-context remains a valid team rule name, and rule synchronization runs before instruction synchronization. An existing rules/teamai-context.md is therefore copied to the exact generated target for Claude, Cursor, CodeBuddy, or WorkBuddy; planFile then treats it as user-owned and refuses to deliver culture, shared instructions, or recall. Reserve/reject this rule name or use a destination that cannot collide with team rules.
  • [P2 non-blocking] src/instruction-targets.ts:422 — OpenCode registration treats any existing context path as successfully delivered. If teamai-context.md already contains user-authored content, planFile warns and leaves it untouched, but this condition still adds that file to opencode.json, unexpectedly activating unrelated content while delivering none of the TeamAI blocks. Registration should depend on a valid existing managed block or a successful planned write.

The PR description includes sufficient real-CLI/end-to-end testing evidence.

…#945)

Add src/instruction-targets.ts, one resolver for where the culture,
claudemd and recall blocks go per tool and scope. Pull, recall
enable/disable, local-agent and uninstall read targets from it.

A pull now skips tools that are not installed, whether or not they have
a rules path, so Hermes no longer writes ~/AGENTS.md when it is absent.
It also strips teamai blocks from known targets no installed tool reads,
and deletes the file when nothing else is left. Targets are unchanged.
Culture, claudemd and recall blocks now go through one planner that
works out each file's content first. A pull no longer rewrites a file
whose blocks are current, leaves a block with a missing or repeated
marker alone with a warning, deletes an emptied file only when git does
not track it, and reports the files it would change under --dry-run.
Recall is part of the same pass, so a pull removes the recall block
when recall is disabled, and recall enable/disable use the same
targets.
Claude's project-scope culture, claudemd and recall blocks move from
.claude/CLAUDE.md to .claude/rules/teamai-context.md. Claude loads that
file from the root and from subdirectories, and still reads AGENTS.md
and an authored CLAUDE.md the way it chose to; CLAUDE.local.md would
have stopped the native AGENTS.md load. The next pull removes the old
blocks from .claude/CLAUDE.md and keeps the rest of the file.

teamai-context is excluded from the rules sweep and from push, so the
file is neither deleted as stale nor pushed as a team rule.

An e2e test pulls as two members of one project with different roles:
each gets their own selection and AGENTS.md keeps its bytes.
)

Cursor had no instruction target and only saw the blocks other tools
left in AGENTS.md and .claude/CLAUDE.md. It now gets them in
.cursor/rules/teamai-context.mdc with alwaysApply: true, in both
scopes.

Uninstall and local-agent go through the same planner as pull, so a
teamai-context file is removed whole, header included, and is created
with its header.
…#945)

CodeBuddy and WorkBuddy both read the project's .codebuddy/rules, so
their project blocks share one always-applied
.codebuddy/rules/teamai-context.md instead of .codebuddy/CODEBUDDY.md and
the project AGENTS.md. WorkBuddy's user blocks move from ~/AGENTS.md to
~/.workbuddy/rules/teamai-context.md. Uninstalling one of the two keeps
the shared copy while the other is installed.

The shared AGENTS.md is cleaned only once no installed tool still
targets it; Pi and Hermes keep it until their own channels land.
Hermes' user-scope culture and claudemd blocks move from ~/AGENTS.md,
which Hermes does not read from a project under the home directory, to
$HERMES_HOME/SOUL.md beside the team rules block teamai already writes
there. Only a user-scope pull writes them, so a project pull leaves
them alone. Hermes counts as installed when $HERMES_HOME exists, the
same check rules delivery uses, instead of a ~/.hermes probe.
…#945)

Oh My Pi keeps one context file per level, so ~/.omp/agent/AGENTS.md
hid ~/.agents/AGENTS.md and .omp/AGENTS.md hid the project's AGENTS.md.
User-scope blocks now go to ~/.omp/agent/RULES.md, an always-applied
rule beside that slot. In a project, teamai's OMP extension asks the new
`hook-dispatch instructions` event for the member's blocks when a
session starts and appends them to each turn's system prompt; OMP
rebuilds that prompt from its base every turn, so they reach each
request once. The next pull removes the old blocks from both context
files.

Verified with OMP 18.2.1 against a local capture server: the blocks
reach each request once from the project root and a subdirectory, and
the project AGENTS.md still loads.
)

Pi's project blocks went into the project AGENTS.md, the file every
member shares. teamai's Pi extension now asks `hook-dispatch
instructions` for the member's blocks when a session starts and adds
them to each run's system prompt; Pi renders that prompt from its base
for every run, so they do not pile up. The next pull removes the old
blocks from AGENTS.md once no installed tool still writes there.
User-scope blocks stay in ~/.pi/agent/AGENTS.md.
Hermes' project blocks went into the project AGENTS.md even when Hermes
was not installed. teamai now installs a Hermes plugin,
$HERMES_HOME/plugins/teamai-instructions, enabled in plugins.enabled,
whose system prompt section asks `hook-dispatch instructions` for the
member's blocks for the session's directory. Hermes builds it once per
session and keeps it through compression and resume. A section holds
4,000 characters; when the blocks are longer, pull says Hermes skips
them instead of cutting them or falling back to AGENTS.md.

With Pi, Hermes and WorkBuddy moved, no tool writes the project
AGENTS.md any more, so the next pull removes the teamai blocks left
there. Uninstall also cleans a tool's retired files.
OpenCode had no instruction target and only saw the blocks other tools
left in AGENTS.md. It now gets them in .opencode/teamai-context.md,
listed in the instructions of .opencode/opencode.json, and in user scope
in ~/.config/opencode/teamai-context.md, listed by absolute path in the
user opencode.json. Only that entry is added or removed; the member's
entries and the root opencode.json stay as they are.

While ~/.config/opencode/AGENTS.md does not exist, OpenCode reads
~/.claude/CLAUDE.md, which already holds the user blocks when Claude is
installed, so teamai adds no second copy and says so.

Verified with OpenCode 1.18.21 against a local capture server: the
blocks reach the request once from the project root and a subdirectory.
…ncent#945)

doctor now asks what keeps a tool from loading this member's culture,
claudemd and recall blocks, not whether a file was written: each file
target must hold the current blocks, OpenCode's file must be listed in
its instructions, the Pi and Oh My Pi extensions and the Hermes plugin
must be installed as this build writes them (and the plugin enabled),
the Hermes section must fit its 4,000-character limit, and no file an
earlier release wrote may still hold blocks.
… removal (Tencent#945)

Three real-CLI tests for the closing criteria of Tencent#945: two members of
one project with every file-based tool installed keep the shared
AGENTS.md byte-identical across a role change and a claudemd edit; with
the generated targets excluded by the fixture, a pull that updates the
instructions leaves the task diff, the staged diff, the index and the
exclude file unchanged; and disabling recall, deleting a claudemd
source and leaving a namespace remove that content from files and from
the extension's prompt text.

Docs drop the remaining wording that named CLAUDE.md or AGENTS.md as
the injection target, and the changelog asks teams to upgrade together.
…encent#945)

Rebased onto Tencent#940, which already moves Codex's project content to its
session hooks. The rebase kept this branch's side in conflicting hunks;
this commit restores what that dropped of Tencent#940 (teamRulesHandler, the
fast-path Codex rules sync, uninstall's per-block retention and
keptRuleFiles) and joins the two designs:

- teamRulesHandler takes Codex's culture, claudemd and recall from
  resolveInstructionBlocks, as pull and the Pi, OMP and Hermes
  extensions do, and no longer skips a block found in the project
  AGENTS.md: pull removes those, and the skip handed Codex another
  member's stale selection.
- The Codex family is a hook target in project scope, with AGENTS.md
  retired for a team override or an earlier build. Hook text drops
  block markers for every tool, as Tencent#940 did for Codex.
- Uninstall keeps Tencent#940's per-block retention and clears through the
  planner; the team-rules block is one of the blocks cleanup knows.
- An emptied file goes only when it opens with a teamai block, as
  Tencent#940 decided, and git does not track it.

AGENTS.md now assert that nothing does and that uninstall clears the
blocks left there.
… exists (Tencent#945)

pull registers .opencode/teamai-context.md (or the user file) in
instructions only after writing it, so a team with no culture or
claudemd/ has no file and no entry. doctor failed that case.
…nCode path, channel reports

Standards and spec review, round 1:

- Cleanup touches only the files earlier releases wrote blocks to, never
  a tool's current target: a member without Copilot no longer strips a
  tracked .github/copilot-instructions.md a teammate's pull wrote.
- The OpenCode Claude fallback and the instructions registration live
  in instruction-targets.ts, so pull, recall enable, local-agent and
  doctor agree; a dry run reports the opencode.json change.
- pull (after installing hooks) and init name a Pi or OMP extension or
  Hermes plugin that is missing, out of date or disabled, and Hermes
  text over its limit; "Synced" is printed for file targets only.
- The Codex team-rules writer stays out of a project file when Codex's
  session hook carries the rules, even with a team claudemd override.
- Uninstall decides which blocks a remaining tool keeps from its target,
  not from toolPath.claudemd.
- One helper resolves hook text for both handlers; doctor and pull share
  the channel and limit checks and one Hermes plugins.enabled reader.
- Docs: the destination table lists every tool and marks the channels no
  live session has checked; the misplaced rows leave the recall table;
  the uninstall text in both guides and skill-data describes the shared
  CodeBuddy/WorkBuddy file; the stale Codex dedupe sentence goes.
- Docs: pull cleans only the files earlier releases wrote, not a tool's
  current file; the Cursor and CodeBuddy notes say what the loader reads
  instead of claiming live delivery.
- init reports instruction channel problems on every path that installs
  hooks; a silent session-start pull leaves them to doctor; a failed
  check no longer logs as a skipped hook reconcile; the fix names
  `teamai hooks inject`.
- A dry run promises an OpenCode instructions entry only when it would
  write the file, and a failed registration points at doctor.
- recall disable drops OpenCode's entry once its file goes; local-agent
  defers to OpenCode's Claude fallback only when Claude's file holds the
  blocks.
- Tests: uninstall keeps the shared .codebuddy rule for a WorkBuddy
  entry without claudemd; the channel check names a missing Pi
  extension; recall enable writes no OpenCode copy beside the fallback.
…call (Tencent#945)

Pi, Hermes and OpenClaw have no teamai-recall subagent, so they got no
recall block. They now get one that tells the agent to run
`teamai recall "<keywords>"` itself before code, debugging or design
work, with the subagent block's skip conditions. It uses the same
markers, so recall disable, uninstall, cleanup and doctor treat both
blocks alike; each target and hook gets the one that matches its tool.

Verified with Pi 0.99.2 against a local capture server: the block
reaches each request once from the project root and a subdirectory.
Pi's README row now shows learnings, codebase and teamwiki, the same
criterion Hermes, OpenClaw and DeepSeek Harness already meet.
- OMP counts as installed for its team instructions only where ~/.omp
  exists, which is where teamai installs its extension: a member without
  OMP in a project that has .omp/ no longer gets a warning on every pull
  and a failing doctor check that hooks inject cannot fix.
- The Hermes over-limit message names the recall block and
  `teamai recall disable`, since the recall block now counts too.
- Uninstall keeps the recall block for every remaining tool on a shared
  file, as pull now writes one to every tool.
- Tests: recall disable removes the direct block from Pi's hook text; a
  project with .omp/ and no ~/.omp reports no OMP problem.
…override.md (Tencent#945)

Tencent#947 taught the Codex session hook to skip a block already present in
AGENTS.override.md. This branch removed that skip for AGENTS.md: a teamai
block in a project instructions file holds whoever pulled last, so the
hook adds the member's own selection instead. The rebase keeps that rule
for AGENTS.override.md too, inverts Tencent#947's skip test, and drops the
skip sentence from the unreleased Tencent#938 changelog entry.
- Retired files include the claudemd a team's toolPaths gives a tool
  whose target moved, so a team override such as claude.claudemd:
  CLAUDE.md no longer keeps another member's blocks after the upgrade.
  A path that is any tool's current target is never retired; uninstall
  keeps the blocks a remaining tool still writes there.
- A team rule named teamai-context is not delivered, since it would land
  on teamai's own context rule file and block the instructions; pull
  names it.
- OpenCode's instructions list teamai-context.md only while it holds
  teamai's blocks, so a same-named file of the member's is not activated;
  doctor asks for the entry under the same condition.
SaulMoro added a commit to SaulMoro/teamai-cli that referenced this pull request Oct 2, 2026
Carries Tencent#952 (fix/945-member-instruction-targets at 46d7635) rebased onto main, which now holds Tencent#947. Tencent#952 drops the Codex hook's skip for blocks already in the project instructions file, so Tencent#947's AGENTS.override.md variant of that skip and its test go with it.
@SaulMoro
SaulMoro force-pushed the fix/945-member-instruction-targets branch from 46d7635 to 27a21a1 Compare October 2, 2026 05:26
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/hermes-hooks.ts:135 — Hermes hook injection overwrites an existing ~/.hermes/plugins/teamai-instructions/plugin.yaml and __init__.py without verifying TeamAI ownership; removeHermesHooks then recursively deletes the entire directory at src/hermes-hooks.ts:161. A user with a pre-existing same-named plugin loses its files during inject/uninstall. Add marker/ownership checks like the Pi and OMP adapters.
  • [P1 blocking] src/uninstall.ts:1078 — OpenCode’s instructions entry is removed only when teamai-context.md is deleted. If the user added text outside TeamAI’s blocks, uninstall correctly preserves the file but leaves TeamAI’s config entry active, so OpenCode continues loading it after uninstall --agent opencode or full uninstall. Remove the entry whenever OpenCode is being uninstalled, regardless of whether the preserved file remains.
  • [P2 non-blocking] src/instruction-targets.ts:398 — A shared target is marked recall-capable when any reader has an agents path. With CodeBuddy and WorkBuddy both installed but only one configured with agents, their shared .codebuddy/rules/teamai-context.md receives the subagent-based recall block, leaving the other tool instructed to invoke a nonexistent teamai-recall agent. Shared targets should use the subagent variant only when every reader can load it, otherwise use the direct CLI variant.

The three findings from the earlier review are resolved in the current diff. The PR description includes sufficient real-CLI/end-to-end testing evidence.

- The Hermes teamai-instructions plugin is written, enabled, disabled and
  removed only while its directory is absent or its plugin.yaml carries
  teamai's marker, so a member's same-named plugin survives inject and
  uninstall; pull and doctor name it.
- Uninstalling OpenCode drops teamai's instructions entry even when the
  member's own text keeps teamai-context.md.
- A file several tools share gets the teamai-recall subagent block only
  when every tool reading it has the subagent, so WorkBuddy without
  agents beside CodeBuddy gets the direct teamai recall block.
- Removing a block that opened a file leaves no blank lines above the
  member's text.
- A copy of a team rule named teamai-context an earlier release
  delivered to a rules directory is removed when the record shows it
  unchanged or it matches the team rule's render, so the instructions can
  take that path; an edited copy stays and the instruction sync names it.
- A toolPaths entry with only claudemd is probed through that file's
  directory, and a bare file such as AGENTS.md counts as installed, as
  before.
- Project CodeBuddy and WorkBuddy keep their configured claudemd when the
  entry has no rules, like Claude Code and Cursor.
- The HTTP local agent's claudemd sync strips the blocks earlier releases
  left in files no installed tool reads now, as pull does.
- Uninstall drops teamai's OpenCode instructions entry whenever the
  config lists it, also after the context file was deleted or stripped.
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/local-agent.ts:2088 — Project-scoped HTTP prompt delivery now skips every hook-only target. For Pi, OMP, and Hermes, instructionTargetFile() returns undefined, so an install_prompt_rule reaches no tool and fails at line 2144; the new hook handler cannot recover it because it reads the primary team repository, not the HTTP resource cache.
  • [P1 blocking] src/local-agent.ts:2107 — WorkBuddy’s project prompt target is .codebuddy/rules/teamai-context.md, so installation is incorrectly probed through .codebuddy. On a normal WorkBuddy-only project with .workbuddy/ present and no .codebuddy/, HTTP prompt installation is skipped and acknowledged as failed even though WorkBuddy is installed.
  • [P1 blocking] src/instruction-targets.ts:424 — OpenCode’s Claude fallback is detected only from active targets. If Claude previously received blocks and is later excluded while OpenCode remains enabled, the old ~/.claude/CLAUDE.md is deliberately preserved but no longer recognized as OpenCode’s fallback; pull then activates a second OpenCode context file, causing OpenCode to load both the stale Claude selection and the current member’s selection.

The previously reported findings are resolved in the current diff. The PR description contains sufficient real-CLI testing evidence. No PR code was executed.

- The HTTP local agent's project prompts reach Pi, OMP and Hermes: a
  machine-level `instructions` handler adds the agent's cached claudemd
  for the session's project, and the sync counts such a tool as reached
  once its extension or plugin is ready.
- The HTTP sync probes each tool through its own paths, as pull does, so
  a WorkBuddy-only project without .codebuddy/ gets its prompt.
- OpenCode treats ~/.claude/CLAUDE.md as its fallback while that file
  holds teamai blocks, also ones an excluded Claude Code left there, so
  it no longer loads a second copy; pull warns that nothing keeps them
  current.
- A test resolves targets for every tool, scope and toolPaths shape
  (full, without rules, only claudemd, only settings): an installed tool
  with a claudemd keeps getting the blocks, and no target is retired.
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/instruction-targets.ts:458 — When a user-authored teamai-context.md is already listed in opencode.json, present becomes false because the file has no TeamAI markers, and reconciliation removes the existing entry. Thus teamai pull preserves the foreign file but silently disables the user’s instructions. Only remove the entry when TeamAI previously owned/added it.
  • [P1 blocking] src/local-agent.ts:2145 — A server-delivered prompt is acknowledged as successfully installed even when planInstructionFiles refuses a foreign generated target. For example, an existing user-authored .claude/rules/teamai-context.md produces only a warning and no write, but syncedAny is still set at line 2154, so the server receives a success ACK although Claude gets none of the prompt.
  • [P1 blocking] src/local-agent.ts:2107 — Hermes HTTP prompt delivery checks only whether the plugin is installed, not whether the generated text exceeds HERMES_SECTION_LIMIT. A prompt over 4,000 characters is therefore acknowledged successfully even though Hermes skips the section; apply hookLimitProblem before counting the tool as reached.

The previously reported findings are resolved in the current diff. The PR description contains sufficient real-CLI testing evidence. No PR code was executed.

- OpenCode registration leaves an instructions entry alone while its
  teamai-context.md is a file of the member's: pull no longer drops an
  entry the member listed for their own file.
- The HTTP prompt sync acks failed, with the reason, when the planner
  leaves a target unchanged (a file teamai did not write, a malformed
  block) instead of counting the tool as reached.
- Hermes counts as reached only while the project's instructions, team
  blocks plus HTTP prompts, fit its 4,000-character section.
- The HTTP prompt sync strips an old instruction file only when every
  installed tool that wrote it received this sync's instructions, so a
  CodeBuddy prompt no longer removes Claude's blocks from .claude/CLAUDE.md.
- A rule tombstone named teamai-context no longer deletes the instruction
  file the unchanged-revision pull just refreshed.
- A placed namespaced rule named teamai-context keeps its namespaced path
  instead of landing on teamai's instruction file.
- Codex project HTTP prompts reach Codex through its SessionStart and
  SubagentStart hooks; the cache handler is named http-prompt-instructions
  so the HTTP reporter wiring stays one handler per event.
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/instruction-targets.ts:388 — Codex project HTTP prompts still fail in a normal repository without a local .codex/ directory. Codex has no project claudemd, and its hooks are installed globally, but installation detection probes .codex/skills; hookDeliveryProblem() therefore reports Codex as uninstalled and the command is negatively acknowledged. The new test masks this by explicitly creating .codex/skills.
  • [P1 blocking] src/uninstall.ts:462 — OpenCode uninstall treats every matching instructions entry as TeamAI-owned. If a user already had their own .opencode/teamai-context.md listed before TeamAI—an ownership case registerOpencodeContext() explicitly preserves—uninstall --agent opencode removes that user-owned entry. Track whether TeamAI added the entry or only schedule removal when the file contains TeamAI-managed blocks.

The other previously reported findings appear resolved. The PR description contains sufficient real-CLI testing evidence. No PR code was executed.

- A Codex project HTTP prompt is delivered when Codex is installed for
  the member: its hooks are user-level, so a project without .codex/
  still reaches it.
- Uninstall leaves OpenCode's instructions entry for a teamai-context.md
  that holds none of teamai's blocks: that file is the member's.
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/instruction-targets.ts:387 — Project-scoped Pi detection still probes <project>/.pi, although its instruction extension is installed globally under ~/.pi/agent/extensions. A normal Pi installation with ~/.pi but no repository-local .pi/ is omitted from hooks, so pull and doctor silently provide no project culture, shared instructions, or recall. Probe Pi through its user installation root, as already done for OMP and Codex.
  • [P1 blocking] src/uninstall.ts:462 — The round-7 OpenCode cleanup issue is reintroduced when a TeamAI-created context file still exists but its marker lines were manually removed. Such a file is absent from claudeMdFiles, so uninstall leaves the TeamAI-added instructions entry active and OpenCode continues loading the remaining generated text after uninstall --agent opencode. Ownership of the config entry needs tracking independent of the file’s current markers.

The other previously reported findings appear resolved. The PR description contains sufficient real-CLI testing evidence. No PR code was executed.

- Pi counts as installed for project instructions when ~/.pi exists:
  teamai installs its extension there, so a project needs no .pi/.
- teamai records the OpenCode instructions entry it adds, and uninstall
  removes a recorded entry even after the member stripped the markers
  from the context file.
…st main

- The unchanged-revision pull reclaims an earlier release's copy of a
  team rule named teamai-context before syncing the instructions.
- Uninstall removes only this checkout's recorded OpenCode entry, and
  forgets the entries it removed.
- Rule removal skips the teamai-context file that holds teamai's blocks,
  so a targeted uninstall keeps a shared instruction file.
- Codex is probed at the member's recorded tool root.
- The HTTP ack fails when OpenCode's config cannot list the prompt file.
- A prompt whose HTTP delivery fails leaves the cache as it was, so
  session hooks do not read it.
- The HTTP agent records state in the project's data home.
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/instruction-targets.ts:588 — Existing owned rule files never have their required header restored. If .cursor/rules/teamai-context.mdc or the CodeBuddy/WorkBuddy context file still contains TeamAI markers but loses its alwaysApply: true frontmatter, pull updates only the blocks; doctor also reports it current, although the tool may not load it automatically.
  • [P1 blocking] src/uninstall.ts:462 — OpenCode entry ownership is inferred from the file currently containing TeamAI blocks. If the user had already listed .opencode/teamai-context.md while the file was absent or empty, pull fills the file but does not record the pre-existing entry; uninstall then removes that user-owned configuration entry. Removal should rely on the ownership record, with an explicit migration path for legacy entries.
  • [P2 non-blocking] src/instruction-targets.ts:351 — Ownership is inferred solely from a basename beginning with teamai-context.. With a valid fallback configuration such as claude: { claudemd: ".claude/teamai-context.md" } and no rules, the documented member-owned fallback is instead treated as generated: an existing notes file blocks delivery and can later be deleted as an owned file. Ownership should come from selecting the generated rules target, not its filename.
  • [P2 non-blocking] src/pull.ts:1935 — Success messages depend on targets.length, not successful writes. When every target is foreign, malformed, or unwritable, pull emits warnings but still prints “Synced team culture/shared instructions,” falsely reporting delivery.

The previously reported findings appear resolved in the current diff. The PR description includes sufficient real-CLI testing evidence. No PR code was executed.

- Pull restores the alwaysApply header of teamai's own rule file when it
  was lost or changed, so doctor no longer reports it current.
- OpenCode's instructions entry is removed, by pull or uninstall, only
  when teamai recorded adding it.
- A configured claudemd named teamai-context (no rules) is the member's
  file, in pull and uninstall.
- Pull reports synced culture and instructions only when a target was
  reached.
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/instruction-targets.ts:478 — OpenCode registration accepts any existing TeamAI marker, even when the planned update was rejected or failed. If the file contains another member’s old blocks or malformed markers and cannot be rewritten, pull still adds it to opencode.json, activating stale instructions. Registration must require the file to already match the desired content or a successful write.
  • [P1 blocking] src/local-agent.ts:2208 — Removing the final HTTP claudemd resource cannot fail: once files.length === 0, write warnings/failures never trigger an exception. If the destination is malformed or unwritable, the server receives a success ACK and the manifest entry is deleted while the old prompt remains active with no retry path.
  • [P1 blocking] src/uninstall.ts:1413 — The OpenCode ownership record is discarded even when executeRemoval failed to update a writable-looking opencode.json. For example, a permissions error leaves the TeamAI-added instructions entry in place, but subsequent uninstalls no longer recognize it as TeamAI-owned and cannot retry its removal.

The previously reported findings appear resolved in the current diff. The PR description contains sufficient real-CLI/end-to-end testing evidence. No PR code was executed.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/pull.ts:1916 — Replacement writes and retired-file cleanup are executed in one best-effort plan. If a new target is foreign or unwritable, applyInstructionPlan records that failure but still removes the old working blocks. Likewise, hook targets are cleaned before their extension/plugin is successfully reconciled. An upgrade can therefore leave Claude, Pi, OMP, Hermes, or Codex with no instructions. Retired files must only be cleaned after replacement delivery is confirmed.
  • [P1 blocking] src/instruction-targets.ts:412 — Excluded hook-only tools are skipped without protecting their retired instruction files. For example, with Pi in disabledAgents, AGENTS.md is still classified as stale and cleaned even though no Pi extension is installed or updated. This violates the documented guarantee that excluded tools are neither written to nor deleted from.
  • [P1 blocking] src/local-agent.ts:2209 — Failures or malformed-marker warnings from retired-file cleanup are only logged and do not fail syncClaudemd. The server therefore receives a success ACK and the prompt cache/manifest is removed or advanced while the legacy prompt remains active, leaving no automatic retry path.
  • [P1 blocking] src/uninstall.ts:1414 — OpenCode ownership is preserved after a failed config update only when includeShared is false. If OpenCode is the last installed tool, a targeted or full uninstall deletes the shared data directory after a failed opencode.json update, losing the ownership record while the TeamAI-added entry remains; a subsequent uninstall cannot safely retry it.
  • [P1 blocking] src/uninstall.ts:367 — A project-scoped targeted uninstall schedules OMP’s and Pi’s single global extensions for deletion (Hermes follows the same pattern via hermesCleanup). Because this PR makes those extensions the sole project-instruction delivery channel, uninstalling the tool from project A disables team instructions and automatic pull for the same tool in every other configured project.

The other previously reported findings appear resolved. The PR description includes sufficient real-CLI/end-to-end testing evidence. No PR code was executed.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/pull.ts:1956 — Replacement success is tracked per file/tool rather than per block. If culture.md is temporarily unreadable during the first upgraded pull, the new target can still be marked reached after writing the claudemd/recall blocks; cleanup at line 2600 then removes the only valid culture block from the retired file. Do not retire a block unless that specific replacement was resolved and delivered.
  • [P1 blocking] src/local-agent.ts:2209 — HTTP synchronization passes only the current command’s reached tool to retiredFilesOfReached, although fullTeamConfig considers every installed former writer. With Pi and WorkBuddy installed, separate prompt commands reach [pi] and [workbuddy]; neither invocation ever satisfies both writers, so the legacy shared AGENTS.md block is never removed and cross-member instructions remain active.
  • [P1 blocking] src/uninstall.ts:672 — Project uninstall still detects remaining tools through project-local paths. After migration, Pi, OMP, Hermes, and project Codex can have no local tool directory because their delivery adapters are global. For example, uninstalling WorkBuddy from a project that still enables globally installed Pi treats WorkBuddy as the last tool, removes the project’s .teamai state, and leaves Pi’s global extension unable to deliver instructions there.

The previously reported findings appear resolved in the current diff. The PR description includes sufficient real-CLI/end-to-end testing evidence. No PR code was executed.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/uninstall.ts:745 — A project-scoped uninstall --agent codex still merges Codex’s user-level hook file into the removal plan. With two configured projects, uninstalling Codex from one removes the shared ~/.codex/hooks.json TeamAI hooks, so the other project loses its sole project-instruction delivery channel. Project uninstall must preserve the global Codex hook and only record the project exclusion, as already done for Pi/OMP/Hermes.
  • [P1 blocking] src/instruction-targets.ts:540 — OpenCode ownership is recorded only when opencode.json itself changes. If adding the entry succeeds but saving state.json fails, the retry sees the entry already present, returns changed === false, and never repairs the ownership record. A later uninstall deletes the generated context file but leaves the TeamAI-added instructions entry behind.
  • [P2 non-blocking] src/instruction-targets.ts:452 — Excluding a file-based tool protects only its current target, not its retired paths. Consequently, an excluded Claude installation with legacy blocks in .claude/CLAUDE.md leaves that file intentionally untouched during pull, but doctor still classifies it as stale and continually reports a failed delivery check.

The previously reported findings appear resolved in the current diff. The PR description includes sufficient real-CLI/end-to-end testing evidence. No PR code was executed.

…achine (Tencent#945)

A project uninstall kept the Pi and OMP extensions, the Hermes plugin,
Codex's user hooks and server-pushed agent hooks unconditionally, so that
other projects keep their delivery channel. On a machine with no user
scope and no other project, nothing removed them any more, unlike main:
every Pi, OMP, Hermes or Codex session kept running teamai hook-dispatch
after teamai was uninstalled.

A project uninstall now keeps them only while the user config or another
project partition exists, and the last install removes them.
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/uninstall.ts:203 — removesGlobalAdapters() recognizes only the user config.yaml and partitioned projects under ~/.teamai/projects. It misses HTTP-only installations and self/legacy projects whose config remains inside <project>/.teamai. Uninstalling one project can therefore remove the global Pi/OMP/Hermes/Codex adapters—and at src/uninstall.ts:1143 all server-pushed agent hooks—while another supported installation still depends on them.
  • [P2 non-blocking] src/pull.ts:2595 — During pull --dry-run, hook reconciliation intentionally writes nothing, but cleanup eligibility is checked against the currently installed hook rather than the hook that would be installed. With a missing or outdated Pi/OMP/Hermes/Codex adapter, the preview omits retired-file removals that the real pull performs, so dry-run does not accurately describe its destructive changes.

The previously reported findings appear resolved. The PR description includes sufficient real-CLI/end-to-end testing evidence. No PR code was executed.

…em (Tencent#945)

The previous commit removed the Pi/OMP extensions, the Hermes plugin,
Codex's user hooks and pushed agent hooks when no user config or other
project partition existed. That misses HTTP-only installs and self or
legacy projects, whose config stays inside <project>/.teamai and cannot
be enumerated, so one project's uninstall could cut another install off.

A project uninstall keeps them again and its summary names each one,
with `teamai hooks remove`, which removes them, as the step to run first
when no other install uses them.

pull --dry-run now previews the retired-file cleanup a real pull does
after installing a Pi, OMP, Hermes or Codex adapter, unless a member's
same-named file or a disabled Hermes plugin keeps that channel closed.
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/uninstall.ts:1360 — The empty-plan shortcut executes excludeUninstalledAgent() before the dry-run and confirmation checks. For example, teamai uninstall --agent pi --dry-run on a project with only the retained global adapter still modifies the project config by adding pi to disabledAgents; an interactive uninstall also makes this change without confirmation. Move this branch after the dry-run/confirmation gates.

The previously reported findings appear resolved. The PR description includes sufficient real-CLI/end-to-end testing evidence. No PR code was executed.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/hook-handlers.ts:782 — HTTP prompt delivery ignores the resolved project configuration and therefore ignores disabledAgents. For example, after teamai uninstall --agent pi records pi in disabledAgents while retaining its global extension, the extension still invokes this handler, which reads and injects the cached HTTP prompt; localAgentHandler can also download it again. This violates the documented guarantee that a later session-start hook will not resurrect an excluded tool’s resources.
  • [P1 blocking] src/instruction-targets.ts:253 — Hook-delivered instructions no longer suppress blocks still present in a legacy file the tool reads natively. If Pi and WorkBuddy previously shared AGENTS.md and WorkBuddy’s replacement is blocked, cleanup correctly retains the old file, but Pi’s working extension simultaneously injects the newly resolved blocks. Pi therefore receives both the stale shared member selection and the current one—the cross-member contamination this PR is intended to eliminate.
  • [P2 non-blocking] src/doctor-delivery.ts:1215 — The stale-file doctor check considers only planned changes and ignores planning warnings. A retired file with an incomplete or repeated TeamAI marker produces no change but does produce a warning, so doctor incorrectly reports “No team instruction blocks are left” even though pull cannot clean the file.

The previously reported findings appear resolved in the current diff. The PR description includes sufficient real-CLI/end-to-end testing evidence. No PR code was executed.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/instruction-targets.ts:253 — Hook suppression checks only hard-coded native files, not the configured retired claudemd. For example, Pi configured with claudemd: "CLAUDE.md" can retain an old or malformed TeamAI block there when cleanup fails; Pi still reads that file, while the hook injects the new member’s block because only AGENTS.md was inspected. This recreates the cross-member duplicate the PR is intended to prevent.
  • [P1 blocking] src/instruction-targets.ts:562 — Removing the final OpenCode instruction block from a TeamAI-created context file containing additional user text leaves the TeamAI-owned instructions entry registered. Once the block is removed, this early return mistakes the preserved file for a foreign file before consulting opencodeContextEntries; an HTTP uninstall then reports success and deletes its manifest/cache while OpenCode continues loading that file.

Previously reported findings appear resolved in the current diff. The PR description includes sufficient real-CLI/end-to-end testing evidence. No PR code was executed.

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 members with different roles overwrite each other's blocks in shared root AGENTS.md

2 participants