Authoritative semantic contract for conversation/context participation in AI-provider translation. Conversation history represents accepted semantic translations, not provider transport activity.
-
Owner(s):
TranslationSessionManager,AIConversationHelper,ConversationAcceptanceCoordinator,OptimizedJsonHandler, and feature acceptance boundaries. -
Scope: conversation participation, logical-parent turns, candidate lifecycle, and final acceptance for AI translation.
-
Status: semantic foundation for P8 implementation phases.
-
Owner of storage:
TranslationSessionManager(in-memory session map, TTL 30 min, max 50, LRU evict).AIConversationHelperreads/writes through it and does not touch browser storage.
Conversation history exists to provide accepted translation context, including linguistic/style continuity for eligible Select Element AI translations. It is separate from provider-call success.
A provider call may succeed without any conversation commit. A provider completion, provider batch, retry, failover, or recovery pass is not itself a conversation turn.
For structured Select Element translation, one logical parent is the semantic translation unit. Transport fragmentation is transparent to conversation semantics.
| Term | Definition |
|---|---|
| ConversationSession | Extension-managed in-memory conversation namespace. Owned by TranslationSessionManager; not provider-native state. |
| ConversationTurn | One finally accepted semantic translation context. For Select Element structured AI translation, one turn represents one accepted logical parent. |
| ConversationCandidate | Provisional parent-scoped source/result contribution used by non-migrated provider paths. It is not history. |
| ConversationAcceptanceHandoff | Immutable Background-to-Content parent identity and clean-source handoff. |
| ConversationAcceptanceHandle | Private mutable lifecycle state for one request's logical parents. |
| ConversationAcceptanceCoordinator | Background owner of ACK routing, ordered commits, timeout, idempotency, and lifecycle cleanup. |
| HistoryRead | Projection of committed conversation turns into an eligible provider prompt. Owned by AIConversationHelper. |
| HistoryWrite | Creation of provisional semantic history data. It is not durable until commit. |
| HistoryCommit | Irreversible append of one accepted ConversationTurn to session history. |
| HistoryDiscard | Removal of provisional candidate without changing committed history. |
| LogicalParent | Semantic translation unit, such as one Select Element BlockGroup. It may contain multiple transport fragments. |
| ProviderBatch | Provider execution unit. It is not a canonical conversation unit. |
| PhysicalProviderResponse | One provider response fact represented by an ADR-016 CompletionRecord. It is not history. |
| FeatureOperation | One user translation workflow that may contain multiple logical parents. It is not automatically one conversation turn. |
| FinalAcceptance | Content-side point at which a LogicalParent is fully reconstructed, validated, and accepted, making it eligible for DOM commit and causing a parent ACK. It is not the DOM mutation itself. |
| stage | Capture provisional parent-level semantic content before commit. |
| commit | Persist one accepted turn into conversation history, at most once. |
| discard | Drop provisional candidate without history mutation. |
| primary translation | A normal PRIMARY_TRANSLATION execution eligible only when participation rules pass. |
| structured recovery | A provider-local STRUCTURED_RECOVERY pass after a contract violation; never normal history. |
| late settlement | A provider outcome arriving after the request reached a terminal state. |
| history-enabled request | A request satisfying the complete conversation participation predicate, including mode, setting, purpose, provider, and session identity. |
Conversation participation is evaluated once per LogicalParent, not once per fragment, provider batch, retry, or physical response.
A request participates only when all conditions hold:
AI provider
AND history setting enabled
AND mode is history-eligible
AND callPurpose === PRIMARY_TRANSLATION
AND valid session identity exists
Current history-eligible mode is Select Element. Other modes remain non-participating until separately defined by product contract.
Participation controls both directions:
participating request
→ may read normal history
→ may write normal history
non-participating request
→ no normal history read
→ no normal history write
Read/write symmetry is mandatory. Accidental write-only participation and prewarming are prohibited.
STRUCTURED_RECOVERY is always excluded from normal history, regardless of subset/full scope.
Operational call sequencing may remain independent, but operational sequence is not a ConversationTurn.
ADR-016 CompletionRecord describes one physical provider response:
PhysicalProviderResponse → CompletionRecord
ConversationTurn describes one accepted semantic translation:
FinalAcceptance(LogicalParent) → ConversationTurn
Therefore:
- one provider completion does not imply a conversation commit;
- multiple physical responses may produce one conversation turn;
- retries, failover, and recovery do not create additional turns;
- completion termination and conversation acceptance remain separate facts.
ConversationTurn.userContent is clean logical parent source content.
ConversationTurn.assistantContent is the final accepted clean logical translation.
Conversation history must never contain:
- provider prompts or request payloads;
- JSON transport envelopes;
- internal marker-bearing source or output;
- individual transport-fragment text;
- raw provider responses;
- repair prompts or
repairContext; - rejected or source-filled output.
Content is copied into the provisional candidate and does not depend on later mutable DOM state.
For Select Element logical parents, the acceptance lifecycle is:
Provider execution
→ semantic validation
→ immutable handoff registered in Background
→ result dispatched to Content
→ BlockGroupReconstructor.apply()
→ FinalAcceptance
→ PARENT_ACCEPTANCE_ACK
→ ConversationAcceptanceCoordinator
→ ordered commitAcceptedParent()
FinalAcceptance is the earliest semantic point at which the parent is fully validated and accepted by the feature. It is not the DOM mutation itself. DOM commit and Conversation commit both consume the same accepted semantic parent; neither commit defines, replaces, or changes semantic acceptance.
A failed parent produces neither normal conversation history nor a committed parent DOM result.
DOM mutation and history storage remain separate responsibilities, but both consume the same accepted logical-parent result. A failure in one parent emits a rejected ACK and writes no normal history. A failure does not roll back unrelated accepted parents.
Provider transport may execute:
LogicalParent
→ N ProviderBatches
→ M PhysicalProviderResponses
Conversation semantics remain:
one finally accepted LogicalParent
→ one ConversationTurn
The conversation lifecycle must never observe provider fragmentation as separate semantic turns.
The following are implementation details only and must never become semantic history units:
ProviderBatch;- transport fragment;
PhysicalProviderResponse.
Examples:
1 LogicalParent
5 ProviderBatches
9 PhysicalProviderResponses
→ 1 ConversationTurn
1 LogicalParent
3 retries
→ 1 ConversationTurn
1 LogicalParent
provider failover
→ 1 ConversationTurn
eligible logical parent begins
→ parent candidate/handoff created
→ provider/parser/recovery execution
→ result dispatched to Content
→ FinalAcceptance
→ ACK
→ ordered Coordinator commit
→ one ConversationTurn
- A
ConversationCandidateis provisional and does not imply accepted history. - For migrated Select Element parent lifecycles, provider adapters stage or discard provisional data; they never durably commit the parent.
ConversationAcceptanceCoordinatoris the only durable writer for migrated parent lifecycles, throughTranslationSessionManager.commitAcceptedParent().- ACK activation starts only after successful result dispatch; registration alone starts no timer.
- ACK timeout disposes the handle and removes the coordinator entry. Pending parents are discarded; accepted-but-uncommitted parents remain accepted in the disposed handle and cannot commit.
- Late ACKs return
STALE; duplicate ACKs cannot create duplicate writes. - Transport fragmentation does not create additional turns.
- Non-fragmented and fragmented parents use identical parent-level semantics.
- One accepted parent with one or five transport fragments produces exactly one turn.
- Provider retries and failover do not create additional turns.
- The final parent acceptance barrier and ACK must precede conversation commit.
No migrated parent commit before Content ACK.
For non-migrated/direct provider paths, the existing provider candidate commit remains available. Such paths do not use the parent acceptance lifecycle and are outside this ACK contract.
provider/parser candidate invalid
→ discard provisional candidate contribution
→ structured recovery (selective or full, per provider policy)
- A provider/parser-invalid contribution is provisional and is discarded; recovery does not reuse or commit it.
- A provider-level recovery success does not itself create history. The final logical parent must still pass final acceptance.
- A post-merge logical-parent failure discards the parent candidate, even when every physical provider call succeeded.
- Recovery scope may cover a subset of invalid request units or the full structured batch; conversation semantics are identical either way. The subset or full distinction is a provider recovery-policy concern, not a conversation concern.
- Recovery is a single provider-local pass (see TRANSLATION_PROVIDER_LOGIC.md).
- Exactly one provider-local structured recovery pass is attempted, using selective or full sequential recovery according to provider policy.
- Call purpose:
STRUCTURED_RECOVERY. - No conversation participation (excluded from history, per Participation Policy).
- Recovery may operate on a subset of invalid request units or the full structured batch; conversation isolation is unchanged either way.
- Atomic failure: a failure rejects the active structured-recovery operation.
- Original recovery error is propagated with its
typepreserved (e.g.NETWORK_ERROR).
repairContext is transient recovery metadata supplied to the structured
recovery request. It:
- exists only for the recovery request;
- is not committed into normal conversation history;
- does not alter conversation turn-counter semantics;
- does not become provider memory.
The fact that recovery may be supplied with failure-specific repair guidance does not change the conversation-isolation rules above.
| Failure | Candidate discarded | History changed | Notes |
|---|---|---|---|
NETWORK_ERROR |
yes | no | catch → discard |
API_RESPONSE_INVALID |
yes | no | same |
VALIDATION |
yes | no | same |
| recovery failure | yes | no | recovery rethrown |
Invariant:
failed request
→ no conversation commit
timeout / signal aborted before commit
→ discard candidate
→ no commit
→ USER_CANCELLED internally if abort propagated
→ external timeout remains TRANSLATION_TIMEOUT
- A timed-out request with an aborted signal discards its candidate; the commit is skipped.
- If the abort surfacing was a cancellation, it is raised as
USER_CANCELLED; the canonical external timeout staysTRANSLATION_TIMEOUT. - Late-commit guard: after a discard or an aborted signal,
commit()is a no-op (settledguard).
Conversation acceptance timeout is separate from provider execution timeout:
handle registered
→ result dispatch succeeds
→ acceptance window activated
→ ACK timeout
→ pending parents discarded
→ handle disposed and coordinator entry removed
→ late ACK = STALE
Timeout cleanup never creates history and never rewrites an ACCEPTED parent as REJECTED.
USER_CANCELLED
→ discard candidate
→ no commit
A genuine cancellation must not be reported as a timeout or generic translation error.
- Late provider success and late provider failure after the request is already terminal are dropped.
- No history write after terminal timeout/cancel.
- No second commit/discard cycle — once settled,
commit/discardare no-ops.
stage: at most once per parent candidate
commit: at most once per accepted logical parent
discard: at most once for the parent terminal path
- Candidate is parent-scoped for eligible migrated Select Element work and must not be independently committed per provider batch.
stagecaptures semantic parent content only while the candidate remains provisional.commitis allowed only after final logical-parent acceptance and the cancellation check.discardinvalidates the entire provisional parent contribution.- A second
discardis a no-op; commit after discard is prohibited.
Nuance: discard is effectively idempotent rather than strictly once, because repeated discard calls are no-ops once settled. Do not overstate strictly-once semantics.
- Queue retry creates a fresh translation attempt. It does not create a new semantic conversation turn.
- A failed parent candidate from a prior attempt is not committed and not reused.
- Recovery failure is rethrown and may enter the outer
QueueManagerretry of the whole task. - Retries must not double-commit any previous parent candidate; only one candidate can commit for one accepted logical parent.
- Structured recovery is not queue retry. Structured recovery is a provider-local contract-failure response path excluded from conversation history; queue retry is a separate execution retry lifecycle. They must not be conflated in conversation semantics. See TRANSLATION_PROVIDER_LOGIC.md for execution policy.
These are separate systems:
| System | What it holds | Persistence |
|---|---|---|
| Conversation history | TranslationSessionManager.session.history (messages) |
in-memory only |
| Provider physical stats | TranslationStatsManager (recordRequest/recordError, counters) |
in-memory (no raw payload) |
| Diagnostics | structural facts | — |
| Feature history/UI history | HistoryStorage (translationHistory key, sourceText/translatedText) |
persisted (StorageCore) |
Do not imply they are one persistence system.
- Conversation history stores raw
userContentandassistantContentin memory (session.history[].content), plus the system prompt. - It is not persisted to
browser.storage— only the in-memoryMap, TTL 30 min, evicted by LRU. - Durable feature history (
HistoryStorage) persistssourceText/translatedTextundertranslationHistory.
Do not state more than verified: raw request/response content is retained in the in-memory conversation session only.
Conversation history order follows logical/source parent order, not:
- provider completion timing;
- provider execution order;
- retry order;
- failover order;
- fragment arrival order;
- stream arrival order.
Concurrent provider execution must not change semantic history ordering. Accepted parent candidates may wait for earlier logical parents before commit.
sessionId identifies an extension-managed conversation namespace. It is not provider-native conversation state.
TranslationSessionManager owns in-memory session storage, TTL, eviction, and committed message pairs. turnCounter is operational sequence bookkeeping for diagnostics and provider logging. It is not ConversationTurn identity and does not define history order.
Gaps in operational sequence numbers caused by failure, cancellation, retry, or excluded recovery are not semantic history gaps.
| Responsibility | Owner |
|---|---|
| Session storage and committed messages | TranslationSessionManager |
| Participation predicate and history projection | AIConversationHelper |
| Provider protocol execution | Provider adapters and BaseAIProvider |
| Provisional parent candidate | Conversation lifecycle |
| Logical-parent acceptance | OptimizedJsonHandler and feature reconstruction boundary |
| DOM mutation | BlockGroupReconstructor / feature adapter |
| History commit/discard | Conversation lifecycle |
| Terminal request state | UnifiedTranslationService |
Provider adapters may contribute provisional semantic data, but must not decide final logical-parent acceptance or independently commit history.
CompletionRecordis notConversationTurn.- Conversation lifecycle must never observe provider fragmentation as semantic history.
- History participation is evaluated once per logical parent.
- History read and write eligibility are symmetric.
- History disabled means no normal history read and no normal history write.
- Transport execution never defines semantic history.
- Recovery, retry, failover, and physical response count do not create extra turns.
- History ordering must never depend on completion timing.
- A failed logical parent contributes no normal conversation history.
- Unrelated accepted parents remain committed.
Callers may assume:
- accepted, committed history corresponds only to finally accepted logical parents;
- structured recovery does not pollute conversation history;
- timeout/cancel cannot create phantom history entries.
This contract defines semantics only. Runtime alignment is staged:
Align history reads and writes with the participation predicate. History-disabled and non-participating requests create no normal history contribution.
Introduce parent-scoped provisional conversation data without independently committing provider-batch or fragment contributions.
Commit one clean turn only after Content FinalAcceptance and ACK. Discard or reject the parent contribution on parent validation failure, cancellation, timeout, dispatch failure, or reconstruction failure.
Evaluate the same parent-level lifecycle for non-fragmented Select Element parents so fragmentation remains transport-transparent.
Synchronize documentation with parent acceptance ownership. Legacy provider candidate commits remain valid only for non-migrated paths.
These phases must not change ADR-015 outcome semantics or ADR-016 completion semantics.
ADR-015 remains authoritative for translation outcomes, validation, feature acceptance, and DOM/feature mutation semantics.
ADR-016 remains authoritative for normalized physical provider completion facts.
This contract complements both decisions:
CompletionRecord → physical provider fact
ConversationTurn → accepted semantic translation context
TranslationOutcome → final workflow outcome
No contract here reclassifies completion termination, changes recovery policy, or changes final translation outcome semantics.
successful structured recovery
→ returned to caller
→ not committed into conversation history
This remains an intentional policy: recovery can produce an accepted translation result without creating a separate recovery history turn. The eventual accepted logical parent may create one normal turn under the parent lifecycle.
This document does not define:
- provider retry algorithms → PROVIDER_CONTRACT.md.
- provider recovery execution policy (selective vs full eligibility) → TRANSLATION_PROVIDER_LOGIC.md.
- identity/fragment rules → TRANSLATION_IDENTITY_AND_FRAGMENT_CONTRACT.md.
- feature source preservation → FEATURE_CONTRACTS.md.
- canonical
TranslationOutcomemodel → ../../adr/ADR-015-translation-outcome-semantics.md. - overall shared pipeline and runtime flow → ../architecture/TRANSLATION_SYSTEM.md.
Diagram: see the AI conversation lifecycle in ../architecture/DIAGRAMS.md.
| Lifecycle guarantee | Primary tests |
|---|---|
| Participation policy / recovery exclusion | src/features/translation/providers/utils/AIConversationHelper.test.js |
| Commit-once, final-acceptance and abort guard | src/features/translation/providers/BaseAIProvider.test.js |
| Contract violation → discard → one recovery | src/features/translation/providers/BaseAIProvider.test.js, AIResponseParser.test.js |
| Recovery no history pollution | src/features/translation/providers/BaseAIProvider.test.js, AIConversationHelper.test.js |
| Failure discards, no history write | src/features/translation/providers/BaseAIProvider.test.js |
| Timeout / abort discard + late-commit guard | src/features/translation/providers/BaseAIProvider.test.js, src/features/translation/core/managers/OptimizedJsonHandler.test.js |
| Cancellation discard, no commit | src/features/translation/providers/BaseAIProvider.test.js |
| Late settlement suppression | src/features/translation/core/managers/OptimizedJsonHandler.test.js |
Exactly-once stage/commit/discard (settled guard) |
src/features/translation/providers/BaseAIProvider.test.js |
| Physical stats (transactional separation) | src/features/translation/core/TranslationStatsManager.test.js, src/features/translation/providers/utils/ProviderRequestEngine.test.js |