Promotion
Promotion is the only path from ephemeral work to durable storage.
Promotion creates clean durable commits from selected session changes instead of replaying agent history.
Current core promotion supports:
- all touched files
- selected touched files
- baseline conflict detection
- durable commits through
durable.applyCommit()
It does not currently support hunk-level promotion.
Selectors
Section titled “Selectors”type PromoteSelector = | { mode: 'all' } | { mode: 'files'; files: string[] }selector: { mode: 'all' } promotes all touched files.
selector: { mode: 'files', files: [...] } promotes selected touched files only.
Empty selection fails with PROMOTE_FAILED at stage staging.
Promotion flow
Section titled “Promotion flow”flowchart TD
Touch["ephemeral.touchedFiles(sessionId)"] --> Select["selector: all or files"]
Select --> Empty{"empty selection?"}
Empty -->|yes| Fail["PROMOTE_FAILED\nstage: staging"]
Empty -->|no| Head["read durable HEAD"]
Head --> Drift{"durable HEAD changed\nsince baseline?"}
Drift -->|no| Apply["durable.applyCommit(files)"]
Drift -->|yes| Overlap{"durable changed files\noverlap selected files?"}
Overlap -->|yes| Conflict["BASELINE_CONFLICT"]
Overlap -->|no| Apply
Apply --> Result["PromoteResult\n{ sha, branch, prUrl? }"]
Diff behavior
Section titled “Diff behavior”Current diff behavior:
- Computes diffs from
ephemeral.touchedFiles(sessionId). - Reads baseline content from durable at
baselineSha. - Reads current content from ephemeral if the file exists.
- Missing ephemeral file means deletion.
- Text files use unified patches via the
diffpackage. - Binary files are reported as
Binary files a/<path> and b/<path> differ.
Durable drift
Section titled “Durable drift”Gittrix reads current durable HEAD before applying. If durable HEAD changed since the session baseline, Gittrix checks changed file paths.
If durable-changed files overlap selected files, Gittrix throws BASELINE_CONFLICT.
Non-overlapping durable drift does not block promotion.
class BaselineConflictError extends GittrixError { code: 'BASELINE_CONFLICT' conflictingFiles: string[] durableSha: string baselineSha: string}Apply behavior
Section titled “Apply behavior”Selected files are sent to durable.applyCommit() as:
Record<string, Uint8Array | null>null means delete the file on durable.
Default commit message is gittrix: <task>, or gittrix: promote session <session-id> if task is blank.
Strategy limitations
Section titled “Strategy limitations”type PromoteStrategy = 'auto' | 'commit' | 'branch' | 'pr' | 'patch'The type includes auto, commit, branch, pr, and patch.
Current core implementation passes selected files to the durable adapter’s applyCommit() on the durable branch. It does not yet implement separate behavior for branch, PR, or patch strategies.
GitHub PR creation exists as GitHubDurableAdapter.openPullRequest(), not as automatic UserSession.promote({ strategy: 'pr' }) behavior.