Skip to content

fix(discord-bot): stop posting the same Grok answer twice - #486

Draft
omegent-app[bot] wants to merge 445 commits into
fork/devfrom
fix/discord-no-duplicate-finals
Draft

omegent-app[bot] wants to merge 445 commits into
fork/devfrom
fix/discord-no-duplicate-finals

Conversation

@omegent-app

@omegent-app omegent-app Bot commented Sep 23, 2026

Copy link
Copy Markdown

Grok/ACP often opens a second assistant id with the same body. Discord treated that as a new bubble, posted another final, and the stats footer (37s · 149k vs 31s · 157k) meant accept-without-ack could not adopt the first message.

Drop later assistants whose text matches the already-posted final, collapse consecutive duplicate bubbles, and ignore stats/T3 footers when matching an already-landed Discord final.

grok-4.6 / Grok harness

opened by Patrick Roza in chat thread Discord · Stop echoing Grok runtime_info · T3 (.)

omegent-app Bot and others added 30 commits August 15, 2026 16:37
AGENTS.md is the single current fork workflow:

- change PRs target `fork/dev` and squash-merge
- update `main` from `upstream/main`, then classic-merge into `fork/dev`
- catching a change PR up is rebase or merge — pick one

Removed the historical stack/handover docs (`docs/fork-stack.md`,
`docs/fork-base.md`, `docs/stable-dev-release-branch-handover.md`) so
they cannot describe a past or future model.

Grok 4.6 / T3 Discord

opened by [patroza](https://discord.com/users/95218063095377920) in chat
thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1538189075251601469/1538189075251601469)
· [T3](https://t3vm/?thread=866be431-d52a-48b6-bb4b-cfcd2399d79c)

---------

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
#401 was squash-merged. That kept the code and threw the lineage away:
the 23 upstream commits stopped being ancestors, so `fork/dev` read as
**29 commits behind upstream when only 6 were genuinely outstanding**,
and the next sync would have re-merged and re-resolved all 23 — on the
same mobile files that took sixteen conflicts to land the first time.

Nobody noticed for two merges. It surfaced in a deploy alert that said
**"Commits (2)"** for a range that had carried 23.

## What this adds

A `push`-triggered check on `fork/dev` that fails when a commit which
carried a sync has fewer than two parents, and prints the `-s ours`
repair in the log.

Sync commits are identified by **the head branch of the PR they came
from**, not by their subject, because subjects vary by merge method:

```
Merge pull request #400 from patroza/sync/upstream-2026-08-12b
Merge upstream/main into fork/dev (23 commits) (#401)
merge: sync upstream through b73232b
```

A merge-button commit names the branch inline, so no API call is needed;
squash and rebase commits are resolved through the API, with the subject
line as a fallback when that is unavailable. **Ordinary fork PRs are
untouched** — they are expected to squash, and are never checked.

## Verified against the real commits

| commit | what it is | result |
|---|---|---|
| `5e63531b1` | #401, squash-merged sync | **fails**, exit 1 |
| `0bf7835cc` | #400, sync merged properly | recognised as a sync,
passes (2 parents) |
| `a76069bd9` | #402, an ordinary squashed PR | not flagged |

The third row is the one that matters most: the guard has to stay silent
on your normal workflow.

## This detects, it does not prevent

Worth being explicit, since it was the first question asked: **clicking
merge does not fail.** The check runs after the merge lands, because
GitHub has no per-PR merge-method control, and a repository-wide setting
cannot allow squash for ordinary fork PRs while requiring a merge commit
for syncs. Squash merges do fire `push` — `5e63531b1` triggered Fork CI
at 08:08 — so this turns a silent, weeks-later discovery into a red
check within a minute.

Actual prevention is `gh pr merge <n> --merge`, which is how #398, #399
and #400 all landed correctly. That is now written into the sync runbook
in `AGENTS.md`.

If you would rather it be enforced at the button, the next step is a
`sync:upstream` label workflow that merges the PR through the API once
checks pass — say the word and I will add it.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>

🤖 Generated with [Claude Code](https://claude.com/claude-code) via [T3
Chat](https://t3.chat) on Discord

---------

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
…PRs (#413)

Manual `/omegent today-recap` (alias `/agent today-recap`) starts a
recap thread for the repo bound to the calling Discord channel
(`t3-<shortName>` topic). No schedule — someone has to run it.

## Why
Daily recaps of PR opens/merges/closes (with Discord thread links) are
useful and cheap. Doing it as a slash command now lets us iterate on
format without a cron.

## What
- `/omegent today-recap` in a project channel (e.g. scanner) opens a new
thread and asks the agent for that repo's UTC-day recap
- Same from a child thread: recap still lands on the parent project
channel
- `@Omegent today-recap` / `today recap` as a mention fallback
- Prompt encodes the format we settled: what/why from PR descriptions,
`[PR #N](url)`, bare Discord thread URLs, `## 🟢 MERGED` / `## 🔴 CLOSED`
/ `## 🟠 OPEN`, `### fix` / `### feat` outline
- Recap turns run `--local` (no worktree)

## Test plan
- [x] `vp test run` todayRecap, slashCommands, channelInfoPin, mentions,
MentionRouter
- [x] `apps/discord-bot` typecheck
- [x] targeted lint
- [ ] After deploy: `/omegent today-recap` in the scanner channel

opened by [joshuadima](https://discord.com/users/593167616273809448) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1539325452873769041/1539325452873769041)
·
[T3](https://t3vm.tail86038f.ts.net/?thread=1c8e37cb-722d-405e-8071-5c8c3cd159fe)

---------

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Joshua Dimaunahan <170177550+MindfulLearner@users.noreply.github.com>
Merges 66 upstream commits into fork/dev. Notable weld points:

- ChatComposer: upstream pingdotgg#7150 relocated the prompt editor, length validation
  and footer, leaving the fork's copies as a second live layout. Ported the
  fork-only pieces into upstream's copies first — the environment-unavailable
  placeholder, the model picker's usage snapshot, and the attach-images button
  that drives the hidden iOS file input — then dropped the stale layout.
- MessagesTimeline: upstream's new active-turn block indexes whatever it
  iterates; the fork walks its collapsed/interleaved list, so the block was
  rebased onto that list to keep the indices referring to one array.
- DesktopUpdates: upstream's action reservation and widened installable check,
  kept compatible with the fork's Linux dir-install mode.
- CommandPalette: dropped the `open` prop upstream removed while keeping the
  fork's file-picker / content-search overlays, and took upstream's newer
  reduceCommandPaletteUiState over the fork's relocated copy of the old one.
- mobile-showcase-screenshots: GitHub-hosted runners (this fork has no
  Blacksmith) with upstream's palette-driven timeout.
- ProviderRegistry.test: kept the fork's coverage for mergeProviderSnapshots /
  selectProvidersByKind, which upstream deleted while leaving both exported.

Upstream's new t3code/no-native-title-tooltip rule flagged seven fork-only
components; four had a title duplicating an existing aria-label, three needed
the label moved off the native tooltip.

rerere was disabled for this merge: it had replayed earlier resolutions onto
new upstream content, which is exactly how a weld goes silently wrong.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
merge: sync upstream through f708f63 (66 commits)
…one (#417)

Thread search restricted assistant matches to the single row a turn
names as its
terminal message:

```sql
messages.message_id IN (
  SELECT turns.assistant_message_id FROM projection_turns AS turns
  WHERE turns.assistant_message_id IS NOT NULL
)
```

Providers stream an answer as many assistant items — roughly ten per
turn now,
and climbing as chunking gets finer — and only the last is linked as
`turns.assistant_message_id`. Everything said before it was invisible to
search.
That is why full-text search appeared to "stop working" without anything
visibly changing: the query never changed, the shape of the data under
it did.

## Measured on a live `state.sqlite`

| | searchable |
|---|---|
| before | 7,118 / 20,094 settled messages (35%) |
| after | 20,094 (100%) |
| assistant messages before | 3,430 / 16,406 (21%) |

A real query for `worktree` goes from **138 to 187 threads**.

All 425 threads with assistant text had *some* searchable row, so the
symptom
was a search quietly missing most of what was said, not one missing
whole
threads — which is exactly why it was hard to pin down.

## Scope

- **Tool calls were never involved.** They are activities in
`projection_thread_activities`, not rows in
`projection_thread_messages`, so
  they stay out by construction.
- **`system` notices** are now excluded explicitly rather than
incidentally.
- **Per-thread dedupe is unchanged**: `thread_match_rank = 1` still
collapses a
  thread to one best row, preferring a user match, then the most recent.
- **Streaming rows stay excluded** via the existing `is_streaming = 0`.

## Known limit

A phrase that straddles a split boundary still will not match — each
message row
is matched independently, so "the quick brown" in one item and "fox" in
the next
is two rows, not one string. Fixing that means matching against a
per-turn
concatenation, which is a bigger change with its own cost; happy to do
it as a
follow-up if it matters in practice.

## Tests

The existing test asserted the old behaviour outright — its fixture
literally
read `'Interim needle must not be searchable.'` — so that expectation is
inverted here, and a split-answer case is added whose matching half is
not the
turn's `assistant_message_id`. Mutation-checked: restoring the old
restriction
fails both new assertions.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>

---------

Signed-off-by: aoright <102943475+aoright@users.noreply.github.com>
Co-authored-by: Rodrigo Brechard <rodrigobrechard@gmail.com>
Co-authored-by: Rodrigo Brechard <rodrigo@clubtidy.fr>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Bilal Bakr <62337003+Bil0000@users.noreply.github.com>
Co-authored-by: Eddy Naboulet <93473191+eddy-naboulet@users.noreply.github.com>
Co-authored-by: maria <maria@kuuro.net>
Co-authored-by: Wout Stiens <71498452+StiensWout@users.noreply.github.com>
Co-authored-by: Pavlo Trinko <paul.trinko95@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com>
Co-authored-by: Chris Deeming <chris@xenforo.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Francisco Arredondo <95440147+frarredondo@users.noreply.github.com>
Co-authored-by: Shivam Sharma <91240327+shivamhwp@users.noreply.github.com>
Co-authored-by: Nitay Rabinovich <nitayr@wix.com>
Co-authored-by: Lars Nieuwenhuis <35393046+lnieuwenhuis@users.noreply.github.com>
Co-authored-by: Tristan Knight <admin@snappeh.com>
Co-authored-by: Nick Anisimov <n.anisimov.23@gmail.com>
Co-authored-by: Maslin Edwin <maslinje@gmail.com>
Co-authored-by: aoright <102943475+aoright@users.noreply.github.com>
Co-authored-by: Guilherme Barros <gbarros1095@gmail.com>
Co-authored-by: Rishet11 <154429365+Rishet11@users.noreply.github.com>
Co-authored-by: Augie <augie@luebbers.email>
Co-authored-by: Taras <Taras.Fomin@gmail.com>
Co-authored-by: Gianmarco <gianmarcosimone89@gmail.com>
Co-authored-by: Theo Browne <me@t3.gg>
Co-authored-by: Inaya Yousfi <zied.essaber@gmail.com>
Co-authored-by: maria <254055478+maria-rcks@users.noreply.github.com>
Co-authored-by: Rakshith Bhat <88523594+RakshithBhat03@users.noreply.github.com>
Co-authored-by: GPT-5.6 <noreply@openai.com>
Co-authored-by: David Balderston <dbalders@gmail.com>
Co-authored-by: Dara Adedeji <76637177+SunkenInTime@users.noreply.github.com>
Co-authored-by: Luis Gustavo Couto Wacker <luis.wacker@pagar.me>
Co-authored-by: Jake Leventhal <jakeleventhal@me.com>
Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
## Why

Sentry short ids (`SCANNER-313`) have the same shape as Jira keys
(`PROJ-123`). The Discord bot extracted those tokens from sentry.io URLs
and Sentry alert embeds, pinned them under **Jira**, and injected `jira:
SCANNER-313` into agent turns — so the agent treated an obvious Sentry
issue as a Jira ticket.

## What

- Do not treat sentry.io URLs, Sentry-bot authors, or Sentry Discord
embeds as Jira. Atlassian browse / `selectedIssue` URLs are still
extracted.
- Drop already-stored false positives on pin refresh and backfill (and
skip mining our own Omegent Info pin, which echoed the misclassified
key).
- Persist sentry.io issue URLs and render them as **Sentry** on the
thread-info pin (above Jira).

## Test plan

- [x] `vp test run` jiraLinks, sentryLinks, threadInfoPin,
ThreadLinkStore, threadContext, MentionRouter
- [x] `apps/discord-bot` typecheck
- [ ] After deploy: Sentry alert / pasted
`https://*.sentry.io/issues/SCANNER-313` should pin **Sentry**, not
**Jira**

opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** · [what happened
here?](https://discord.com/channels/1083767712431480922)

---------

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
…#414)

## Summary

Omegent on t3vm failed with `T3 did not become ready after a server
restart` because the Discord bot re-exchanges
`local-bootstrap-credential` on every process start, and that grant was
seeded with the **desktop 24h TTL**. After `t3code-server` had been up
more than a day, a bot-only restart (or a deploy that restarted only the
bot) looped `invalid_credential` forever.

- Keep the desktop IPC bootstrap seed at 24h.
- Seed the file-backed local grant with a long-lived TTL (colocated
trusted clients).
- Persist the 30-day bearer under the bot data dir and reuse it on the
next start; clear and re-bootstrap if T3 rejects it.
- Also include basename asar unpack globs (`*.node` as well as
`**/*.node`) so `packWindowsServerAsar` still creates
`server.asar.unpacked` on hosts where `@electron/asar` 3.4 does not
treat `**` as nested.

## Test plan

- [x] `PairingGrantStore` local-file grant still consumes after 25h and
400d
- [x] Desktop IPC grant still expires after 24h
- [x] Persisted bearer reuse / host mismatch / remaining-time floor /
disk round-trip
- [x] T3Session existence contract for persist + invalid_credential
fallback
- [x] `build-desktop-artifact` Windows asar unpack tests
- t3vm: rebuilt dist, restarted server then bot; new Discord bot session
issued 2026-08-20 06:49 UTC and HTTPS `/` is 200 again

---------

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
…ap (#418)

`/omegent today-recap` posted the ack twice (channel starter + slash
reply) and opened a thread with no recap in it until/unless the agent
finished — so the thread looked empty.

## What
- Open one public thread with no starter message
- Slash ack is ephemeral (jump only)
- Recap is the only public bot message, inside that thread
- Mention-in-channel still threads off the mention; mention-in-thread
only posts a jump, not a second ack

opened by [joshuadima](https://discord.com/users/593167616273809448) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1539325452873769041/1539325452873769041)
·
[T3](https://t3vm.tail86038f.ts.net/?thread=1c8e37cb-722d-405e-8071-5c8c3cd159fe)

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Joshua Dimaunahan <170177550+MindfulLearner@users.noreply.github.com>
Working + Stop stays stuck above human chat when a later Tasks post is
the channel tip, and heartbeat refused to hop during tool-only phases.

Scan recent messages for humans after Working (not just the latest
message). Hop by creating the replacement Working bubble first, then
deleting the original so the live tip actually moves below the humans —
no frozen copy left above.

opened by [patroza](https://discord.com/users/95218063095377920) in chat
thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1540285630255464518/1540285630255464518)
·
[T3](https://t3vm.tail86038f.ts.net/?thread=0b72e901-c83f-42cc-9e69-f51de4531c88)

---------

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
…ad (#422)

`/omegent today-recap` opened a new Discord thread and posted nothing
into it until the turn finished, so the thread showed Discord's
empty-state copy.

## What
- Do not create a Discord thread
- Public deferred slash reply in the channel you ran the command in
- Recap edits that one message (extra chunks if over 2000 chars)
- Mention path replies in the same channel

opened by [joshuadima](https://discord.com/users/593167616273809448) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1539325452873769041/1539325452873769041)
·
[T3](https://t3vm.tail86038f.ts.net/?thread=1c8e37cb-722d-405e-8071-5c8c3cd159fe)

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Joshua Dimaunahan <170177550+MindfulLearner@users.noreply.github.com>
Classic-merge 121 upstream commits (pingdotgg/t3code through 06de9e9) into fork/dev so the deploy branch keeps upstream as a second parent.
… weld

Keep queue-bound mobile sends from scrolling, accept Codex localMessages on
the steered feed, land inactive pins in the settled recency section (pingdotgg#7969),
and keep send-disabled covering both feedback uploads and loading messages.
Windows workspace paths must go through classifyMarkdownImageSource so
relative and drive-letter forms keep their signed-asset paths. The fork
normalizer only covers Codex `attachment:` / generated_images URIs the
shared classifier blocks.
Keep clearing the session before provider interrupt, then still record a
failed interrupt unless a newer ready snapshot won the race. Tests now
expect migrations 041/042, Claude /compact, ACP runtime item ids, and
bootstrap thread.delete on worktree failure.
The new upload-queue tests mocked `@t3tools/client-runtime/state/runtime`
without spreading the real module. Under the fork's isolate:false unit
project that incomplete mock leaked into PullRequestListFilters.
merge: sync upstream through 06de9e9 (121 commits)
## Why

Fork CI on the #423 merge SHA (`1b3f2f52e`) failed its **Test** job
while Check / Release Smoke / Mobile Native / Upstream Lineage Guard
were green.

The only failure was the pingdotgg#8006 perf assertion:

`session-logic.test.ts > session activity performance > updates 20,000
ordered tool activities within 100 ms`

`AssertionError: expected 139.01 to be less than 100`

That merge tree is identical to the last green PR tip (`2efc0552e`).
Same code, noisier GitHub-hosted runner.

## What

- Skip the copy+sort in `deriveWorkLogEntries` when activities are
already ordered (the streaming-append path ChatView actually hits).
- Widen the budget from 100ms to 250ms. That still fails a quadratic
rebuild; 100ms was too tight for this runner class.

## Test plan

- [x] `vp test run apps/web/src/session-logic.test.ts -t "orders work
log by activity sequence|session activity performance"`
- [ ] Fork CI Test on this PR / then on `fork/dev` after merge

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Classic-merge 11 upstream commits (pingdotgg/t3code through a3a8cbd) into fork/dev so the deploy branch keeps upstream as a second parent.
ProjectionThread gained required unsettledAt from upstream 043; the fork
fixture used by WorktreeLifecycle tests still built rows without it.
The last-N assertions on the independent upstream ledger still ended at
042. 043_ProjectionThreadsUnsettledAt is a true upstream migration.
Those tests mock useAssetUrlState. Under isolate:false ChatMarkdown is
already bound to the real asset URL atom, so useAtomValue() is null and
the signed-URL cases throw on `_tag`.
The suite mocks ~/localApi. Under isolate:false the module is already
bound, so confirm never hits the mock and pending-state assertions fail.
merge: sync upstream through a3a8cbd (11 commits)
## Problem
`.githooks/post-checkout` deletes `apps/*/dist` on every branch
checkout. The production server serves `apps/server/dist/client` live
from disk, so a Discord-only deploy's `git switch` immediately returns
**Web assets unavailable** (HTTP 503) until something rebuilds the tree.

## Fix
Skip `apps/server/dist` in the emit wipe. Still clear other
`apps/*/dist`, `dist-electron`, `packages/*/dist`, and `*.tsbuildinfo`.

## Test plan
- [x] Harness: server dist preserved; web/package dist removed
- [ ] After merge + deploy: Discord-only promote no longer 503s
`https://t3vm.tail86038f.ts.net/`

opened by
[andreasimonecosta](https://discord.com/users/446049435810791424) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1536393400130076773/1536393400130076773)
·
[T3](https://t3vm.tail86038f.ts.net/?thread=28d099c3-603c-4659-bdac-b87fb8d03261)

---------

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Andrea Simone Costa <24520167+jfet97@users.noreply.github.com>
Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
… status (#427)

## Why

`/omegent today-recap` looked stuck on Discord’s deferred “ci sto
lavorando…” spinner because the slash only edited the thinking message
after the T3 recap turn finished.

The recap that did land was hard to read: jargon, `###` type headings,
Discord’s 2000-char split cut [PR
pingdotgg#1880](https://github.com/macs-holding/scanner/pull/1880) in half, and
related PRs (1880 merged + 2273 open follow-up) repeated the same
Packmittel story twice.

## What

- Edit the deferred slash reply immediately with `Writing today's recap
of \`<repo>\` (YYYY-MM-DD UTC)…`.
- Log recap start, T3 thread id, settle, timeout, and working-status
webhook failures.
- Treat `latestTurn.state === "error"` as terminal.
- Recap prompt: `(fix)` / `(feat)` on the PR line, plain-language
what/why, spell out shop-floor terms.
- Related PRs (same change, follow-up, or a first try closed because a
later PR handled it) are one history block; do not list those PRs again.
Unrelated closed PRs stay in CLOSED.
- Split follow-up Discord messages on blocks, not mid-sentence.

## Test plan

- [ ] `/omegent today-recap` replaces “thinking” with the working status
within a second, then the recap.
- [ ] Recap uses `(fix)` / `(feat)` next to each heading `PR #N`.
- [ ] Related PRs appear once (history under the latest status);
unrelated closed PRs only under CLOSED.
- [ ] A recap longer than 2000 characters does not split a block in
half.
- [ ] Bot journal shows `today-recap starting T3 turn` / `today-recap T3
thread started` / `today-recap T3 turn settled`.

Scanner tickets referenced in the recap format discussion:
[SA-437](https://macs-holding.atlassian.net/browse/SA-437) ·
[SA-449](https://macs-holding.atlassian.net/browse/SA-449) ·
[SA-438](https://macs-holding.atlassian.net/browse/SA-438)

opened by [joshuadima](https://discord.com/users/593167616273809448) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1539325452873769041/1539325452873769041)
·
[T3](https://t3vm.tail86038f.ts.net/?thread=1c8e37cb-722d-405e-8071-5c8c3cd159fe)

---------

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Joshua Dimaunahan <170177550+MindfulLearner@users.noreply.github.com>
Discord turns inject linked Jira keys as context. Agents treated that as
a request to post completion summaries on the ticket (example:
unsolicited Omegent comment on
[SA-438](https://macs-holding.atlassian.net/browse/SA-438), now
deleted).

Reply only on the originating surface unless the user explicitly asked.
Opening a GitHub PR for landable work is still allowed; commenting on
Jira/GitHub/Confluence is not.

## Test plan
- [x] `vp test run` threadContext + T3AgentRules
- [x] changed-file `vp check`
- [ ] After deploy: a Discord turn that mentions a Jira key must not
comment on the issue unless asked

opened by [enricopolanski](https://discord.com/users/147977704522645504)
in chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1542173337164447804/1542173337164447804)

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Enrico Polanski <16064771+enricopolanski@users.noreply.github.com>
Classic-merge pingdotgg/t3code main into fork/dev (4 commits).

3-way weld where Grok work overlapped: keep fork Kimi usage, Direnv,
session-mode pinning, and mid-session set_model; take upstream Grok
Build transcript scanning, permission-mode spawn args, skills, and ACP
reliability.
merge: sync upstream through ead4ce5 (4 commits)
Related follow-ups stay inside the latest PR's paragraph. Drop "landed today".

Co-authored-by: Joshua Dimaunahan <170177550+MindfulLearner@users.noreply.github.com>
github-actions Bot and others added 26 commits September 18, 2026 11:23
Replay bootstrap now updates the worktree path before the duplicate
thread.create.

Made-with: Grok 4.6 (xhigh) in T3 Code
PullRequestDetailPanel always mounts the checks popover, and the
summary tab mounts the reaction picker on comments. react-test-renderer
has no Element/window, so CI failed 29 tests with floating-ui errors.
Isolate the files so the mocks cannot be stolen under isolate:false.
sync: classic-merge upstream/main (56a9bf2) into fork/dev
Upstream pingdotgg#12326 committed the placeholder `set this to true or false`,
which Schema.Boolean rejects. Desktop artifact publish then failed on
every 30s poller retry and Discord treated each timestamped journal
line as a new failure. Keep the native addon allowed to build.
sync: classic-merge upstream/main (52e4b44) into fork/dev
)

After the Effect 4.0.0-rc.115 weld, Omegent looks healthy (REST slash
registration, rehydrate) but **never receives Discord gateway events**.

`dfx` 1.0.15 still does `writeRaw = yield* socket.writer` then
`writeRaw(...)`. On rc.115 that throws `TypeError: writeRaw is not a
function` (`DiscordWS.ts:75`), shard count stays 0, and mentions/slash
go nowhere.

Bump catalog `dfx` **1.0.15 → 1.0.16** (`writer.write` + `reader.pull`).
Test asserts the installed dfx is 1.0.16+.

Guest evidence on `t3code-discord-bot` at `d16d055996`: error at
10:27:14 UTC, then zero `MESSAGE_CREATE` while archived-thread REST
noise continues.

Restart-only will not fix this. After merge the poller will deploy
discord.

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Fork CI on `b4ad10f8` (dfx 1.0.16) failed twice on all 11 `BoardCard`
tests:

`No \"environmentServerConfigsAtom\" export is defined on the
\"../../state/server\" mock`

BoardCard now opens PRs through `useOpenPrLink` → that atom. Under
`isolate: false`, `UsagePage.refresh.test.tsx` binds a stub
`state/server` mock first and poisons BoardCard. That red push job
blocks the poller from deploying the Discord gateway fix.

Isolate BoardCard and the stub mock so they cannot share a module
registry.

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
…3c149

# Conflicts:
#	.agents/skills/test-t3-app/SKILL.md
#	.agents/skills/test-t3-mobile/SKILL.md
#	.github/VOUCHED.td
#	apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx
#	apps/mobile/src/features/home/HomeHeader.tsx
#	apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
#	apps/mobile/src/features/threads/ThreadRouteScreen.tsx
#	apps/mobile/src/features/threads/thread-list-items.tsx
#	apps/mobile/src/features/threads/thread-list-v2-items.tsx
#	apps/mobile/src/native/StackHeader.tsx
#	apps/server/src/mcp/PreviewAutomationBroker.ts
#	apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
#	apps/server/src/persistence/Layers/ProjectionThreads.ts
#	apps/server/src/persistence/Services/ProjectionThreads.ts
#	apps/server/src/pullRequest/PullRequestService.ts
#	apps/server/src/server.test.ts
#	apps/server/src/textGeneration/TextGeneration.ts
#	apps/server/src/vcs/GitVcsDriver.test.ts
#	apps/server/src/vcs/VcsProcess.ts
#	apps/server/src/ws.ts
#	apps/web/src/components/BranchToolbar.tsx
#	apps/web/src/components/GitActionsControl.tsx
#	apps/web/src/components/ProjectScriptsControl.tsx
#	apps/web/src/components/Sidebar.tsx
#	apps/web/src/components/chat/ChatHeader.tsx
#	apps/web/src/components/chat/ComposerBannerStack.tsx
#	apps/web/src/components/chat/MessagesTimeline.tsx
#	apps/web/src/components/chat/OpenInPicker.tsx
#	apps/web/src/hooks/useThreadActions.ts
#	packages/contracts/src/ipc.ts
#	packages/contracts/src/providerRuntime.ts
#	packages/contracts/src/vcs.ts
#	pnpm-workspace.yaml
ThreadStatusPresentation only has StatusTone + kind/pulse. The weld
left iconColor/iconBackground on plan-ready and typecheck failed.
Fork ElectronApp.Service only exposes setAsDefaultProtocolClient.
Upstream's harness field does not exist on the service type.
Schema mapFields is a value; use its Type for row decode. Fork ledger
tests still called removed NodeSqliteClient.layerMemory — use
layer({ filename: ':memory:' }) like upstream migration tests.
…oser banners

Archive undo tests hit previewWorktreeCleanup without a mock; optional-chain
the tagged result so a missing preview degrades to a plain archive.
Isolate composer banner tests so a stub Button mock cannot steal buttonVariants.
sync: classic-merge upstream/main (1de563c) into fork/dev
…7b73a

# Conflicts:
#	apps/mobile/app.config.ts
#	apps/mobile/src/Stack.tsx
#	apps/mobile/src/features/home/HomeHeader.android.tsx
#	apps/mobile/src/features/home/HomeHeader.tsx
#	apps/mobile/src/features/home/HomeHeader.types.ts
#	apps/mobile/src/features/home/HomeRouteScreen.tsx
#	apps/mobile/src/features/home/HomeScreen.tsx
#	apps/mobile/src/features/home/home-list-filter-menu.test.ts
#	apps/mobile/src/features/home/home-list-filter-menu.ts
#	apps/mobile/src/features/home/home-list-options.test.ts
#	apps/mobile/src/features/home/home-list-options.ts
#	apps/mobile/src/features/home/homeListItems.test.ts
#	apps/mobile/src/features/home/homeListItems.ts
#	apps/mobile/src/features/home/homeThreadList.ts
#	apps/mobile/src/features/threads/ThreadDetailScreen.tsx
#	apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
#	apps/mobile/src/features/threads/ThreadRouteScreen.tsx
#	apps/mobile/src/features/threads/thread-list-items.tsx
#	apps/mobile/src/features/threads/threadListV2.ts
#	apps/mobile/src/features/threads/threadPresentation.ts
#	apps/mobile/src/persistence/mobile-preferences.ts
#	apps/server/src/ws.ts
#	apps/web/src/components/BranchToolbarBranchSelector.tsx
#	apps/web/src/components/CommandPalette.tsx
#	apps/web/src/components/LegacySidebar.tsx
#	apps/web/src/components/ProjectScriptsControl.tsx
#	apps/web/src/components/Sidebar.tsx
#	apps/web/src/components/ThreadStatusIndicators.tsx
#	apps/web/src/components/chat/MessagesTimeline.tsx
#	apps/web/src/components/chat/OpenInPicker.tsx
#	apps/web/src/components/chat/ProviderStatusBanner.tsx
#	apps/web/src/components/chat/ThreadErrorBanner.tsx
#	apps/web/src/components/ui/scroll-area.tsx
#	packages/contracts/src/settings.test.ts
#	packages/contracts/src/settings.ts
#	pnpm-lock.yaml
PopoverPopup dropped viewportClassName; Button size owns padding/type.
Full vp check lints the whole tree. Fork board/jump/identity/host
status still restyled SidebarInset, Spinner, TooltipPopup, DialogPanel.
HomeHeader uses title=\"Ownership\" on NativeHeaderToolbar.Menu, not
the object form title: \"Ownership\".
Incoming t3.json submodule-init setting matches \"work\" and must stay
in the catalog list.
sync: classic-merge upstream/main (829af7b) into fork/dev
Grok extra ACP prompt parts (`<runtime_info>`, `<pull_request_linking>`)
were persisted as user-role text. The Discord bridge treated that as
t3-client input and posted the raw harness instructions as `💭 from
**unknown@unknown**`.

Classify those envelopes as internal scaffolding so they are not
mirrored, and strip them from echoed text as a fallback.

Pushing this branch required the current `fork/dev` typecheck unblocks:
ChromiumCookies yield* union, updates harness feed types,
HostPowerMonitor / NativeTelemetryClient Option.match.

grok-4.6 / Grok harness

opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1552207449409200192/1552207449409200192)
· [T3](https://t3vm/?thread=02e592b7-34fe-4b0f-bf3f-651f0883c29f)
[(.)](t3code://t3vm/?thread=02e592b7-34fe-4b0f-bf3f-651f0883c29f)

---------

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
## Summary

GitHub, Discord, and Teams refuse anyone who is not on the identity map.
They cannot start, continue, stop, or approve a T3 thread.

Jira uses the same gate for the agent. An unmapped Jira account can
still leave a context note on a thread that is already linked. That note
does not start or continue the agent.

Teams people are matched by `teamsAadObjectId` (Azure AD object id /
Graph `from.user.id`) or `teamsUserId` (Bot Framework `29:…` id). An
empty map denies everyone.

## Test plan

- [x] Identity map, Teams actor, and Jira trust tests
- [x] Changed-file check and typecheck on the first push
- [ ] After deploy, an unmapped Teams user cannot open or continue a
thread; an unmapped Jira user can leave a context note on an
already-linked thread and cannot run the agent

grok-4.7 / Grok harness

---------

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
)

## Problem

Browser automation broke in the desktop app. `preview_status` and
`preview_open` succeeded, but `preview_snapshot`, `preview_navigate`,
`preview_evaluate`, `preview_wait_for` (and
click/type/press/scroll/color scheme/recording) failed with
`PreviewAutomationExecutionError`. Desktop traces show the actual cause:

```
PreviewTabNotFoundError: Preview tab not found: tab_1
PreviewWebviewNotInitializedError: Preview tab "tab_1" has no webview registered
```

## Root cause

Upstream moved desktop preview tabs to a runtime identity:
`previewRuntimeTabId(threadRef, serverEpoch, tabId)`. An upstream merge
into the fork kept a stale fork copy of `PreviewAutomationHosts.tsx`. In
that copy, `status` and the overlay-ready wait use the runtime id, but
every other bridge call still passes the bare server id (`tab_1`). So
status reports a healthy tab while everything else misses it in the
desktop `PreviewManager`.

This is unrelated to the Discord browser host. The failing client was
the desktop renderer host (`preview-…`), not `discord-browser-*`.

## Fix

- Restore `PreviewAutomationHosts.tsx` from the last merged upstream
commit (`829af7b73a`). That brings back runtime ids everywhere, the
viewport rollback, the presentation settle, and the shared
`waitForNavigationReadiness`.
- Keep the one intentional fork change: `resolveNavigableUrl` for
`open`/`navigate`.
- I checked every other file that uses `runtimeTabId`; none diverge from
upstream.

## Tests

The new test `PreviewAutomationHosts desktop operations` checks that a
`snapshot` request reaches the desktop bridge with the runtime tab id.
It fails on the current `fork/dev` file and passes with this change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#484)

Fork CI on `ce3ef6b4` (#483) failed all four `citation comment source
disappearance` tests in `AssistantCitationChip.test.tsx` with
`TypeError: Cannot read properties of null (reading 'isServer')` from
the real TanStack `Link`.

The file mocks `@tanstack/react-router`, but in the `unit` project
(`isolate: false`) a sibling file can bind the real module first. The
file passes on its own, and the failure depends on test order, the same
hazard already documented for the other entries in
`isolatedUnitTestFiles`. This PR moves the file into the isolated
project.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tream pingdotgg#10982) (#485)

Imports the open upstream PR pingdotgg#10982 (by @Mnigos), which
fixes the accepted upstream bug pingdotgg#10980: an agent's
`preview_click` leaves keyboard focus inside the preview page, so the
user's typing and pastes go to the page, even for hidden `open: false`
tabs.

## Provenance
- Source: pingdotgg#10982, commits `5d24187dd3`, `b577a8380f` and
`a2e44cd6c2`, squashed into one commit.
- Imported unchanged: `apps/desktop/src/preview/Manager.ts` and
`Manager.test.ts`. The test file needed a 3-way merge but no changes.
- Nothing adapted or excluded. It adds no migrations.

## Change
The click path now saves and restores the previously focused WebContents
through a `restoreFocusedWebContents` helper it shares with
`preview_press`. The restore runs whether the click succeeds or fails.
It does nothing if focus moved to another renderer during the action or
the user switched to another app.

## Tests
Upstream's new Manager tests are included. All 93 tests in
`src/preview/Manager.test.ts` pass locally.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mnigos <makowskiigor@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Grok/ACP often opens a second assistant id with the same body. Discord
treated that as a new bubble, finalized again, and the stats footer
changed so accept-without-ack could not adopt the first post.

Drop later assistants whose text matches the already-posted final,
collapse consecutive duplicate bubbles, and ignore stats/T3 footers
when matching an already-landed Discord final.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
@patroza
patroza force-pushed the fork/dev branch 4 times, most recently from 80434dc to fc6701e Compare October 3, 2026 10:00

This branch has not been deployed

No deployments
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