diff --git a/apps/labfox/lib/features/merge_requests/data/literal_original_diff.dart b/apps/labfox/lib/features/merge_requests/data/literal_original_diff.dart new file mode 100644 index 00000000..59ca8e73 --- /dev/null +++ b/apps/labfox/lib/features/merge_requests/data/literal_original_diff.dart @@ -0,0 +1,151 @@ +import 'package:gitlab_models/gitlab_models.dart'; + +/// Literal metadata and deeply immutable, complete original diff hunks. +typedef LiteralOriginalDiff = ({CommitDiffFile file, List hunks}); + +final _header = RegExp( + r'^@@ -(0|[1-9][0-9]*)(?:,(0|[1-9][0-9]*))? ' + r'\+(0|[1-9][0-9]*)(?:,(0|[1-9][0-9]*))? @@(?: [^\r\n]*)?$', +); +// Conservative coordinate bound for review positions, across supported runtimes. +const _maxCoordinate = 2147483647; + +bool _supportedPath(String path) => + path.isNotEmpty && + !path.startsWith('/') && + !path.contains(RegExp(r'[\x00\r\n]')) && + path + .split('/') + .every((part) => part.isNotEmpty && part != '.' && part != '..'); + +/// Returns null for unsupported or incomplete input; never repairs raw text. +/// Valid local hunks alone do not establish original refs or write eligibility. +LiteralOriginalDiff? validateLiteralOriginalDiff(CommitDiffFile file) { + if (!_supportedPath(file.oldPath) || + !_supportedPath(file.newPath) || + file.isCollapsed == true || + file.isTooLarge == true || + (file.isNew && file.isDeleted) || + (file.isRenamed && (file.isNew || file.isDeleted)) || + (file.isRenamed + ? file.oldPath == file.newPath + : file.oldPath != file.newPath)) { + return null; + } + final text = file.diff; + if (text == null || text.isEmpty) return null; + final raw = text.split('\n'); + // Only the final delimiter is framing, never an empty body line. + if (raw.last.isEmpty) raw.removeLast(); + final hunks = []; + List? lines; + var oldStart = 0, newStart = 0, oldCount = 0, newCount = 0; + var old = 0, newer = 0, previousOldEnd = -1, previousNewEnd = -1; + var endedOld = false, endedNew = false, markerAllowed = false; + var header = ''; + DiffLineType? previousType; + + bool finish() { + if (lines == null) return true; + if (old - oldStart != oldCount || newer - newStart != newCount) { + return false; + } + hunks.add( + DiffHunk( + oldStart: oldStart, + newStart: newStart, + header: header, + lines: List.unmodifiable(lines), + ), + ); + previousOldEnd = oldStart + (oldCount == 0 ? 1 : oldCount); + previousNewEnd = newStart + (newCount == 0 ? 1 : newCount); + return true; + } + + for (final row in raw) { + final match = _header.firstMatch(row); + if (match != null) { + if (!finish()) return null; + final values = [ + int.tryParse(match.group(1)!), + int.tryParse(match.group(3)!), + int.tryParse(match.group(2) ?? '1'), + int.tryParse(match.group(4) ?? '1'), + ]; + if (values.any((v) => v == null || v > _maxCoordinate)) return null; + oldStart = values[0]!; + newStart = values[1]!; + oldCount = values[2]!; + newCount = values[3]!; + if ((oldCount == 0 && newCount == 0) || + (oldCount > 0 && oldStart == 0) || + (newCount > 0 && newStart == 0) || + oldStart + oldCount > _maxCoordinate || + newStart + newCount > _maxCoordinate || + (file.isNew && + (oldStart != 0 || + oldCount != 0 || + newStart != 1 || + hunks.isNotEmpty)) || + (file.isDeleted && + (newStart != 0 || + newCount != 0 || + oldStart != 1 || + hunks.isNotEmpty))) { + return null; + } + final effectiveOld = oldStart + (oldCount == 0 ? 1 : 0); + final effectiveNew = newStart + (newCount == 0 ? 1 : 0); + if (previousOldEnd >= 0 && + (effectiveOld < previousOldEnd || + effectiveNew < previousNewEnd || + effectiveOld - previousOldEnd != effectiveNew - previousNewEnd)) { + return null; + } + old = oldStart; + newer = newStart; + header = row; + lines = []; + markerAllowed = false; + continue; + } + if (lines == null || row.isEmpty) return null; + if (row == r'\ No newline at end of file') { + if (!markerAllowed) return null; + if (previousType != DiffLineType.added) endedOld = true; + if (previousType != DiffLineType.removed) endedNew = true; + markerAllowed = false; + continue; + } + final DiffLineType type; + switch (row[0]) { + case ' ': + type = DiffLineType.context; + case '-': + type = DiffLineType.removed; + case '+': + type = DiffLineType.added; + default: + return null; + } + final usesOld = type != DiffLineType.added, + usesNew = type != DiffLineType.removed; + if ((usesOld && (endedOld || old - oldStart >= oldCount)) || + (usesNew && (endedNew || newer - newStart >= newCount))) { + return null; + } + lines.add( + DiffLine( + type: type, + text: row.substring(1), + oldLine: usesOld ? old++ : null, + newLine: usesNew ? newer++ : null, + ), + ); + previousType = type; + markerAllowed = true; + } + if (!finish() || hunks.isEmpty) return null; + return (file: file, hunks: List.unmodifiable(hunks)); +} diff --git a/apps/labfox/lib/features/merge_requests/data/original_diff_range.dart b/apps/labfox/lib/features/merge_requests/data/original_diff_range.dart index 80ed6325..c4d85db1 100644 --- a/apps/labfox/lib/features/merge_requests/data/original_diff_range.dart +++ b/apps/labfox/lib/features/merge_requests/data/original_diff_range.dart @@ -3,6 +3,8 @@ import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:gitlab_models/gitlab_models.dart'; +import 'literal_original_diff.dart'; + final _lineCode = RegExp(r'^[0-9a-f]{40}_(0|[1-9][0-9]*)_(0|[1-9][0-9]*)$'); final _header = RegExp(r'^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@'); @@ -30,14 +32,21 @@ typedef _Row = ({DiffLine line, int hunk, String code}); /// Matches literal line codes and visible side coordinates in an original file. /// Opposite counters in added/removed line codes are not visible coordinates. /// Only complete, contiguous available hunks can represent a selected span. -List? originalDiffRange(FileDiff file, DiffNoteLineRange range) { +List? originalDiffRange(FileDiff file, DiffNoteLineRange range) => + _originalDiffRange(file.newPath, file.hunks, range); + +List? _originalDiffRange( + String newPath, + List hunks, + DiffNoteLineRange range, +) { if (!supportsOriginalDiffRange(range)) return null; - final hash = sha1.convert(utf8.encode(file.newPath)).toString(); + final hash = sha1.convert(utf8.encode(newPath)).toString(); final rows = <_Row>[]; final complete = []; final ends = <(int, int)>[]; - for (var h = 0; h < file.hunks.length; h++) { - final hunk = file.hunks[h]; + for (var h = 0; h < hunks.length; h++) { + final hunk = hunks[h]; var old = hunk.oldStart, newer = hunk.newStart; for (final line in hunk.lines) { rows.add((line: line, hunk: h, code: '${hash}_${old}_$newer')); @@ -77,8 +86,7 @@ List? originalDiffRange(FileDiff file, DiffNoteLineRange range) { final first = span.first.hunk, last = span.last.hunk; for (var h = first; h <= last; h++) { if (!complete[h]) return null; - if (h > first && - ends[h - 1] != (file.hunks[h].oldStart, file.hunks[h].newStart)) { + if (h > first && ends[h - 1] != (hunks[h].oldStart, hunks[h].newStart)) { return null; } } @@ -92,3 +100,13 @@ List? originalDiffRange(FileDiff file, DiffNoteLineRange range) { row.line, ]); } + +/// Matches review endpoints only after validating the complete literal file. +List? literalOriginalDiffRange( + CommitDiffFile file, + DiffNoteLineRange range, +) { + final validated = validateLiteralOriginalDiff(file); + if (validated == null) return null; + return _originalDiffRange(file.newPath, validated.hunks, range); +} diff --git a/apps/labfox/test/literal_original_diff_test.dart b/apps/labfox/test/literal_original_diff_test.dart new file mode 100644 index 00000000..e8237a16 --- /dev/null +++ b/apps/labfox/test/literal_original_diff_test.dart @@ -0,0 +1,178 @@ +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:gitlab_models/gitlab_models.dart'; +import 'package:labfox/features/merge_requests/data/literal_original_diff.dart'; +import 'package:labfox/features/merge_requests/data/original_diff_range.dart'; + +const text = '@@ -1 +1 @@\n-old \n+new\n'; +CommitDiffFile file(String? diff) => CommitDiffFile( + oldPath: 'lib/a.dart', + newPath: 'lib/a.dart', + isNew: false, + isDeleted: false, + isRenamed: false, + diff: diff, +); + +void main() { + for (final (raw, accepted) in [ + (text, true), + ('@@ -1 +1 @@\n-a\n?ignored\n+b', false), + ('@@ -1,2 +1 @@\n-a\n+b', false), + ('$text@@ -5 +5 @@\n-a\n+b', true), + ]) { + test( + 'literal endpoint matching validates all input before selection $accepted $raw', + () { + final hash = sha1.convert(utf8.encode('lib/a.dart')).toString(); + final endpoint = DiffNoteRangeEndpoint( + lineCode: '${hash}_2_1', + type: 'new', + newLine: 1, + ); + final range = DiffNoteLineRange(start: endpoint, end: endpoint); + final result = literalOriginalDiffRange(file(raw), range); + if (accepted) { + expect(result!.single.newLine, 1); + expect(() => result.clear(), throwsUnsupportedError); + } else { + expect(result, isNull); + } + expect( + literalOriginalDiffRange( + file(raw).copyWith(isCollapsed: true), + range, + ), + isNull, + ); + }, + ); + } + for (final raw in [ + file('@@ -0,0 +2 @@\n+a').copyWith(isNew: true), + file('@@ -2 +0,0 @@\n-a').copyWith(isDeleted: true), + ]) { + test('new/deleted text must begin at the first existing line $raw', () { + expect(validateLiteralOriginalDiff(raw), isNull); + }); + } + + test( + 'retains literal metadata, whitespace, counters and immutable hunks', + () { + final raw = file('@@ -1,2 +1,2 @@ label\n same\r\n-old \n+new\n'); + final result = validateLiteralOriginalDiff(raw)!; + expect(identical(result.file, raw), isTrue); + expect(result.hunks.single.header, '@@ -1,2 +1,2 @@ label'); + expect(result.hunks.single.lines.map((l) => l.text), [ + 'same\r', + 'old ', + 'new', + ]); + expect(result.hunks.single.lines.map((l) => (l.oldLine, l.newLine)), [ + (1, 1), + (2, null), + (null, 2), + ]); + expect(() => result.hunks.clear(), throwsUnsupportedError); + expect(() => result.hunks.single.lines.clear(), throwsUnsupportedError); + }, + ); + for (final input in [ + null, + '', + 'Binary files differ', + 'preamble\n$text', + '@@ -1 +1 @@garbage\n-a\n+b', + '@@ -01 +1 @@\n-a\n+b', + '@@ -0 +1 @@\n-a\n+b', + '@@ -1,0 +1,0 @@', + '@@ -1,2 +1 @@\n-a\n+b', + '@@ -1 +1 @@\n-a\n+b\n+extra', + '@@ -1 +1 @@\n-a\n\n+b', + '@@ -1 +1 @@\n-a\n?ignored\n+b', + '@@ -1 +1 @@\n-a\n\\junk\n+b', + '@@ -999999999999999999999999999999 +1 @@\n-a\n+b', + '@@ -2147483647,2 +1 @@\n-a\n-b\n+c', + '$text@@ -1 +1 @@\n-a\n+b', + '$text@@ -0,0 +2 @@\n+b', + '$text@@ -3 +4 @@\n-a\n+b', + '$text@@ -2 +2 @@\n-a\n+b\ntrailer', + '@@ -1 +1 @@\n\\ No newline at end of file\n-a\n+b', + '@@ -1 +1 @@\n-a\n\\ No newline at end of file\n\\ No newline at end of file\n+b', + '@@ -1,2 +1 @@\n-a\n\\ No newline at end of file\n-b\n+c', + ]) { + test('rejects malformed or incomplete literal input $input', () { + expect(validateLiteralOriginalDiff(file(input)), isNull); + }); + } + for (final change in [ + file(text).copyWith(isCollapsed: true), + file(text).copyWith(isTooLarge: true), + file(text).copyWith(isNew: true, isDeleted: true), + file(text).copyWith(isRenamed: true), + file(text).copyWith(newPath: 'other'), + file(text).copyWith(oldPath: ''), + file(text).copyWith(newPath: '/dev/null'), + file(text).copyWith(oldPath: '../secret'), + file(text).copyWith(newPath: 'lib/../a'), + file(text).copyWith(newPath: 'lib//a'), + file(text).copyWith(newPath: 'lib/a\u0000'), + file(text).copyWith(newPath: 'lib/a\n'), + file(text).copyWith(isNew: true, isRenamed: true), + file(text).copyWith(isDeleted: true, isRenamed: true), + file(text).copyWith(isNew: true), + file(text).copyWith(isDeleted: true), + ]) { + test('rejects contradictory or unsupported metadata $change', () { + expect(validateLiteralOriginalDiff(change), isNull); + }); + } + for (final input in [ + text, + text.substring(0, text.length - 1), + '$text@@ -2 +2 @@\n-a\n+b', + '$text@@ -5 +5 @@\n-a\n+b', + '@@ -0,0 +1,2 @@\n+a\n+b', + '@@ -1,2 +0,0 @@\n-a\n-b', + '@@ -5,0 +6,2 @@\n+a\n+b\n@@ -6 +8 @@\n-a\n+b', + '@@ -1 +1 @@\n-a\n\\ No newline at end of file\n+b\n\\ No newline at end of file\n', + '@@ -1 +1 @@\n same\n\\ No newline at end of file', + ]) { + test('accepts complete literal hunks $input', () { + expect(validateLiteralOriginalDiff(file(input)), isNotNull); + }); + } + test('new deleted and renamed metadata matches literal sides', () { + expect( + validateLiteralOriginalDiff( + file('@@ -0,0 +1 @@\n+a').copyWith(isNew: true), + ), + isNotNull, + ); + expect( + validateLiteralOriginalDiff( + file('@@ -1 +0,0 @@\n-a').copyWith(isDeleted: true), + ), + isNotNull, + ); + expect( + validateLiteralOriginalDiff( + file(text).copyWith(isRenamed: true, newPath: 'other'), + ), + isNotNull, + ); + }); + test( + 'unknown omission metadata stays unknown without synthesizing flags', + () { + final raw = file(text); + final result = validateLiteralOriginalDiff(raw)!; + expect(result.file.isCollapsed, isNull); + expect(result.file.isTooLarge, isNull); + expect(result.file.diff, text); + }, + ); +} diff --git a/docs/literal-original-diff.md b/docs/literal-original-diff.md new file mode 100644 index 00000000..299bca59 --- /dev/null +++ b/docs/literal-original-diff.md @@ -0,0 +1,11 @@ +# Literal original diff selection + +`validateLiteralOriginalDiff` reads a `CommitDiffFile` without using the permissive display parser. It retains the original DTO and returns deeply immutable hunks and lines, or null when local selection cannot be validated. Unknown omission flags remain unknown. + +The validator requires nonempty relative paths without empty/traversal segments or NUL/CR/LF; consistent added/deleted/renamed flags and paths; no reported collapsed/too-large content; and nonempty literal hunk text. Headers use canonical nonnegative decimal coordinates with conservative signed 32-bit bounds. Nonempty sides start above zero; zero-count ranges preserve their literal insertion/deletion offsets. Hunk counts must match every context/removal/addition row. Hunks cannot overlap or run backwards, and omitted unchanged gaps must advance both sides equally. Added/deleted files require one hunk beginning at line one on their existing side. + +Unknown prefixes, preambles, trailers, malformed headers, blank body rows, extra/truncated rows and stray/repeated no-newline markers reject the whole file. Only the exact `\ No newline at end of file` marker is accepted after a content row, and no subsequent row may consume that ended side. A final LF is framing; content whitespace and CR bytes are preserved without normalization. + +`literalOriginalDiffRange` validates the whole raw file before matching original line codes and side coordinates. It shares the existing range matcher: endpoints must be unique and ordered, and multi-hunk selections require contiguous available coordinates. Returned selections are immutable. Existing `originalDiffRange` and permissive display parsing retain their behavior. + +This local contract is deliberately conservative; unsupported valid formats can remain readable through existing display flows. It does not prove complete server file coverage, choose original commit references or make a private write safe. Captured origin/account checks, fresh MR membership, authoritative original reference semantics and guarded controller/private-save reservation/recovery UI remain necessary follow-ups. No live instance/device validation or UI change is included. diff --git a/docs/mobile-web-parity.md b/docs/mobile-web-parity.md index dffe7458..df9246d5 100644 --- a/docs/mobile-web-parity.md +++ b/docs/mobile-web-parity.md @@ -57,7 +57,7 @@ issues and PRs. Keep it open until its full acceptance boundary is verified. | MW-04 | P1 | Member invitations, role/expiry changes, and removal for groups/projects. | Queued | Issue needed | | MW-05 | P0 | Issue/work-item editing, metadata, types, and validated state transitions. | In progress | [#329](https://github.com/Theorvane/labfox/issues/329), [PR #330](https://github.com/Theorvane/labfox/pull/330) shipped title and description editing; [#339](https://github.com/Theorvane/labfox/issues/339), [PR #340](https://github.com/Theorvane/labfox/pull/340) shipped due-date editing; [#341](https://github.com/Theorvane/labfox/issues/341), [PR #342](https://github.com/Theorvane/labfox/pull/342) shipped issue-label editing; [#343](https://github.com/Theorvane/labfox/issues/343), [PR #344](https://github.com/Theorvane/labfox/pull/344) shipped assignee editing; [#345](https://github.com/Theorvane/labfox/issues/345), [PR #346](https://github.com/Theorvane/labfox/pull/346) shipped milestone editing; [#347](https://github.com/Theorvane/labfox/issues/347), [PR #348](https://github.com/Theorvane/labfox/pull/348) shipped confidentiality; [#349](https://github.com/Theorvane/labfox/issues/349), [PR #350](https://github.com/Theorvane/labfox/pull/350) shipped discussion locking. Type-conversion compatibility and remaining work-item actions need separate issues. | | MW-06 | P1 | Boards and iterations: discover mobile-web behavior, then list/detail/mutations. | Queued | Issue needed | -| MW-07 | P0 | Advanced MR review: audit inline discussions/suggestions and finish missing review/approval flows. | In progress | [#552](https://github.com/Theorvane/labfox/issues/552), [PR #553](https://github.com/Theorvane/labfox/pull/553) prevent overlapping approval, merge, and other MR commands per project/IID, including while the repository resolves; failures release the reservation for explicit retry. [#554](https://github.com/Theorvane/labfox/issues/554), [PR #555](https://github.com/Theorvane/labfox/pull/555) add repository-session observation, pre-dispatch cancellation and late-result/error isolation, success-only current-session refresh/analytics/inbox effects, cancelled to-do outcomes without current-account toasts, and generation-owned duplicate reservations. Already dispatched server writes cannot be undone. [#556](https://github.com/Theorvane/labfox/issues/556), [PR #557](https://github.com/Theorvane/labfox/pull/557) add localized approval-read loading/error states, disabled approval toggles until successful reads, read-only retry, retained-data refresh protection, reactive new-session reads with stale-result isolation, and current-state toggle selection while preserving unavailable endpoint compatibility and independent merge controls. [#558](https://github.com/Theorvane/labfox/issues/558), [PR #559](https://github.com/Theorvane/labfox/pull/559) derives approval ownership from the authenticated account user ID and documented `approved_by` membership, ignores conflicting legacy current-user flags, and refreshes membership on account replacement. Regression coverage checks real toggle dispatch on mobile/desktop in both themes. [#560](https://github.com/Theorvane/labfox/issues/560), [PR #561](https://github.com/Theorvane/labfox/pull/561) validates successful approval responses before model parsing: explicit approver lists, complete user wrappers, positive integral user IDs, and nonnegative integral counts when present. Malformed reads become sanitized domain errors; status mapping precedes payload parsing. End-to-end widget tests verify blocked approval toggles and read-only recovery in both widths/themes, while unavailable endpoint compatibility remains intact. [#562](https://github.com/Theorvane/labfox/issues/562), [PR #563](https://github.com/Theorvane/labfox/pull/563) adds an account-bound, one-page MR discussion reader with generated grouped-note models and nullable resolution metadata. It preserves next-page headers without assuming totals or fetching ahead, rejects incomplete groups and nonadvancing cursors, and keeps HTTP failures distinct from sanitized parsing errors. Building on the API/models foundation, [#564](https://github.com/Theorvane/labfox/issues/564), [PR #565](https://github.com/Theorvane/labfox/pull/565) connects grouped MR replies to the detail-screen comments flow and aggregates resolution across all resolvable notes without claiming unknown states are resolved. The shared top-level composer refreshes discussion page one after posting; explicit load-more and read/page retries preserve groups and cursors. Account-bound reads/posts ignore late results, drafts reset when the session or resource changes, and resizing preserves the current draft. Five locales and narrow/compact/wide light/dark layouts are covered. [#566](https://github.com/Theorvane/labfox/issues/566), [PR #567](https://github.com/Theorvane/labfox/pull/567) adds the MR discussion reply API foundation: encoded thread/project paths, explicit IID routing, exact nonempty Markdown bodies, validated generated note responses, sanitized typed errors and no automatic authentication replay for the write. [#568](https://github.com/Theorvane/labfox/issues/568), [PR #569](https://github.com/Theorvane/labfox/pull/569) connects a localized inline reply composer to the selected discussion. One active reply preserves the top-level draft; cancel and write reservations prevent accidental overlaps. Replies preserve Markdown, retain failed drafts for explicit retry, refresh authoritative discussion page one and ignore old-session completions. Account/MR replacement resets the selection, resizing and pagination preserve current drafts, and a removed target restores top-level submission. Five locales and three widths in both themes are covered. [#570](https://github.com/Theorvane/labfox/issues/570), [PR #571](https://github.com/Theorvane/labfox/pull/571) adds known-state Resolve/Reopen controls and a strict account-bound mutation: encoded identity, explicit resolution, no redirects/authentication replay, and updated thread/state confirmation. Resolution shares comment/reply write reservations, preserves the top-level draft, retains localized permission/failure feedback for explicit retry, and refreshes discussions and MR detail only on current-session success. Unknown/nonresolvable states offer no toggle, and an active reply draft prevents resolution. [#572](https://github.com/Theorvane/labfox/issues/572), [PR #573](https://github.com/Theorvane/labfox/pull/573) preserves generated diff-note position metadata: original version SHAs, renamed paths, old/new text lines, multiline range endpoints and image/file types. Discussion reads, replies and resolution responses reject malformed or fractional coordinates before parsing; absent legacy fields remain unknown. [#574](https://github.com/Theorvane/labfox/issues/574), [PR #575](https://github.com/Theorvane/labfox/pull/575) adds paginated diff-version reads and an explicitly selected original unified-diff snapshot. Generated version/file models retain SHA triplets, renamed paths, raw text and nullable omission flags; absent files differ from empty snapshots. Identity/payload validation, status-first typed failures and no eager pagination or current-diff fallback are covered. [#576](https://github.com/Theorvane/labfox/issues/576) / [PR #577](https://github.com/Theorvane/labfox/pull/577) adds an expandable original-diff context beside positioned notes. It matches complete SHA triplets, both file paths and exact old/new single-line text coordinates; explicit older-page reads and read-only retries preserve drafts. The matched original hunk/line has an accessible non-color marker. Unsupported, omitted, truncated or ambiguous contexts remain unavailable without a current-diff fallback; account/resource replacement resets panels and stale reads cannot dispatch follow-up snapshots or affect the new session. Five locales, three widths and both themes are covered. Multiline context, positioned thread creation and suggestion application remain separate slices. [#578](https://github.com/Theorvane/labfox/issues/578), [PR #580](https://github.com/Theorvane/labfox/pull/580) adds the single-line positioned discussion creation API foundation: exact original SHA/path/line anchors, Markdown preservation, pre-dispatch argument validation, no redirects or authentication replay, created-thread confirmation and sanitized typed failures. The line selector/composer, multiline creation and suggestions remain separate. [#579](https://github.com/Theorvane/labfox/issues/579), [PR #581](https://github.com/Theorvane/labfox/pull/581) connects the latest authoritative diff-version snapshot to localized single-line selection and discussion creation. Exact original coordinates, indexed eligibility, shared write reservations, retained drafts, session/resource isolation and visible authoritative discussion inspection before uncertain-write retry are covered. The existing MR Changes route restores from project/IID; commit diffs stay read-only. Multiline context/creation and suggestion application remain separate. [#582](https://github.com/Theorvane/labfox/issues/582) / [PR #583](https://github.com/Theorvane/labfox/pull/583) adds typed immutable suggestion metadata to MR discussion responses, preserving original/replacement code, inclusive ranges, applied status and both documented applicability spellings. Missing states remain unknown; positive integral identities/coordinates, range order, response-wide uniqueness, content/flag types and alias agreement are validated before generated parsing. Suggestion presentation/application and multiline context/creation remain separate. [#584](https://github.com/Theorvane/labfox/issues/584) / [PR #585](https://github.com/Theorvane/labfox/pull/585) adds read-only suggestion previews beside user notes with exact selectable original/replacement code, nullable application/applicability states, localized ranges and empty/unavailable content, bounded code panes, retained drafts across resize/theme, and account/resource/metadata isolation. Suggestion application and batching remain separate. [#586](https://github.com/Theorvane/labfox/issues/586) / [PR #587](https://github.com/Theorvane/labfox/pull/587) adds the single-suggestion application API foundation with a positive global identity, exact optional commit messages, strict HTTP 200 and matching applied=true confirmation, shared metadata validation, typed sanitized failures, and no redirect/authentication replay. Application UI/session reservations and batch application remain separate. [#588](https://github.com/Theorvane/labfox/issues/588) / [PR #589](https://github.com/Theorvane/labfox/pull/589) adds explicit single-suggestion confirmation with literal code and optional commit messages, a fresh exact-discussion preflight, shared reservations including pagination, current-view/account isolation, confirmed-success detail/discussion/review-snapshot refreshes, and draft-preserving read-only recovery before uncertain-write retry. Five locales, three widths, both themes, keyboard insets and large text are covered. Batch application and multiline context/creation remain separate. [#590](https://github.com/Theorvane/labfox/issues/590) / [PR #591](https://github.com/Theorvane/labfox/pull/591) adds the batch suggestion API foundation: one account-bound request with immutable positive unique global IDs, exact optional messages, strict complete matching applied-state confirmation, preserved server order/nullable metadata, status-first sanitized failures, and no redirects, authentication replay or single-write fallback. Building on that API, [#592](https://github.com/Theorvane/labfox/issues/592) / [PR #593](https://github.com/Theorvane/labfox/pull/593) adds explicit loaded-target selection and multi-patch confirmation, deduplicated all-thread preflight before one batch, immutable selected identities, shared reservations, current-account/view isolation, complete staged read-only recovery, exact retained drafts and success-only refreshes. Five locales, three widths, both themes and compact keyboard/large-text layouts are covered; see [the batch UI](mr-suggestion-batch-ui.md). [#594](https://github.com/Theorvane/labfox/issues/594) / [PR #595](https://github.com/Theorvane/labfox/pull/595) adds exact original multiline context with inclusive side-aware per-line markers, full filename-hash/raw-counter endpoint identity, optional coordinate support, complete contiguous hunk checks, duplicate/reversed/gapped-span rejection and retained draft/session behavior. Five locales, three widths, both themes and compact keyboard/large-text layouts are covered; see [original context](mr-discussion-context.md). [#596](https://github.com/Theorvane/labfox/issues/596) / [PR #597](https://github.com/Theorvane/labfox/pull/597) adds explicit original multiline selection and positioned creation, indexed complete contiguous range eligibility, exact endpoint request/response confirmation, inclusive accessible markers, retained drafts and complete-range recovery inspection. Repository identity is checked before dispatch and after completion to isolate replaced-session outcomes. Five locales, three widths, both themes and compact keyboard/large-text layouts are covered; see [multiline creation](mr-multiline-discussions.md). [#598](https://github.com/Theorvane/labfox/issues/598) / [PR #599](https://github.com/Theorvane/labfox/pull/599) adds the read-only pending-review draft foundation: generated private note/global MR/author identities, exact Markdown and nullable original position/reply/resolution metadata; strict one-page draft_notes reads with immutable server order, header pagination, sanitized malformed/status failures and normal read-only OAuth refresh. Account-bound repositories reject other-author pages; auto-disposed page consumers prevent stale pre-dispatch reads and ignore late outcomes. Presentation removes retained private rows immediately during account/client/instance changes, sign-out, loading and failed refresh. Draft mutations, aggregation and pending-review UI remain separate; no live GitLab/device validation is claimed. [#600](https://github.com/Theorvane/labfox/issues/600) / [PR #601](https://github.com/Theorvane/labfox/pull/601) adds the unpublished regular/original text-range draft creation API prerequisite: exact Markdown and recursive null omission, strict HTTP 201 and original-position/body confirmation, no redirect/OAuth replay/automatic retry/public-comment fallback, and sanitized typed/status-preserving failures. Account-bound repositories require and confirm captured author plus explicit global MR identity separately from route IID. Shared text/range validation preserves published discussion behavior. Replies, commit/image/file drafts, editing/deletion, publication and the pending-review controller/UI remain follow-up work; uncertain writes require authoritative inspection before an explicit retry, and no live GitLab/device validation is claimed. [#602](https://github.com/Theorvane/labfox/issues/602) / [PR #603](https://github.com/Theorvane/labfox/pull/603) adds read-only pending-review pagination with an authoritative global MR identity separate from route IID, immutable server order, explicit cursors through empty pages, optional totals and duplicate-load prevention. Mismatched author/MR identities, duplicate drafts and backward cursors reject the full result. Account/repository generations cancel obsolete dispatch and isolate initial/pagination success/errors. Presentation immediately removes retained private rows during account/client/instance/sign-out, refresh and failed reads; explicit retry restarts page one. Offset traversal is not an atomic snapshot and cannot authorize publication. The controller exposes no UI or mutation; see [private pagination](mr-pending-review-reader.md). [#604](https://github.com/Theorvane/labfox/issues/604) / [PR #605](https://github.com/Theorvane/labfox/pull/605) adds a read-only private pending-review panel on MR detail with exact selectable saved Markdown, known original metadata, localized loading/empty/partial/error states and explicit paging/refresh/retry. Guarded queries await fresh MR detail and verify route/global identity before dispatch, closing account/instance/client replacement requests ahead of detail; old private rows disappear during detail refresh and failed reads. Five locales, three widths, both themes, large text, long content and resize/theme preservation are covered with six synthetic captures. The panel sends no mutation or embedded image request and never infers/re-anchors original positions; see [private review panel](mr-pending-review-panel.md). Creation orchestration, uncertain-write recovery and publication remain follow-ups; no live GitLab/device validation is claimed. [#606](https://github.com/Theorvane/labfox/issues/606) / [PR #607](https://github.com/Theorvane/LabFox/pull/607) adds regular private review save orchestration with fresh account-bound detail/global identity, shared discussion write/pagination reservations, exact Markdown and one confirmed create before route-scoped private-reader refresh. Uncertain writes block replay across ordinary refreshes and same-account client/repository replacement; explicit complete read-only inspection follows empty-page cursors, validates ownership/identity/uniqueness/order and waits for any dispatched write to settle. Old-session/view results and errors cannot expose private rows or dispatch further pages. The controller has no composer UI, positioned/reply save orchestration, editing/deletion or publication; see [guarded saves](mr-pending-review-save.md). Recovery is in-memory and offset inspection cannot promise exactly-once creation after a client timeout; no live-instance/device validation is claimed. [#608](https://github.com/Theorvane/LabFox/issues/608) / [PR #609](https://github.com/Theorvane/LabFox/pull/609) adds a localized regular private-note composer on MR detail with exact Markdown, guarded one-save orchestration, retained failed input, complete visible inspection and separate acknowledgement before manual retry. Account/client/repository/resource replacement discards private input and snapshots; obsolete results cannot toast or expose data. Public drafts stay intact, and same-account reopen retains uncertain-write gating. Five locales, three widths, both themes and keyboard/large-text recovery are covered with twelve synthetic captures; see [private composer](mr-pending-review-composer.md). Positioned/reply/commit saves, editing/deletion and publication remain separate; no exactly-once or live-instance/device validation is claimed. [#610](https://github.com/Theorvane/LabFox/issues/610) / [PR #611](https://github.com/Theorvane/LabFox/pull/611) adds private draft update/delete API and account-bound repository foundations: exact Markdown, explicit original text-position preservation, strict 200 target/body/position confirmation and 204 deletion acknowledgement, positive project/IID/draft identities, captured author/global-MR checks and unchanged non-body metadata. Status-first plain-wire decoding preserves typed failures even for malformed JSON; redirects, authentication replay, fallback writes and automatic retry are disabled. Original line-code-only and unsupported update anchors fail before dispatch; owned opaque/image/file drafts can be deleted by identity. See [maintenance contract](mr-draft-note-maintenance.md). Fresh target/controller reservations, localized edit/delete confirmation/recovery and publication remain follow-ups; no live-instance/device validation is claimed. [#612](https://github.com/Theorvane/LabFox/issues/612) connects private draft editing and confirmed deletion to the existing pending review panel. Shared discussion reservations, fresh detail/global identity and complete private-page comparison refuse stale or missing selected targets before one write. Uncertain results gate every private write; complete visible inspection and acknowledgement adopt a current target only for explicit retry, with obsolete session/view input and outcomes discarded. Five locales, keyboard/large text and twelve synthetic captures cover three widths and both themes; see [maintenance UI](mr-pending-review-maintenance.md). Publication and positioned/reply/commit creation remain separate; no atomic snapshot, conditional-write, durable recovery or live-instance/device guarantee is claimed. [#614](https://github.com/Theorvane/LabFox/issues/614) adds whole-review publication: complete visible private-note confirmation and consent, a fresh full-set comparison before one strict-204 account-scoped bulk POST, and uncertainty that blocks private mutations until complete private/public inspection. Manual retries require new consent; session/view changes discard old confirmation and outcomes. Five locales and three widths in both themes are covered; see [publication flow](mr-pending-review-publication.md). Single-note publication, summary/reviewer-state controls and positioned/reply/commit creation remain separate. No automatic approval/merge, atomic server snapshot, durable recovery or live-instance/device guarantee is claimed. [#616](https://github.com/Theorvane/labfox/issues/616) adds private text line/range draft creation from the MR changes screen, fresh authoritative diff selection checks, immutable selected text, session/snapshot/origin isolation, preserved public input and explicit uncertain-save inspection. See [private inline review](mr-pending-review-inline.md). [#618](https://github.com/Theorvane/LabFox/issues/618) adds selected saved-note publication through one strict-204 PUT, fresh exact target comparison, explicit consent and shared two-sided recovery. Unrelated saved notes remain private; a missing selected ID cannot publish another note. See [single-note publication](mr-pending-review-single-publication.md). [#620](https://github.com/Theorvane/LabFox/issues/620) adds whole-review submission with an exact public summary, explicit reviewed/request-changes outcomes, complete current-reviewer reads, fresh reviewer preflight comparison and postwrite outcome verification. Partial or obsolete outcomes require private/public/reviewer inspection after actual write settlement and renewed consent, including selected-note recovery entry. Five locales, three widths, both themes and keyboard/large text are covered; see [review submission](mr-review-submission.md). Formal approval remains separate; additional draft creation types are follow-ups. No atomic, durable recovery or live-instance/device guarantee is claimed. [#622](https://github.com/Theorvane/LabFox/issues/622) adds private reply drafts on existing public discussion groups, exact target/body and non-resolution confirmation, a fresh selected-thread preflight, shared command reservations, and actual-settlement recovery that stages every private page together with the original public target. Missing targets stay missing; scoped recovery cannot clear publication/reviewer uncertainty. The localized shared composer preserves unsent public comments/replies and survives its own detail refresh or target removal. Five locales, keyboard/large text and twelve synthetic captures cover three widths and both themes; see [private replies](mr-private-review-replies.md). Commit/image/file draft creation remains separate; no atomic, durable recovery or live-instance/device guarantee is claimed. [#624](https://github.com/Theorvane/LabFox/issues/624) adds the private commit-text draft API/repository foundation: exact commit/body/original text position, matching head SHA, strict private acknowledgement and captured author/global MR validation, with no redirect/auth replay/retry. No user action is exposed; guarded commit selection, membership/freshness, shared reservations and recovery remain follow-ups. See [commit-text draft API](mr-commit-text-draft-api.md). Image/file creation and MW-07 remain in progress. [#626](https://github.com/Theorvane/LabFox/issues/626) adds MR-scoped commit reads and complete advertised-page traversal, preserving unknown/root/ordered parent metadata. Strict SHA-1 identities, offset and same-resource Link cursor checks, cross-page duplicate rejection, immutable results and origin currency guards prevent partial or obsolete membership exposure. Read-only OAuth refresh remains account-bound. See [MR commit membership reads](mr-commit-membership-reads.md). Original commit diff selection and guarded private-save/recovery UI remain follow-ups; no atomic snapshot or live-instance guarantee is claimed. [#628](https://github.com/Theorvane/LabFox/issues/628) adds fixed-SHA literal commit diff pages and guarded advertised-page traversal, retaining raw paths/text and unknown omission flags. Shared offset validation preserves MR membership and confirms unified format plus exact route echoes. Diff file limits can stop pagination before full coverage; no original coordinate, membership or private-write preflight is inferred. See [original commit diff reads](original-commit-diff-reads.md). Authoritative fork/parent context and guarded literal selection/save/recovery UI remain follow-ups. [#630](https://github.com/Theorvane/LabFox/issues/630) preserves separate nullable fork project identities and adds a fresh captured MR detail context read. Strict integer and route/global identity checks reject malformed or mismatched responses; unknown target context is never inferred, while unknown source metadata stays unknown. Currency guards discard obsolete results and typed failures; read-only authentication remains captured. See [MR commit review context](mr-commit-review-context.md). Original parent/reference validation and guarded literal selection/save/recovery UI remain follow-ups; no atomic snapshot, original-coordinate or write-eligibility guarantee is claimed. [#632](https://github.com/Theorvane/LabFox/issues/632) adds strict fixed-head commit detail and guarded captured-account reads. Exact SHA and ordered nullable parent checks preserve unknown/root/merge metadata without synthesizing references; status-first failures, disabled redirects and obsolete-result isolation retain honest context. General branch/tag reads remain compatible. See [fixed commit review reads](fixed-commit-review-reads.md). Original reference/coordinate validation and guarded private-save/recovery UI remain follow-ups; no membership, atomic snapshot or write-eligibility guarantee is inferred. Captured original commit aggregation follows in #634 ([contract](mr-original-commit-context.md)); guarded literal coordinates and private-save UI remain in progress. | +| MW-07 | P0 | Advanced MR review: audit inline discussions/suggestions and finish missing review/approval flows. | In progress | [#552](https://github.com/Theorvane/labfox/issues/552), [PR #553](https://github.com/Theorvane/labfox/pull/553) prevent overlapping approval, merge, and other MR commands per project/IID, including while the repository resolves; failures release the reservation for explicit retry. [#554](https://github.com/Theorvane/labfox/issues/554), [PR #555](https://github.com/Theorvane/labfox/pull/555) add repository-session observation, pre-dispatch cancellation and late-result/error isolation, success-only current-session refresh/analytics/inbox effects, cancelled to-do outcomes without current-account toasts, and generation-owned duplicate reservations. Already dispatched server writes cannot be undone. [#556](https://github.com/Theorvane/labfox/issues/556), [PR #557](https://github.com/Theorvane/labfox/pull/557) add localized approval-read loading/error states, disabled approval toggles until successful reads, read-only retry, retained-data refresh protection, reactive new-session reads with stale-result isolation, and current-state toggle selection while preserving unavailable endpoint compatibility and independent merge controls. [#558](https://github.com/Theorvane/labfox/issues/558), [PR #559](https://github.com/Theorvane/labfox/pull/559) derives approval ownership from the authenticated account user ID and documented `approved_by` membership, ignores conflicting legacy current-user flags, and refreshes membership on account replacement. Regression coverage checks real toggle dispatch on mobile/desktop in both themes. [#560](https://github.com/Theorvane/labfox/issues/560), [PR #561](https://github.com/Theorvane/labfox/pull/561) validates successful approval responses before model parsing: explicit approver lists, complete user wrappers, positive integral user IDs, and nonnegative integral counts when present. Malformed reads become sanitized domain errors; status mapping precedes payload parsing. End-to-end widget tests verify blocked approval toggles and read-only recovery in both widths/themes, while unavailable endpoint compatibility remains intact. [#562](https://github.com/Theorvane/labfox/issues/562), [PR #563](https://github.com/Theorvane/labfox/pull/563) adds an account-bound, one-page MR discussion reader with generated grouped-note models and nullable resolution metadata. It preserves next-page headers without assuming totals or fetching ahead, rejects incomplete groups and nonadvancing cursors, and keeps HTTP failures distinct from sanitized parsing errors. Building on the API/models foundation, [#564](https://github.com/Theorvane/labfox/issues/564), [PR #565](https://github.com/Theorvane/labfox/pull/565) connects grouped MR replies to the detail-screen comments flow and aggregates resolution across all resolvable notes without claiming unknown states are resolved. The shared top-level composer refreshes discussion page one after posting; explicit load-more and read/page retries preserve groups and cursors. Account-bound reads/posts ignore late results, drafts reset when the session or resource changes, and resizing preserves the current draft. Five locales and narrow/compact/wide light/dark layouts are covered. [#566](https://github.com/Theorvane/labfox/issues/566), [PR #567](https://github.com/Theorvane/labfox/pull/567) adds the MR discussion reply API foundation: encoded thread/project paths, explicit IID routing, exact nonempty Markdown bodies, validated generated note responses, sanitized typed errors and no automatic authentication replay for the write. [#568](https://github.com/Theorvane/labfox/issues/568), [PR #569](https://github.com/Theorvane/labfox/pull/569) connects a localized inline reply composer to the selected discussion. One active reply preserves the top-level draft; cancel and write reservations prevent accidental overlaps. Replies preserve Markdown, retain failed drafts for explicit retry, refresh authoritative discussion page one and ignore old-session completions. Account/MR replacement resets the selection, resizing and pagination preserve current drafts, and a removed target restores top-level submission. Five locales and three widths in both themes are covered. [#570](https://github.com/Theorvane/labfox/issues/570), [PR #571](https://github.com/Theorvane/labfox/pull/571) adds known-state Resolve/Reopen controls and a strict account-bound mutation: encoded identity, explicit resolution, no redirects/authentication replay, and updated thread/state confirmation. Resolution shares comment/reply write reservations, preserves the top-level draft, retains localized permission/failure feedback for explicit retry, and refreshes discussions and MR detail only on current-session success. Unknown/nonresolvable states offer no toggle, and an active reply draft prevents resolution. [#572](https://github.com/Theorvane/labfox/issues/572), [PR #573](https://github.com/Theorvane/labfox/pull/573) preserves generated diff-note position metadata: original version SHAs, renamed paths, old/new text lines, multiline range endpoints and image/file types. Discussion reads, replies and resolution responses reject malformed or fractional coordinates before parsing; absent legacy fields remain unknown. [#574](https://github.com/Theorvane/labfox/issues/574), [PR #575](https://github.com/Theorvane/labfox/pull/575) adds paginated diff-version reads and an explicitly selected original unified-diff snapshot. Generated version/file models retain SHA triplets, renamed paths, raw text and nullable omission flags; absent files differ from empty snapshots. Identity/payload validation, status-first typed failures and no eager pagination or current-diff fallback are covered. [#576](https://github.com/Theorvane/labfox/issues/576) / [PR #577](https://github.com/Theorvane/labfox/pull/577) adds an expandable original-diff context beside positioned notes. It matches complete SHA triplets, both file paths and exact old/new single-line text coordinates; explicit older-page reads and read-only retries preserve drafts. The matched original hunk/line has an accessible non-color marker. Unsupported, omitted, truncated or ambiguous contexts remain unavailable without a current-diff fallback; account/resource replacement resets panels and stale reads cannot dispatch follow-up snapshots or affect the new session. Five locales, three widths and both themes are covered. Multiline context, positioned thread creation and suggestion application remain separate slices. [#578](https://github.com/Theorvane/labfox/issues/578), [PR #580](https://github.com/Theorvane/labfox/pull/580) adds the single-line positioned discussion creation API foundation: exact original SHA/path/line anchors, Markdown preservation, pre-dispatch argument validation, no redirects or authentication replay, created-thread confirmation and sanitized typed failures. The line selector/composer, multiline creation and suggestions remain separate. [#579](https://github.com/Theorvane/labfox/issues/579), [PR #581](https://github.com/Theorvane/labfox/pull/581) connects the latest authoritative diff-version snapshot to localized single-line selection and discussion creation. Exact original coordinates, indexed eligibility, shared write reservations, retained drafts, session/resource isolation and visible authoritative discussion inspection before uncertain-write retry are covered. The existing MR Changes route restores from project/IID; commit diffs stay read-only. Multiline context/creation and suggestion application remain separate. [#582](https://github.com/Theorvane/labfox/issues/582) / [PR #583](https://github.com/Theorvane/labfox/pull/583) adds typed immutable suggestion metadata to MR discussion responses, preserving original/replacement code, inclusive ranges, applied status and both documented applicability spellings. Missing states remain unknown; positive integral identities/coordinates, range order, response-wide uniqueness, content/flag types and alias agreement are validated before generated parsing. Suggestion presentation/application and multiline context/creation remain separate. [#584](https://github.com/Theorvane/labfox/issues/584) / [PR #585](https://github.com/Theorvane/labfox/pull/585) adds read-only suggestion previews beside user notes with exact selectable original/replacement code, nullable application/applicability states, localized ranges and empty/unavailable content, bounded code panes, retained drafts across resize/theme, and account/resource/metadata isolation. Suggestion application and batching remain separate. [#586](https://github.com/Theorvane/labfox/issues/586) / [PR #587](https://github.com/Theorvane/labfox/pull/587) adds the single-suggestion application API foundation with a positive global identity, exact optional commit messages, strict HTTP 200 and matching applied=true confirmation, shared metadata validation, typed sanitized failures, and no redirect/authentication replay. Application UI/session reservations and batch application remain separate. [#588](https://github.com/Theorvane/labfox/issues/588) / [PR #589](https://github.com/Theorvane/labfox/pull/589) adds explicit single-suggestion confirmation with literal code and optional commit messages, a fresh exact-discussion preflight, shared reservations including pagination, current-view/account isolation, confirmed-success detail/discussion/review-snapshot refreshes, and draft-preserving read-only recovery before uncertain-write retry. Five locales, three widths, both themes, keyboard insets and large text are covered. Batch application and multiline context/creation remain separate. [#590](https://github.com/Theorvane/labfox/issues/590) / [PR #591](https://github.com/Theorvane/labfox/pull/591) adds the batch suggestion API foundation: one account-bound request with immutable positive unique global IDs, exact optional messages, strict complete matching applied-state confirmation, preserved server order/nullable metadata, status-first sanitized failures, and no redirects, authentication replay or single-write fallback. Building on that API, [#592](https://github.com/Theorvane/labfox/issues/592) / [PR #593](https://github.com/Theorvane/labfox/pull/593) adds explicit loaded-target selection and multi-patch confirmation, deduplicated all-thread preflight before one batch, immutable selected identities, shared reservations, current-account/view isolation, complete staged read-only recovery, exact retained drafts and success-only refreshes. Five locales, three widths, both themes and compact keyboard/large-text layouts are covered; see [the batch UI](mr-suggestion-batch-ui.md). [#594](https://github.com/Theorvane/labfox/issues/594) / [PR #595](https://github.com/Theorvane/labfox/pull/595) adds exact original multiline context with inclusive side-aware per-line markers, full filename-hash/raw-counter endpoint identity, optional coordinate support, complete contiguous hunk checks, duplicate/reversed/gapped-span rejection and retained draft/session behavior. Five locales, three widths, both themes and compact keyboard/large-text layouts are covered; see [original context](mr-discussion-context.md). [#596](https://github.com/Theorvane/labfox/issues/596) / [PR #597](https://github.com/Theorvane/labfox/pull/597) adds explicit original multiline selection and positioned creation, indexed complete contiguous range eligibility, exact endpoint request/response confirmation, inclusive accessible markers, retained drafts and complete-range recovery inspection. Repository identity is checked before dispatch and after completion to isolate replaced-session outcomes. Five locales, three widths, both themes and compact keyboard/large-text layouts are covered; see [multiline creation](mr-multiline-discussions.md). [#598](https://github.com/Theorvane/labfox/issues/598) / [PR #599](https://github.com/Theorvane/labfox/pull/599) adds the read-only pending-review draft foundation: generated private note/global MR/author identities, exact Markdown and nullable original position/reply/resolution metadata; strict one-page draft_notes reads with immutable server order, header pagination, sanitized malformed/status failures and normal read-only OAuth refresh. Account-bound repositories reject other-author pages; auto-disposed page consumers prevent stale pre-dispatch reads and ignore late outcomes. Presentation removes retained private rows immediately during account/client/instance changes, sign-out, loading and failed refresh. Draft mutations, aggregation and pending-review UI remain separate; no live GitLab/device validation is claimed. [#600](https://github.com/Theorvane/labfox/issues/600) / [PR #601](https://github.com/Theorvane/labfox/pull/601) adds the unpublished regular/original text-range draft creation API prerequisite: exact Markdown and recursive null omission, strict HTTP 201 and original-position/body confirmation, no redirect/OAuth replay/automatic retry/public-comment fallback, and sanitized typed/status-preserving failures. Account-bound repositories require and confirm captured author plus explicit global MR identity separately from route IID. Shared text/range validation preserves published discussion behavior. Replies, commit/image/file drafts, editing/deletion, publication and the pending-review controller/UI remain follow-up work; uncertain writes require authoritative inspection before an explicit retry, and no live GitLab/device validation is claimed. [#602](https://github.com/Theorvane/labfox/issues/602) / [PR #603](https://github.com/Theorvane/labfox/pull/603) adds read-only pending-review pagination with an authoritative global MR identity separate from route IID, immutable server order, explicit cursors through empty pages, optional totals and duplicate-load prevention. Mismatched author/MR identities, duplicate drafts and backward cursors reject the full result. Account/repository generations cancel obsolete dispatch and isolate initial/pagination success/errors. Presentation immediately removes retained private rows during account/client/instance/sign-out, refresh and failed reads; explicit retry restarts page one. Offset traversal is not an atomic snapshot and cannot authorize publication. The controller exposes no UI or mutation; see [private pagination](mr-pending-review-reader.md). [#604](https://github.com/Theorvane/labfox/issues/604) / [PR #605](https://github.com/Theorvane/labfox/pull/605) adds a read-only private pending-review panel on MR detail with exact selectable saved Markdown, known original metadata, localized loading/empty/partial/error states and explicit paging/refresh/retry. Guarded queries await fresh MR detail and verify route/global identity before dispatch, closing account/instance/client replacement requests ahead of detail; old private rows disappear during detail refresh and failed reads. Five locales, three widths, both themes, large text, long content and resize/theme preservation are covered with six synthetic captures. The panel sends no mutation or embedded image request and never infers/re-anchors original positions; see [private review panel](mr-pending-review-panel.md). Creation orchestration, uncertain-write recovery and publication remain follow-ups; no live GitLab/device validation is claimed. [#606](https://github.com/Theorvane/labfox/issues/606) / [PR #607](https://github.com/Theorvane/LabFox/pull/607) adds regular private review save orchestration with fresh account-bound detail/global identity, shared discussion write/pagination reservations, exact Markdown and one confirmed create before route-scoped private-reader refresh. Uncertain writes block replay across ordinary refreshes and same-account client/repository replacement; explicit complete read-only inspection follows empty-page cursors, validates ownership/identity/uniqueness/order and waits for any dispatched write to settle. Old-session/view results and errors cannot expose private rows or dispatch further pages. The controller has no composer UI, positioned/reply save orchestration, editing/deletion or publication; see [guarded saves](mr-pending-review-save.md). Recovery is in-memory and offset inspection cannot promise exactly-once creation after a client timeout; no live-instance/device validation is claimed. [#608](https://github.com/Theorvane/LabFox/issues/608) / [PR #609](https://github.com/Theorvane/LabFox/pull/609) adds a localized regular private-note composer on MR detail with exact Markdown, guarded one-save orchestration, retained failed input, complete visible inspection and separate acknowledgement before manual retry. Account/client/repository/resource replacement discards private input and snapshots; obsolete results cannot toast or expose data. Public drafts stay intact, and same-account reopen retains uncertain-write gating. Five locales, three widths, both themes and keyboard/large-text recovery are covered with twelve synthetic captures; see [private composer](mr-pending-review-composer.md). Positioned/reply/commit saves, editing/deletion and publication remain separate; no exactly-once or live-instance/device validation is claimed. [#610](https://github.com/Theorvane/LabFox/issues/610) / [PR #611](https://github.com/Theorvane/LabFox/pull/611) adds private draft update/delete API and account-bound repository foundations: exact Markdown, explicit original text-position preservation, strict 200 target/body/position confirmation and 204 deletion acknowledgement, positive project/IID/draft identities, captured author/global-MR checks and unchanged non-body metadata. Status-first plain-wire decoding preserves typed failures even for malformed JSON; redirects, authentication replay, fallback writes and automatic retry are disabled. Original line-code-only and unsupported update anchors fail before dispatch; owned opaque/image/file drafts can be deleted by identity. See [maintenance contract](mr-draft-note-maintenance.md). Fresh target/controller reservations, localized edit/delete confirmation/recovery and publication remain follow-ups; no live-instance/device validation is claimed. [#612](https://github.com/Theorvane/LabFox/issues/612) connects private draft editing and confirmed deletion to the existing pending review panel. Shared discussion reservations, fresh detail/global identity and complete private-page comparison refuse stale or missing selected targets before one write. Uncertain results gate every private write; complete visible inspection and acknowledgement adopt a current target only for explicit retry, with obsolete session/view input and outcomes discarded. Five locales, keyboard/large text and twelve synthetic captures cover three widths and both themes; see [maintenance UI](mr-pending-review-maintenance.md). Publication and positioned/reply/commit creation remain separate; no atomic snapshot, conditional-write, durable recovery or live-instance/device guarantee is claimed. [#614](https://github.com/Theorvane/LabFox/issues/614) adds whole-review publication: complete visible private-note confirmation and consent, a fresh full-set comparison before one strict-204 account-scoped bulk POST, and uncertainty that blocks private mutations until complete private/public inspection. Manual retries require new consent; session/view changes discard old confirmation and outcomes. Five locales and three widths in both themes are covered; see [publication flow](mr-pending-review-publication.md). Single-note publication, summary/reviewer-state controls and positioned/reply/commit creation remain separate. No automatic approval/merge, atomic server snapshot, durable recovery or live-instance/device guarantee is claimed. [#616](https://github.com/Theorvane/labfox/issues/616) adds private text line/range draft creation from the MR changes screen, fresh authoritative diff selection checks, immutable selected text, session/snapshot/origin isolation, preserved public input and explicit uncertain-save inspection. See [private inline review](mr-pending-review-inline.md). [#618](https://github.com/Theorvane/LabFox/issues/618) adds selected saved-note publication through one strict-204 PUT, fresh exact target comparison, explicit consent and shared two-sided recovery. Unrelated saved notes remain private; a missing selected ID cannot publish another note. See [single-note publication](mr-pending-review-single-publication.md). [#620](https://github.com/Theorvane/LabFox/issues/620) adds whole-review submission with an exact public summary, explicit reviewed/request-changes outcomes, complete current-reviewer reads, fresh reviewer preflight comparison and postwrite outcome verification. Partial or obsolete outcomes require private/public/reviewer inspection after actual write settlement and renewed consent, including selected-note recovery entry. Five locales, three widths, both themes and keyboard/large text are covered; see [review submission](mr-review-submission.md). Formal approval remains separate; additional draft creation types are follow-ups. No atomic, durable recovery or live-instance/device guarantee is claimed. [#622](https://github.com/Theorvane/LabFox/issues/622) adds private reply drafts on existing public discussion groups, exact target/body and non-resolution confirmation, a fresh selected-thread preflight, shared command reservations, and actual-settlement recovery that stages every private page together with the original public target. Missing targets stay missing; scoped recovery cannot clear publication/reviewer uncertainty. The localized shared composer preserves unsent public comments/replies and survives its own detail refresh or target removal. Five locales, keyboard/large text and twelve synthetic captures cover three widths and both themes; see [private replies](mr-private-review-replies.md). Commit/image/file draft creation remains separate; no atomic, durable recovery or live-instance/device guarantee is claimed. [#624](https://github.com/Theorvane/LabFox/issues/624) adds the private commit-text draft API/repository foundation: exact commit/body/original text position, matching head SHA, strict private acknowledgement and captured author/global MR validation, with no redirect/auth replay/retry. No user action is exposed; guarded commit selection, membership/freshness, shared reservations and recovery remain follow-ups. See [commit-text draft API](mr-commit-text-draft-api.md). Image/file creation and MW-07 remain in progress. [#626](https://github.com/Theorvane/LabFox/issues/626) adds MR-scoped commit reads and complete advertised-page traversal, preserving unknown/root/ordered parent metadata. Strict SHA-1 identities, offset and same-resource Link cursor checks, cross-page duplicate rejection, immutable results and origin currency guards prevent partial or obsolete membership exposure. Read-only OAuth refresh remains account-bound. See [MR commit membership reads](mr-commit-membership-reads.md). Original commit diff selection and guarded private-save/recovery UI remain follow-ups; no atomic snapshot or live-instance guarantee is claimed. [#628](https://github.com/Theorvane/LabFox/issues/628) adds fixed-SHA literal commit diff pages and guarded advertised-page traversal, retaining raw paths/text and unknown omission flags. Shared offset validation preserves MR membership and confirms unified format plus exact route echoes. Diff file limits can stop pagination before full coverage; no original coordinate, membership or private-write preflight is inferred. See [original commit diff reads](original-commit-diff-reads.md). Authoritative fork/parent context and guarded literal selection/save/recovery UI remain follow-ups. [#630](https://github.com/Theorvane/LabFox/issues/630) preserves separate nullable fork project identities and adds a fresh captured MR detail context read. Strict integer and route/global identity checks reject malformed or mismatched responses; unknown target context is never inferred, while unknown source metadata stays unknown. Currency guards discard obsolete results and typed failures; read-only authentication remains captured. See [MR commit review context](mr-commit-review-context.md). Original parent/reference validation and guarded literal selection/save/recovery UI remain follow-ups; no atomic snapshot, original-coordinate or write-eligibility guarantee is claimed. [#632](https://github.com/Theorvane/LabFox/issues/632) adds strict fixed-head commit detail and guarded captured-account reads. Exact SHA and ordered nullable parent checks preserve unknown/root/merge metadata without synthesizing references; status-first failures, disabled redirects and obsolete-result isolation retain honest context. General branch/tag reads remain compatible. See [fixed commit review reads](fixed-commit-review-reads.md). Original reference/coordinate validation and guarded private-save/recovery UI remain follow-ups; no membership, atomic snapshot or write-eligibility guarantee is inferred. Captured original commit aggregation follows in #634 ([contract](mr-original-commit-context.md)); guarded literal coordinates and private-save UI remain in progress. Strict literal original diff validation and endpoint matching follow in #636 ([contract](literal-original-diff.md)); original refs and guarded private-save UI remain in progress. | | MW-08 | P1 | CI/CD configuration: pipeline editor, variables, triggers, schedule editing, and execution history. | In progress | [#423](https://github.com/Theorvane/labfox/issues/423), [PR #424](https://github.com/Theorvane/labfox/pull/424) cover timing editing; [#425](https://github.com/Theorvane/labfox/issues/425), [PR #426](https://github.com/Theorvane/labfox/pull/426) cover deletion; [#427](https://github.com/Theorvane/labfox/issues/427), [PR #428](https://github.com/Theorvane/labfox/pull/428) cover creation; [#429](https://github.com/Theorvane/labfox/issues/429), [PR #430](https://github.com/Theorvane/labfox/pull/430) cover ownership transfer; [#431](https://github.com/Theorvane/labfox/issues/431), [PR #432](https://github.com/Theorvane/labfox/pull/432) cover ref/active editing. These slices await review or maintainer merge. [#433](https://github.com/Theorvane/labfox/issues/433), [PR #434](https://github.com/Theorvane/labfox/pull/434) cover schedule-specific execution history in review: newest-first header pagination, retained rows/cursor on failed next pages, retry, run-now/refresh reload with inline errors, stale-request protection, and identifier-only pipeline navigation. History filters, variables/inputs, pipeline configuration editing, and triggers remain separate slices. | | MW-09 | P1 | Wiki creation/editing/deletion and history with conflict handling. | In progress | [#351](https://github.com/Theorvane/labfox/issues/351), [PR #352](https://github.com/Theorvane/labfox/pull/352) shipped Markdown page creation; [#353](https://github.com/Theorvane/labfox/issues/353), [PR #354](https://github.com/Theorvane/labfox/pull/354) shipped editing with best-effort stale-draft detection; [#373](https://github.com/Theorvane/labfox/issues/373), [PR #374](https://github.com/Theorvane/labfox/pull/374) shipped deletion with confirmation and best-effort stale-page detection; [#375](https://github.com/Theorvane/labfox/issues/375), [PR #376](https://github.com/Theorvane/labfox/pull/376) shipped Markdown template selection. [#377](https://github.com/Theorvane/labfox/issues/377), [PR #378](https://github.com/Theorvane/labfox/pull/378) cover template listing and creation in review. Page history and atomic conflict protection remain. The documented wiki REST API can retrieve a page by version SHA but does not list page history. | | MW-10 | P1 | Snippet creation/editing/deletion, files, and visibility. | In progress | [#355](https://github.com/Theorvane/labfox/issues/355), [PR #356](https://github.com/Theorvane/labfox/pull/356) shipped single-file project snippet creation; [#357](https://github.com/Theorvane/labfox/issues/357), [PR #358](https://github.com/Theorvane/labfox/pull/358) shipped deletion with confirmation; [#359](https://github.com/Theorvane/labfox/issues/359), [PR #360](https://github.com/Theorvane/labfox/pull/360) shipped title and description editing; [#361](https://github.com/Theorvane/labfox/issues/361), [PR #362](https://github.com/Theorvane/labfox/pull/362) shipped single-file content editing; [#363](https://github.com/Theorvane/labfox/issues/363), [PR #364](https://github.com/Theorvane/labfox/pull/364) shipped private/public visibility editing; [#365](https://github.com/Theorvane/labfox/issues/365), [PR #366](https://github.com/Theorvane/labfox/pull/366) shipped multi-file content editing; [#367](https://github.com/Theorvane/labfox/issues/367), [PR #368](https://github.com/Theorvane/labfox/pull/368) shipped adding a file; [#369](https://github.com/Theorvane/labfox/issues/369), [PR #370](https://github.com/Theorvane/labfox/pull/370) shipped deleting a file; [#371](https://github.com/Theorvane/labfox/issues/371), [PR #372](https://github.com/Theorvane/labfox/pull/372) shipped moving and renaming a file. Broader visibility management remains. |