Append-only. Every entry: ## <ISO date+time> — <Stage>: <one-line summary> then Decision / Evidence / Open question. Never rewrite history; if reversed, add a new entry referencing the old one.
- Decision: Rebuild TagTeam on opencode (model-agnostic) on branch
opencode. Drop Claude/Codex backends for now. No external admin or budget until a working POC exists — so no Entra/SSO, no Slack webhook, no K8s. Local-account auth (bcrypt) replaces SSO; SQLite (local file) replaces any managed DB. Boss agent orchestrates the build. - Evidence: Shash: "focus on opencode… I don't have the leverage to go buy new tools or get admin access until I have a working POC." Repo analysis: wire protocol, session model, invite lifecycle, turn engine, and web client are all agent-agnostic — only
server/agent/*+ a few UI strings are Claude-specific. - Open question: provider/model default — opencode Zen vs pinned Anthropic via opencode. (Deferred to M0 spike; Zen is the working assumption.)
- Decision: Use
@opencode-ai/sdkcreateOpencode()in-process (Approach B). One TagTeam session = one opencode session. Map opencode SSE events onto the existingassistant_delta/tool_activity/assistant_completeframes. Wire protocol, HTTP surface, sessions/turns/protocol modules, and web client stay;server/agent/*is replaced. - Evidence: Current design is single-process, no-build, in-memory — in-process SDK preserves that. opencode SDK exposes
session.prompt+event.subscribeSSE with text/tool parts that map cleanly to TagTeam frames. A separateopencode serve(Approach A) would add a second process and ops overhead for no benefit at this scale. - Open question: Does opencode's SSE actually emit per-token text deltas and paired tool start/end? M0 task 0.8 spikes this first.
- Decision: M1 ships local-account auth (bcrypt + httpOnly cookie,
userstable). The auth interface is provider-agnostic so Microsoft Entra / Google / Okta is a later drop-in PR, not a rewrite. Invite tokens bind to a user identity on redemption. - Evidence: Shash has no admin access yet. Local accounts give real identity + accountability with zero infra. The POC's display-name-only model is not company-safe (no accountability, trivial impersonation).
- Open question: Password policy + session-cookie expiry — security agent owns this in
docs/design/security.md.
- Decision: Use in-process
createOpencode({ port: 0 })— confirmed working end-to-end with providerbigmodel/glm-5.2. The adapter will create one opencode session per TagTeam session and pump the globalevent.subscribe()stream. - Evidence:
docs/implementation/spike-opencode-sse.md(confirmed by running, SDK@opencode-ai/sdk@1.18.18).scripts/spike-output.txtholds the raw event stream. - Corrections to M0.md §3 (architect must apply):
- SDK wraps responses as
{ data, request, response }— uses.data.id, nots.id. Same forsession.messages()/session.abort(). createOpencode({ port: 0 })returns{ client, server }— useclientfor calls,server.close()to tear down.- No discrete
tool_start/tool_endevent types. Tools aremessage.part.updatedwithpart.type:"tool"andpart.state.statustransitionspending → running → completed|error. Adapter synthesizestool_start/tool_end. message.part.delta(withfield:"text") is a runtime event type NOT in the SDK'sEventTS union — switch onev.typeat runtime; do not rely on the types.finish:"tool-calls"is NOT a stop — onlyfinish:"stop"ends the turn.- ~80 catalog/plugin events fire at session create — allowlist the ~6 event types we care about; ignore the rest.
- Provider retries (
session.statustype:"retry") are transient — do not treat as fatal.
- SDK wraps responses as
- Open question: Should
reasoningparts be surfaced astext_deltaor dropped? TagTeam's UI doesn't surface them separately. Recommend: route totext_delta(simplest, no UI change). Architect decides.