Move 13 shipped openspec change docs under openspec/changes/archived/. Add docs/features/git-diff-panel, docs/plans/post-review-backlog, and docs/research/cross-app-contract-ssot.md (the research behind the @boocode/contracts SSOT work). Update BOOCHAT.md, BOOCODER.md, and boocode_roadmap.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
362 lines
25 KiB
Markdown
362 lines
25 KiB
Markdown
# Decision log — Git diff panel
|
|
|
|
Decisions behind [`feature-specification.md`](../feature-specification.md). Full decisions carry rationale,
|
|
evidence, and rejected alternatives; trivial decisions are one-liners. Shared D# counter.
|
|
|
|
## Full decisions
|
|
|
|
### D1 — Placement: a tab inside the file browser
|
|
**Question:** Where does the diff view live in the workspace?
|
|
**Decision:** The diff view lives in the right-side file panel as a Files / Git tab, occupying the same
|
|
slot as the file tree, rather than as a new standalone workspace pane.
|
|
**Rationale:** The request was "instead of the file browser," and the file browser is a right-side sidebar,
|
|
not a workspace-grid pane. The reference design (Paseo) puts Changes / Files tabs in one sidebar slot. A
|
|
new workspace pane would require new pane-kind plumbing for an affordance the user described as a
|
|
replacement, not an addition.
|
|
**Evidence:** User answer (2026-06-02). Codebase: the file browser is the right-rail sidebar; the legacy
|
|
"file_browser" pane kind is unused. Paseo `explorer-sidebar.tsx` (Changes/Files tabs in one slot).
|
|
**Rejected alternatives:**
|
|
- A standalone git-diff workspace pane — rejected: it is an addition, not a replacement, and adds pane
|
|
plumbing the user did not ask for.
|
|
**Driven by findings:** —
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** D9, D10.
|
|
**Referenced in spec:** Actors and triggers, Primary flow, User interactions.
|
|
|
|
### D2 — Scope: the project repository, with two comparison modes
|
|
**Question:** What repository and comparison modes does the panel cover?
|
|
**Decision:** The panel shows the **project repository's** changes, with a selector between **Uncommitted**
|
|
(working tree vs. last commit) and **Committed** (current branch vs. its upstream tracking branch when
|
|
set, otherwise the repository's default branch). It does not show the session agent's separate
|
|
working-copy diff — that remains the pending-changes panel's job. In Committed mode the view labels the
|
|
base it resolved ("Git — branch vs <base>"); when no base resolves the panel falls back to
|
|
Uncommitted and labels the mode as a fallback.
|
|
**Rationale:** The file browser is scoped to the project repository, so the diff "instead of" it should
|
|
share that scope. The user chose both comparison modes (Paseo-style) over a single mode. The agent
|
|
working-copy diff is already surfaced by the pending-changes panel; duplicating it here would create two
|
|
overlapping surfaces. Labeling the base prevents silent ambiguity (F11).
|
|
**Evidence:** User answer (2026-06-02, "Both, with a selector"). Codebase: the file browser and project
|
|
git-metadata are scoped to the project path; agent worktree diffs already flow to the pending-changes
|
|
panel. Paseo uncommitted/committed mode selector. F11 (base unlabeled finding).
|
|
**Rejected alternatives:**
|
|
- Project working tree only — rejected: user wanted both modes.
|
|
- Session agent worktree — rejected: overlaps the pending-changes panel and is not the file browser's scope.
|
|
- No base labeling — rejected (F11): the base is ambiguous between upstream tracking branch and default
|
|
branch; unlabeled output invites confusion.
|
|
**Driven by findings:** F11.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** D3, D4, D5, D11, D13.
|
|
**Referenced in spec:** Outcome, Primary flow, Edge cases and failure modes, Coordinations.
|
|
|
|
### D3 — Mode auto-selection and session pinning
|
|
**Question:** How is the initial mode chosen and how does it behave across refreshes?
|
|
**Decision:** Auto mode-selection applies on first open only: Uncommitted when the working tree is dirty,
|
|
Committed when it is clean. Once the user selects a mode explicitly it is pinned for the session;
|
|
refreshes do not override it. If a refresh would change the auto-selected mode (e.g. the tree went clean
|
|
while Uncommitted was pinned), the panel briefly notes the change rather than swapping silently.
|
|
**Rationale:** Paseo's convention is auto-select by state. However, a refresh-triggered silent mode swap
|
|
would dislocate the user's view without warning — they could be mid-review and suddenly see a different
|
|
file list (F12). Pinning after explicit selection preserves the user's intent.
|
|
**Evidence:** Paseo convention (auto-select by dirty state). F12 (silent-dislocation finding;
|
|
design-judgment resolution).
|
|
**Rejected alternatives:**
|
|
- Always auto-select on every refresh — rejected (F12): silently dislocates the view when the tree state
|
|
changes mid-session.
|
|
- No auto-selection (always start in a fixed mode) — rejected: ignores the most useful starting point.
|
|
**Driven by findings:** F12.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** D14.
|
|
**Referenced in spec:** Primary flow, Alternate flows and states.
|
|
|
|
### D5 — Binary and large-file handling
|
|
**Question:** What does the panel show for files it cannot diff or whose diff is too large?
|
|
**Decision:** Binary files show a "Binary file" placeholder instead of a diff body. Files over a display-
|
|
size threshold show "Diff too large to display" in place of the diff body. A git read that does not
|
|
complete within a deadline exits the loading state, shows an error, and offers Refresh. The total
|
|
response payload is bounded so a huge change set cannot stall the panel.
|
|
**Rationale:** Paseo-style caps prevent the panel from hanging or overflowing on large repos. The read-
|
|
deadline (F7) is a distinct concern from the large-result cap: a slow git process can stall the panel
|
|
even when individual files are small.
|
|
**Evidence:** Paseo codebase (display-size caps). F7 (hanging git-read finding; evidence-backed addition).
|
|
**Rejected alternatives:**
|
|
- No cap — rejected: a huge change set can stall or overflow the panel.
|
|
- Combine deadline and size cap into one mechanism — rejected (F7): they address different failure modes
|
|
(slow process vs. large output); both are needed.
|
|
**Driven by findings:** F7.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** —
|
|
**Referenced in spec:** Edge cases and failure modes.
|
|
|
|
### D6 — v1 actions: stage, unstage, commit, discard (no push)
|
|
**Question:** Which write actions are included in v1, and in which modes are they available?
|
|
**Decision:** v1 includes staging/unstaging files, committing staged files with a message, and discarding a
|
|
file's changes — all available only in Uncommitted mode. Committed mode is read-only review with no write
|
|
actions. Pushing, pulling, PRs, and merges are excluded. Commit author/committer identity is derived
|
|
server-side; the request cannot set or influence it.
|
|
**Rationale:** The user chose to include stage/commit over a read-only view. Remote operations were not
|
|
requested and the assistant-level rule already treats remote writes as out of band, so v1 stops at local
|
|
history. Allowing write actions in Committed mode would mean reverting committed history (per-file resets
|
|
of committed commits), which was not requested and creates a different risk profile. Committer identity
|
|
must be server-derived to prevent the request body from spoofing authorship (F3).
|
|
**Evidence:** User answer (2026-06-02, "Include stage/commit"). Convention: the assistant cannot push to
|
|
remotes (project docs) — signals remotes are deliberately out of band. F3 (committer-identity
|
|
finding). F14 (mode-scoping finding; design-judgment resolution).
|
|
**Rejected alternatives:**
|
|
- Read-only review — rejected by the user.
|
|
- Full git actions incl. push/pull/PR (Paseo's set) — deferred (YAGNI): not requested.
|
|
- Write actions in Committed mode too — rejected (F14): reverting committed history is a distinct,
|
|
unrequested capability with a different risk profile.
|
|
- Request-supplied commit identity — rejected (F3): allows spoofing; server-derived identity is the only
|
|
safe source.
|
|
**Driven by findings:** F3, F14.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** D7, D8, D12.
|
|
**Referenced in spec:** Primary flow, User interactions.
|
|
|
|
### D7 — Discard requires a plain confirmation
|
|
**Question:** What confirmation does discard require, and what wording?
|
|
**Decision:** Discarding a file's changes prompts a plain Cancel / Discard confirmation with wording that
|
|
distinguishes the two cases: "Discard changes to X?" for a tracked file (reverts to committed content)
|
|
and "Delete X? It has never been committed and cannot be recovered" for an untracked file (permanently
|
|
removed). Stage, unstage, and commit do not prompt.
|
|
**Rationale:** Discard is the only irreversible action in the set; a confirmation guards an accidental tap,
|
|
especially on mobile. The project's stated preference is plain confirm dialogs, never type-the-name
|
|
patterns. A tracked revert and an untracked permanent delete are different losses — the user deserves to
|
|
know which one they are confirming (F4).
|
|
**Evidence:** Convention: destructive actions use plain Cancel/Confirm dialogs (no type-to-confirm).
|
|
Stage/unstage/commit are reversible (commits can be amended/reset), so they need no prompt. F4
|
|
(tracked-vs-untracked and affordance-separation finding; design-judgment resolution).
|
|
**Rejected alternatives:**
|
|
- No confirmation — rejected: irreversible data loss on a stray tap.
|
|
- Type-the-filename-to-confirm — rejected: against the project's confirmation convention.
|
|
- Single generic confirmation for tracked and untracked — rejected (F4): hides the difference in
|
|
consequence (revert vs. permanent delete) from the user.
|
|
**Driven by findings:** F4.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** D15.
|
|
**Referenced in spec:** Alternate flows and states.
|
|
|
|
### D8 — Git write is a user action, not an assistant tool
|
|
**Question:** Are the panel's write actions gated by session type (e.g. read-only-assistant sessions)?
|
|
**Decision:** The diff panel's write actions (stage/commit/discard) are available wherever the file panel
|
|
appears, including read-only-assistant sessions, because they are the human user's own UI actions, not
|
|
the AI's. The git-write endpoints are never registered as assistant tools, and the artifact sandbox
|
|
prevents a rendered artifact from invoking them.
|
|
**Rationale:** The "read-only" rule constrains what the AI assistant's tools may do. A human committing
|
|
their own repository through a panel is a different actor; gating the panel by session type would be a
|
|
category error and produce inconsistent behavior across sessions. The artifact-sandbox commitment (F1)
|
|
closes the indirect path an artifact might otherwise exploit.
|
|
**Evidence:** Convention: the read-only invariant is defined over the assistant's tool surface; the file
|
|
browser (also user-driven) already appears in all sessions. F1 (artifact-sandbox finding; evidenced by
|
|
`connect-src 'none'` in the artifact iframe sandbox per BOOCHAT.md output-format section).
|
|
**Rejected alternatives:**
|
|
- Restrict the write actions to write-capable (coder) sessions only — rejected: conflates the assistant's
|
|
tool permissions with the user's UI affordances; produces inconsistent behavior.
|
|
**Driven by findings:** F1.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** D12.
|
|
**Referenced in spec:** Actors and triggers, Coordinations.
|
|
|
|
### D10 — Refresh on open, on mutation, on turn completion, on demand, with coalescence
|
|
**Question:** When does the panel re-read the repository, and how are concurrent triggers handled?
|
|
**Decision:** The panel re-reads the repository state when the Git tab is opened, after any stage /
|
|
unstage / commit / discard it performs, after an agent turn completes, after the user applies or discards
|
|
a queued change in the pending-changes panel, and on an explicit Refresh control. Concurrent refresh
|
|
triggers are coalesced — a refresh already in flight absorbs later triggers rather than spawning a
|
|
second concurrent read, so the panel settles to a single final snapshot. Continuous file-watching is
|
|
excluded.
|
|
**Rationale:** These triggers cover every event that can change the project repository's state within the
|
|
app's single-user workflow. Coalescence (F8) prevents a burst of triggers (e.g. multiple rapid mutations)
|
|
from causing redundant concurrent reads or a stale intermediate result overwriting a fresher one. A
|
|
continuous file watcher adds cost without a multi-user need.
|
|
**Evidence:** Convention: event-driven refresh follows the session-event / broker model already used for
|
|
other panels. F8 (concurrent-refresh finding; evidence-backed addition).
|
|
**Rejected alternatives:**
|
|
- Continuous file-watch stream — rejected (YAGNI): event- and demand-driven covers the single-user case;
|
|
deferred under YAGNI.
|
|
- No coalescence (each trigger spawns its own read) — rejected (F8): can produce concurrent reads and
|
|
stale-snapshot overwrites.
|
|
**Driven by findings:** F8.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** —
|
|
**Referenced in spec:** Alternate flows and states.
|
|
|
|
### D11 — All git operations scoped to the project repository path
|
|
**Question:** How is the git operation target scoped and validated?
|
|
**Decision:** Every read and write the panel performs is confined to the project's own repository. The
|
|
repository root is derived server-side from the session's project record — never from the request. Per-
|
|
file arguments are validated as repo-relative paths and rejected if they escape the repository root.
|
|
User-supplied text (commit message, file arguments) is passed as discrete arguments and never
|
|
interpolated into a shell string.
|
|
**Rationale:** The panel acts on the project repo only. Deriving the root server-side and validating
|
|
per-file arguments closes the path-escape and command-injection vectors (F2). Passing text as discrete
|
|
arguments (not a shell string) ensures user-supplied content cannot be interpreted as git flags or shell
|
|
syntax.
|
|
**Evidence:** Convention: project file operations resolve and scope to the project path via the existing
|
|
path-scoping guard; git-metadata reads already do this. F2 (derivation + argument-safety finding;
|
|
evidenced by the existing path-scoping guard).
|
|
**Rejected alternatives:**
|
|
- Accept a caller-supplied repository path — rejected: needless write surface, no use case.
|
|
- Validate path only at the root level (not per-file arguments) — rejected (F2): per-file arguments can
|
|
escape the repo root via `../` traversal if not independently validated.
|
|
- Build git invocations as a shell string — rejected (F2): user-supplied content (commit message, file
|
|
names with special characters) can be interpreted as flags or shell syntax.
|
|
**Driven by findings:** F2.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** D12.
|
|
**Referenced in spec:** Coordinations.
|
|
|
|
### D12 — Git-write security posture
|
|
**Question:** What are the combined security commitments for the git-write surface?
|
|
**Decision:** The git-write surface (stage / commit / discard) has three security commitments: (1) these
|
|
actions are user-initiated UI actions only and are never registered as assistant tools; the artifact
|
|
sandbox prevents a rendered artifact from invoking them; (2) all git operations target only the project's
|
|
own repository, with the root derived server-side and per-file paths validated inside it; (3) commit
|
|
author/committer identity is derived from a server-side source (host git configuration) and cannot be set
|
|
by the request.
|
|
**Rationale:** F1, F2, and F3 each attacked a distinct vector — artifact-driven invocation, path/argument
|
|
injection, and identity spoofing — that D8 and D11 individually did not close. D12 records all three
|
|
commitments together as the complete security posture of the write surface.
|
|
**Evidence:** F1 (artifact-sandbox; `connect-src 'none'` per BOOCHAT.md output-format section).
|
|
F2 (path-scoping guard in the codebase; derivation and validation commitments). F3 (server-derived
|
|
identity commitment; design-judgment that no request field should influence authorship).
|
|
**Rejected alternatives:**
|
|
- Trust the client-supplied repository path — rejected (F2): see D11.
|
|
- Allow request-supplied commit identity — rejected (F3): allows spoofing; no legitimate use case in a
|
|
single-user app.
|
|
- Rely on session-type gating instead of endpoint-level exclusion from tool registry — rejected (F1):
|
|
session type is the wrong layer; artifact-sandbox closes the actual indirect path.
|
|
**Driven by findings:** F1, F2, F3.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** —
|
|
**Referenced in spec:** Actors and triggers, Coordinations.
|
|
|
|
### D13 — Committed-mode base resolution and labeling
|
|
**Question:** What is the base for Committed mode, and how is it surfaced when resolution fails?
|
|
**Decision:** Committed mode compares the current branch against its upstream tracking branch when one is
|
|
set, falling back to the repository's default branch (main/master). The panel labels the base it used in
|
|
the mode header ("Git — branch vs <base>"). When no base resolves (no tracking branch and no
|
|
discoverable default branch), the panel falls back to showing uncommitted changes and labels the mode as
|
|
a fallback, rather than erroring or silently swapping.
|
|
**Rationale:** "Base" was undefined in D2, leaving the committed comparison ambiguous (F11). The tracking-
|
|
branch-first resolution matches git's own upstream model and is the most useful default for contributors
|
|
tracking a remote. Labeling the resolved base makes the comparison unambiguous to the user. A labeled
|
|
fallback is more informative than an error and does not leave the panel empty.
|
|
**Evidence:** F11 (base-unlabeled finding; UX-002, JD-002; design-judgment resolution). Git upstream
|
|
model (tracking branch as natural "base" for a contributor's branch).
|
|
**Rejected alternatives:**
|
|
- Always compare against the default branch, ignoring tracking — rejected (F11): wrong for contributors
|
|
whose tracking branch is a personal fork or a PR target branch, not the default.
|
|
- Error when no base resolves — rejected: leaves the panel useless; an unlabeled fallback is more
|
|
helpful.
|
|
- Silently swap to uncommitted without a label — rejected (F11): the original spec's behavior; confusing
|
|
because the mode selector still shows "Committed".
|
|
**Driven by findings:** F11.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** —
|
|
**Referenced in spec:** Primary flow, Edge cases and failure modes, User interactions.
|
|
|
|
### D14 — Mode pinning and first-open auto-selection
|
|
**Question:** Does auto mode-selection persist across refreshes after the user has acted?
|
|
**Decision:** Auto mode-selection applies on first open only. Once the user selects a mode explicitly (via
|
|
the selector), that choice is pinned for the session. Refreshes do not override a pinned mode. If a
|
|
refresh would change the auto-selected mode (e.g. the tree transitioned from dirty to clean while
|
|
Uncommitted was pinned), the panel briefly notes the change rather than swapping silently.
|
|
**Rationale:** Auto-select on every refresh would dislocate the user mid-review without warning —
|
|
illustrated by the scenario where the tree goes clean while the user is reading the uncommitted diff (F12).
|
|
A brief note on a state change preserves awareness without overriding intent.
|
|
**Evidence:** F12 (silent-dislocation finding; design-judgment resolution). D3 (auto-selection origin).
|
|
**Rejected alternatives:**
|
|
- Re-run auto-selection on every refresh — rejected (F12): dislocates the user's active view.
|
|
- No notification on a would-be mode change — rejected: leaves the user unaware that the repository
|
|
state changed in a way that would normally affect the view.
|
|
**Driven by findings:** F12.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** —
|
|
**Referenced in spec:** Primary flow.
|
|
|
|
### D15 — Discard is irrecoverable; tracked vs. untracked confirmation; separated affordance
|
|
**Question:** What are the full discard semantics and the UI placement of the discard control?
|
|
**Decision:** Discard is hard-delete and irrecoverable. The confirmation dialog uses two distinct wordings:
|
|
"Discard changes to X?" for a tracked file (which reverts to its committed content; the work is lost but
|
|
the file remains in history) and "Delete X? It has never been committed and cannot be recovered" for an
|
|
untracked file (permanent deletion with no recovery path). The Discard affordance is placed in an
|
|
overflow or secondary position rather than as an equal-weight sibling of Stage/Unstage.
|
|
**Rationale:** The spec previously called discard "irrecoverable" but left the git mechanic ambiguous.
|
|
Owning the word and spelling out the two cases (F4) ensures the confirmation is honest. Separating the
|
|
affordance from Stage/Unstage reduces the risk of an accidental tap on mobile (F4, UX concern).
|
|
**Evidence:** F4 (discard-semantics and affordance-separation finding; on-call-engineer OCE-002,
|
|
UX-005, adversarial-security-analyst; design-judgment resolution). Convention: plain
|
|
Cancel/Confirm dialogs.
|
|
**Rejected alternatives:**
|
|
- Single generic confirmation for tracked and untracked cases — rejected (F4): obscures the difference
|
|
in consequence.
|
|
- Discard at equal weight alongside Stage/Unstage — rejected (F4): accidental-tap risk on mobile.
|
|
**Driven by findings:** F4.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** —
|
|
**Referenced in spec:** Alternate flows and states, User interactions.
|
|
|
|
### D16 — Tab named "Git"
|
|
**Question:** What is the new tab called?
|
|
**Decision:** The new tab is named **Git**, giving a Files / Git tab pair. The existing "Pending Changes"
|
|
panel is not renamed; that rename is out of scope.
|
|
**Rationale:** "Changes" (the working name in the initial spec) collides with "Pending Changes" — the name
|
|
of an existing distinct panel — creating discoverability confusion (F10, UX-001, UX-008, JD-001). "Git"
|
|
is shorter, unambiguous, and describes the surface (the project's git state) without implying overlap
|
|
with the pending-changes panel.
|
|
**Evidence:** F10 (naming-collision finding; design-judgment resolution). Existing surface name: "Pending
|
|
Changes" panel in the codebase.
|
|
**Rejected alternatives:**
|
|
- "Changes" — rejected (F10): collides with "Pending Changes"; confusion in context-switching.
|
|
- Rename "Pending Changes" to disambiguate — rejected (F10): out of scope; would require changes to an
|
|
existing surface the user did not ask to rename.
|
|
**Driven by findings:** F10.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** —
|
|
**Referenced in spec:** Actors and triggers, User interactions, Out of scope.
|
|
|
|
### D17 — Ambient dirty indicator and empty-state hint
|
|
**Question:** How does the user discover the Git tab when the panel defaults to Files?
|
|
**Decision:** An ambient indicator on the file-panel toggle/header signals the repository is dirty (derived
|
|
from the refresh data already gathered), making the Git tab findable without opening it. When the Git
|
|
view is empty but the session has unapplied pending changes, the empty state hints that those live in the
|
|
pending-changes panel.
|
|
**Rationale:** Without a visual signal the Git tab is invisible until the user already knows to look for it
|
|
(F10, UX-001). The indicator reuses state already gathered by the refresh cycle — no additional read
|
|
needed. The empty-state hint prevents the user from concluding the panel is broken when what they are
|
|
looking for is actually in the adjacent pending-changes panel.
|
|
**Evidence:** F10 (discoverability finding; design-judgment resolution). Refresh cycle already produces
|
|
dirty/clean state (D10).
|
|
**Rejected alternatives:**
|
|
- No ambient indicator (rely on the user knowing the tab exists) — rejected (F10): undiscoverable by
|
|
new users.
|
|
- Always show dirty indicator (not just when dirty) — rejected: misleading on clean repos.
|
|
**Driven by findings:** F10.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** —
|
|
**Referenced in spec:** Actors and triggers, Alternate flows and states.
|
|
|
|
### D18 — Mobile tap-target and header-fit
|
|
**Question:** What are the layout and accessibility constraints for the new tab and controls?
|
|
**Decision:** All interactive controls in the diff panel follow the app's existing mobile tap-target
|
|
minimum. The Files / Git tab strip and header fit on one line without horizontal scroll or wrapping;
|
|
existing header elements are condensed if needed to maintain fit.
|
|
**Rationale:** The app has an existing toolbar-fit rule (no scroll/wrap on crowded control bars) and a
|
|
mobile-first posture. The new Git tab and its in-panel controls must not break either. Condensing
|
|
existing elements rather than scrolling is the project's established pattern (F15, UX-009, JD-008).
|
|
**Evidence:** F15 (mobile-fit finding; convention). Project convention: toolbars must fit one line (no
|
|
scroll or wrapping); MEMORY.md toolbar-fit rule.
|
|
**Rejected alternatives:**
|
|
- Allow horizontal scroll if the header gets crowded — rejected: against the project's toolbar-fit rule.
|
|
- Wrap the header to a second line — rejected: against the project's toolbar-fit rule.
|
|
**Driven by findings:** F15.
|
|
**Linked technical notes:** —
|
|
**Dependent decisions:** —
|
|
**Referenced in spec:** User interactions.
|
|
|
|
## Trivial decisions
|
|
|
|
- D4: Untracked files included in Uncommitted view — untracked files appear in the Uncommitted file list as additions (considered tracked-only; rejected because the user's new files are part of "what changed"). — Referenced in spec: Primary flow.
|
|
- D9: Unified layout, syntax-highlighted — diffs render in a single-column unified layout reusing the existing code highlighter (considered side-by-side; deferred under YAGNI as a desktop-only enhancement). — Referenced in spec: User interactions.
|