Before submitting
Area
apps/server
Steps to reproduce
Use only two disposable synthetic threads, thread-A and thread-B:
- Start T3 Code and create
thread-A and thread-B in one project.
- Dispatch a
thread.turn.start command to thread-A with a deterministic command ID such as cmd-synthetic-replay, a fresh message ID, and a short synthetic user message.
- Wait for the dispatch to be accepted and record the returned sequence
N.
- Dispatch a second
thread.turn.start using the same commandId: cmd-synthetic-replay, but target thread-B and use a different fresh message ID.
- Inspect the second HTTP response and the snapshots/events for both threads.
The same issue also applies more generally when a previously accepted command ID is reused with a different command payload on the same aggregate: receipt identity is currently the command ID alone, not the normalized command.
Expected behavior
T3 should never replay an accepted receipt as success for a command addressed to a different aggregate or carrying a different payload.
At minimum, reusing a command ID with a different aggregateKind/aggregateId should return a typed conflict. Ideally, the stored receipt should include a normalized-command fingerprint and reject any same-ID/different-command request.
For a genuine retry of the identical command, the response should make the replay explicit and return enough receipt metadata to verify it, for example:
{
"sequence": 123,
"replayed": true,
"aggregateKind": "thread",
"aggregateId": "thread-A"
}
Actual behavior
The second dispatch returns HTTP 200 with the first command's accepted sequence N. No message or turn is created on thread-B.
The persisted receipt remains associated with thread-A, but the caller receives no replay marker or aggregate identity and can therefore report that delivery to thread-B succeeded.
Current source explains the result:
OrchestrationEngine.ts looks up an existing receipt only by commandId and immediately returns its resultSequence when accepted. It does not compare the stored aggregate with the incoming command's aggregate.
002_OrchestrationCommandReceipts.ts makes command_id the sole primary key. Aggregate kind and ID are stored but not part of receipt identity.
- The dispatch HTTP response exposes only the sequence, so a client cannot distinguish a real same-command replay from a destination/payload collision.
This creates a false-success boundary for any external dispatcher that uses stable command IDs for exactly-once retries. A recovery or completion notification can be silently dropped when its destination changes, while the caller sees HTTP 200 and the old sequence.
Impact
Major degradation or frequent failure
This can indefinitely stall orchestration, completion notifications, or recovery flows and can make a cross-thread delivery attempt appear successful when nothing reached the intended thread. The durable receipt prevents an ordinary retry from repairing the delivery.
Version or commit
v0.0.32-nightly.20260802.980 / e60821f0e0d82a5d671ca3b94719c49d333921c8
The behavior is also present on current main at the same commit.
Environment
macOS, T3 Code Desktop nightly, local orchestration HTTP API. Provider/model independent; the failure occurs before provider execution.
Logs or stack traces
# Sanitized synthetic outcome
POST commandId=cmd-synthetic-replay aggregate=thread-A -> 200 {"sequence": N}
POST commandId=cmd-synthetic-replay aggregate=thread-B -> 200 {"sequence": N}
receipt command_id=cmd-synthetic-replay aggregate_id=thread-A status=accepted sequence=N
events on thread-B for cmd-synthetic-replay: 0
No private thread IDs, user messages, credentials, or provider data are involved in this reproduction.
Screenshots, recordings, or supporting files
No response.
Workaround
Clients can include destination aggregate and a durable semantic notification ID in their generated command IDs, then verify a command-correlated message/turn on the intended destination before reporting delivery.
That mitigates accidental cross-destination collisions but does not replace server-side validation: T3 should reject a reused command ID whose aggregate or normalized command differs from the stored receipt.
Before submitting
Area
apps/server
Steps to reproduce
Use only two disposable synthetic threads,
thread-Aandthread-B:thread-Aandthread-Bin one project.thread.turn.startcommand tothread-Awith a deterministic command ID such ascmd-synthetic-replay, a fresh message ID, and a short synthetic user message.N.thread.turn.startusing the samecommandId: cmd-synthetic-replay, but targetthread-Band use a different fresh message ID.The same issue also applies more generally when a previously accepted command ID is reused with a different command payload on the same aggregate: receipt identity is currently the command ID alone, not the normalized command.
Expected behavior
T3 should never replay an accepted receipt as success for a command addressed to a different aggregate or carrying a different payload.
At minimum, reusing a command ID with a different
aggregateKind/aggregateIdshould return a typed conflict. Ideally, the stored receipt should include a normalized-command fingerprint and reject any same-ID/different-command request.For a genuine retry of the identical command, the response should make the replay explicit and return enough receipt metadata to verify it, for example:
{ "sequence": 123, "replayed": true, "aggregateKind": "thread", "aggregateId": "thread-A" }Actual behavior
The second dispatch returns HTTP 200 with the first command's accepted sequence
N. No message or turn is created onthread-B.The persisted receipt remains associated with
thread-A, but the caller receives no replay marker or aggregate identity and can therefore report that delivery tothread-Bsucceeded.Current source explains the result:
OrchestrationEngine.tslooks up an existing receipt only bycommandIdand immediately returns itsresultSequencewhen accepted. It does not compare the stored aggregate with the incoming command's aggregate.002_OrchestrationCommandReceipts.tsmakescommand_idthe sole primary key. Aggregate kind and ID are stored but not part of receipt identity.This creates a false-success boundary for any external dispatcher that uses stable command IDs for exactly-once retries. A recovery or completion notification can be silently dropped when its destination changes, while the caller sees HTTP 200 and the old sequence.
Impact
Major degradation or frequent failure
This can indefinitely stall orchestration, completion notifications, or recovery flows and can make a cross-thread delivery attempt appear successful when nothing reached the intended thread. The durable receipt prevents an ordinary retry from repairing the delivery.
Version or commit
v0.0.32-nightly.20260802.980/e60821f0e0d82a5d671ca3b94719c49d333921c8The behavior is also present on current
mainat the same commit.Environment
macOS, T3 Code Desktop nightly, local orchestration HTTP API. Provider/model independent; the failure occurs before provider execution.
Logs or stack traces
No private thread IDs, user messages, credentials, or provider data are involved in this reproduction.
Screenshots, recordings, or supporting files
No response.
Workaround
Clients can include destination aggregate and a durable semantic notification ID in their generated command IDs, then verify a command-correlated message/turn on the intended destination before reporting delivery.
That mitigates accidental cross-destination collisions but does not replace server-side validation: T3 should reject a reused command ID whose aggregate or normalized command differs from the stored receipt.