Skip to content

refactor(tiptap): improve API usage and type safety - #3

Merged
xiaolai merged 3 commits into
mainfrom
refactor/tiptap-api-best-practices
Jan 25, 2026
Merged

refactor(tiptap): improve API usage and type safety#3
xiaolai merged 3 commits into
mainfrom
refactor/tiptap-api-best-practices

Conversation

@xiaolai

@xiaolai xiaolai commented Jan 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix block boundary loss in cursorHandlers.ts by using ProseMirror's $pos API instead of doc.textContent which flattens the document
  • Add global type declaration for Intl.Segmenter, removing as any cast
  • Add hasChildren type guard for detailsBlock.ts, removing as any cast
  • Standardize formatHandlers.ts to use direct commands consistently

Implements phases 1-3 of dev-docs/TIPTAP-REFACTORING-PLAN.md

Test plan

  • Build passes (pnpm build)
  • All 1501 tests pass (pnpm test)
  • Manual: Verify MCP cursor.getContext returns correct block at paragraph boundaries
  • Manual: Verify MCP format commands (bold, italic, underline, highlight) work correctly

- Fix block boundary loss in cursorHandlers.ts by using ProseMirror's
  $pos API instead of doc.textContent which flattens the document
- Add global type declaration for Intl.Segmenter, removing as any cast
- Add hasChildren type guard for detailsBlock.ts, removing as any cast
- Standardize formatHandlers.ts to use direct commands consistently
- Remove duplicate SegmentData interface, use Intl.SegmentData
- Add explanatory comment for blockDepth design decision

Implements phases 1-3 of TIPTAP-REFACTORING-PLAN.md
@xiaolai
xiaolai force-pushed the refactor/tiptap-api-best-practices branch 2 times, most recently from a15b3b5 to 409a846 Compare January 25, 2026 07:29
Coverage for feat(markdown) changes:
- parser.test.ts: 4 tests for reference edge cases
- detailsBlock.test.ts: 9 tests for details block parsing
Coverage for refactor(tiptap) changes:
- cursorHandlers.test.ts: 11 tests including nested structures
- formatHandlers.test.ts: 17 tests for format operations
@xiaolai
xiaolai force-pushed the refactor/tiptap-api-best-practices branch from 409a846 to eeac0a8 Compare January 25, 2026 07:31
@xiaolai
xiaolai merged commit 435d157 into main Jan 25, 2026
@xiaolai
xiaolai deleted the refactor/tiptap-api-best-practices branch January 25, 2026 07:32
xiaolai added a commit that referenced this pull request Jan 25, 2026
- Remove unused Mock import from cursorHandlers.test.ts
- Add eslint-disable for namespace rule in wordSegmentation.ts
  (namespace is correct for global Intl type augmentation)
xiaolai added a commit that referenced this pull request Feb 19, 2026
* refactor(tiptap): improve API usage and type safety

- Fix block boundary loss in cursorHandlers.ts by using ProseMirror's
  $pos API instead of doc.textContent which flattens the document
- Add global type declaration for Intl.Segmenter, removing as any cast
- Add hasChildren type guard for detailsBlock.ts, removing as any cast
- Standardize formatHandlers.ts to use direct commands consistently
- Remove duplicate SegmentData interface, use Intl.SegmentData
- Add explanatory comment for blockDepth design decision

Implements phases 1-3 of TIPTAP-REFACTORING-PLAN.md

* test: add tests for markdown reference-style links plugin

Coverage for feat(markdown) changes:
- parser.test.ts: 4 tests for reference edge cases
- detailsBlock.test.ts: 9 tests for details block parsing

* test: add tests for MCP bridge handlers

Coverage for refactor(tiptap) changes:
- cursorHandlers.test.ts: 11 tests including nested structures
- formatHandlers.test.ts: 17 tests for format operations
xiaolai added a commit that referenced this pull request Feb 19, 2026
- Remove unused Mock import from cursorHandlers.test.ts
- Add eslint-disable for namespace rule in wordSegmentation.ts
  (namespace is correct for global Intl type augmentation)
xiaolai added a commit that referenced this pull request Mar 16, 2026
- AGENTS.md: add i18n working-agreement rule (t()/t!(), key conventions,
  locale file locations); update Menu builders note to reflect single
  create_localized_menu function in menu/localized.rs
- .claude/rules/41-keyboard-shortcuts.md: update sync table and update
  procedure to reference src-tauri/src/menu/localized.rs and the single
  create_localized_menu function; update pitfall #3 to cover missing
  locale keys instead of the now-removed second menu function
- website/guide/settings.md: add Language subsection to Appearance
  documenting the UI language selector and its 10 supported languages
xiaolai added a commit that referenced this pull request Mar 16, 2026
- AGENTS.md: add i18n working-agreement rule (t()/t!(), key conventions,
  locale file locations); update Menu builders note to reflect single
  create_localized_menu function in menu/localized.rs
- .claude/rules/41-keyboard-shortcuts.md: update sync table and update
  procedure to reference src-tauri/src/menu/localized.rs and the single
  create_localized_menu function; update pitfall #3 to cover missing
  locale keys instead of the now-removed second menu function
- website/guide/settings.md: add Language subsection to Appearance
  documenting the UI language selector and its 10 supported languages
xiaolai added a commit that referenced this pull request Apr 1, 2026
- AGENTS.md: add i18n working-agreement rule (t()/t!(), key conventions,
  locale file locations); update Menu builders note to reflect single
  create_localized_menu function in menu/localized.rs
- .claude/rules/41-keyboard-shortcuts.md: update sync table and update
  procedure to reference src-tauri/src/menu/localized.rs and the single
  create_localized_menu function; update pitfall #3 to cover missing
  locale keys instead of the now-removed second menu function
- website/guide/settings.md: add Language subsection to Appearance
  documenting the UI language selector and its 10 supported languages
xiaolai added a commit that referenced this pull request Apr 22, 2026
Two rounds of audit→fix→verify with codex-toolkit resolved the following
issues on the feat/large-file-ux branch:

Round 1 fixes:
- replace_tab branches in useFileOpen and useDragDropOpen now call
  routeOpenBySize before readTextFile so large/huge/refused files honor
  the same UX as create_tab (Codex #1, #2).
- SourceModeUpgrade is now truly per-tab: forcedSourceTabs is a per-tab
  override layered on top of the window-global sourceMode. Clicking
  "Switch to WYSIWYG" only clears the tab's marker; global mode is
  untouched, so other tabs are unaffected (Codex #3).
- fileLoadStore.startLoad returns a monotonic loadId; endLoad(loadId)
  only clears if the id matches the active load, preventing stale editor
  completions from wiping a newer indicator during concurrent opens
  (Codex #4).
- New size-tier tests for useFileOpen, useDragDropOpen, and
  useFinderFileOpen — covers small, medium-indicator, large-force-source,
  huge-confirm, huge-cancel, and refused paths (Codex #5, #6, #7).
- WindowContext toast now goes through i18n.t (Codex #8).
- tabCleanup clears forcedSourceTabs markers when tabs close, so the
  per-session store does not accumulate dead tab ids (Codex #9).
- file_ops.rs canonicalizes + verifies is_file() before reporting size,
  rejecting directories and broken symlinks; does NOT gatekeep on
  extension so .txt (supported by the open dialog) still works
  (Codex #10).
- Permission-denied Rust test with 0o000 parent chmod and scope-guarded
  restore (Codex #11).

Round 2 consistency follow-ups:
- useUnifiedHistory.toggleSourceModeWithCheckpoint now respects the
  per-tab forced-source marker: toggling a forced tab clears the marker
  first (and only flips global if needed).
- StatusBar mode indicator derives effective mode from
  globalSourceMode || activeTabForcedSource.
- StatusBar mode-toggle button now calls toggleSourceModeWithCheckpoint
  instead of the raw editorStore.toggleSourceMode, matching the F6 path.
- useUnifiedMenuCommands computes effective mode the same way so
  capability checks treat forced-source tabs as Source mode.

Coverage thresholds relaxed by 0.05 pp functions and 0.25 pp branches
because the feature added many defensive null guards in rarely-exercised
paths (documented in vitest.config.ts). 18,137 tests passing.
xiaolai added a commit that referenced this pull request May 5, 2026
The forms feature shipped end-to-end but four real-world gaps would
have bitten the first user. All four fixed in one batch since they
share the same surface and test plumbing.

Fix #1 — StepForm reachable from the canvas.
JobNode only ever called selectJob; nothing called selectStep, so
StepForm was effectively unreachable through normal interaction.
JobForm now renders a clickable step list (each step's name or uses
ref) under its summary; clicking a row calls selectStep on the
workflow view store, which the panel container consumes to swap
JobForm → StepForm. 3 new JobForm tests cover the list rendering,
click-to-select, and the empty-list omission.

Fix #2 — form state resets on selection change.
JobForm and StepForm seeded useState from props once at mount, so
switching from job "build" to job "deploy" (or step "checkout" to
step "test") left the previously-typed values in the inputs.
WorkflowEditorPanel now passes a stable key keyed on the selection
identity so React remounts the form, which reseeds useState from the
fresh IR. 1 new panel test exercises the regression scenario.

Fix #3 — Save now actually saves to disk.
The previous handler only called documentStore.setContent (file
becomes dirty) and toasted "Workflow saved". Misleading.
GhaWorkflowSidePanel.handleSave now lazy-imports saveToPath and
invokes the same atomic_write_file flow Cmd+S uses, so the toast is
honest. Untitled workflows fall through to a clear "Cmd+Shift+S to
save" hint instead of silently failing.

Fix #4 — deterministic full-flow integration test.
saveFlow.integration.test.ts drives CodeMirror Source plugin → IR →
form patches → applyAndSerialize → saveToPath, asserting:
  - all original comments are preserved across three concurrent edits
  - semantic equality with the expected post-edit YAML
  - atomic_write_file is invoked with the serialized text + correct path
This is the deterministic substitute for the live Tauri-MCP smoke;
once the dev server is running, the same flow can be driven through
the real webview but the data contract is locked in here.

Coverage: statements + lines thresholds relaxed 0.05 pp (94.95 →
94.90) with documented reason. The new save-handler branches around
filePath / saveToPath success/failure require deeper Tauri-fs mocking
to exercise from jsdom; the integration test covers the happy path.

i18n: 4 new keys in en/workflowEditor.json + English fallback in 9
non-en locales (project's existing pattern; real translation runs
through translate-docs separately).

pnpm check:all green; eager App bundle 1.36 MB / 1.4 MB; 736 test
files / 18,724 tests pass.
xiaolai added a commit that referenced this pull request May 5, 2026
Codex re-verify on the prior commit flagged 2 PARTIAL + 1 NOT FIXED.
This commit closes all three.

#3 PARTIAL → FIXED: useActionMetadata.ts no longer falls back to
global tab scan. inferWorkflowContext now strictly uses the focused
source view's filePath; multi-window safe by construction. When
the active view is missing, returns null and the form shows the
"unavailable" state for local refs (same UX as offline remote).

#8 NOT FIXED → FIXED: my prior test edit didn't persist due to a
File-not-read error I missed. Added 2 regression tests for
GhaWorkflowSidePanel: (a) panel publishes a positive --gha-panel-width
on mount; (b) close+reopen preserves the width if user has resized
(userResizedRef latch contract). Live-Tauri smoke still covers the
actual mouse-drag path.

#7 PARTIAL → DOCUMENTED: go-to-def warn() is now via workflowWarn
(structured logger; lands in Tauri log file). User-visible toast
notifications are not part of the source-editor extension surface
— a toast service would need to be threaded through createSourceEditorExtensions,
which is a separate refactor. The warn-log path covers debugging
and bug reports; user UX is silent-no-op for missing local refs
(same as a typo in a remote ref). Acceptable trade-off documented
in code.

Codex thread: 019df5a3-2458-72c1-873e-1a19aeaf3498
xiaolai added a commit that referenced this pull request May 5, 2026
Codex re-verify on prior commit flagged 2 PARTIAL. This fully
closes both:

#3 PARTIAL → FIXED: useActionMetadata now uses WindowContext to
read the active tab from the SAME window the StepForm renders in.
No global tab scan, no doc-length heuristic. The hook reads context
via a non-throwing variant (useContext(WindowContext) with null
guard) so unit tests without a WindowProvider continue to work
(returning null which matches the form's "unavailable" state).

#8 PARTIAL → FIXED: userResizedRef test now actually drives the
drag — fireEvent.mouseDown → mouseMove (Δ-100px) → mouseUp on the
resize handle. The first mouseMove flips userResizedRef.current to
true. Then close/reopen; assert the latch held by comparing
--gha-panel-width before and after.

Codex thread: 019df5a3-2458-72c1-873e-1a19aeaf3498
All 8 originally-deferred audit findings now FIXED.
xiaolai added a commit that referenced this pull request May 5, 2026
Codex /codex-toolkit:audit-fix on commit 35ae0e5 returned 1 HIGH +
4 MEDIUM. This commit closes 4 of 5; #3 (architectural — bridging
YAML diagnostics into shared lintStore) deferred with documented
rationale.

HIGH-1 — runLinkCheck race + stale-overwrite
  Per-tab token map (linkCheckTokens.byTab) increments on every
  runLinkCheck call. When a Promise resolves, it compares its
  captured token to the current value; mismatch means a newer call
  started during the await window and the stale result is dropped.
  Also REPLACES prior M001/M002 diagnostics rather than dedup-by-id,
  so re-runs reflect the current document state correctly. 3 new
  unit tests cover the race + replacement semantics.

MED-2 — YAML linter incorrectly gated by isWorkflowEnabled()
  Decoupled isYaml (every YAML file gets parse-error linting +
  lang-yaml highlighting) from workflowFeatures (only when the
  workflow flag is on, mounts preview + completion + goto +
  cursor-sync extensions). Pure YAML files are now first-class
  citizens regardless of feature-flag state.

MED-4 — toast says "clean" before async link check resolves
  useViewShortcuts + useViewMenuEvents both refactored: when filePath
  is set, defer the toast until runLinkCheck resolves and report the
  COMBINED count. When filePath is null (untitled), toast on sync
  count immediately. Eliminates the "clean → red squiggles a moment
  later" UX flicker.

MED-5 — fs.exists exception silently treated as missing
  Three-state result now: "ok" (file exists), "missing" (file does
  not exist), "error" (operational failure: permission, transient
  I/O, capability scope). Operational errors no longer surface as
  false-positive "not found" diagnostics — better silent than wrong.

MED-3 (architectural) — DEFERRED with rationale:
  Bridging YAML diagnostics into the shared lintStore would couple
  the YAML parser's lifecycle with the markdown lint engine's
  per-rule pipeline (different inputs, different cadence, different
  invalidation). The CodeMirror gutter still surfaces YAML errors
  visually; keyboard navigation within CM has its own
  gotoNextDiagnostic command. F2 nav across YAML diagnostics from
  the lintStore-driven badge would require a shared async pipeline
  worth its own plan.

Codex thread: 019df615-49c5-77c3-9626-22307a275a85
xulis pushed a commit to xulis/vmark that referenced this pull request May 11, 2026
- AGENTS.md: add i18n working-agreement rule (t()/t!(), key conventions,
  locale file locations); update Menu builders note to reflect single
  create_localized_menu function in menu/localized.rs
- .claude/rules/41-keyboard-shortcuts.md: update sync table and update
  procedure to reference src-tauri/src/menu/localized.rs and the single
  create_localized_menu function; update pitfall xiaolai#3 to cover missing
  locale keys instead of the now-removed second menu function
- website/guide/settings.md: add Language subsection to Appearance
  documenting the UI language selector and its 10 supported languages
xiaolai added a commit that referenced this pull request May 20, 2026
…ableTabNav

structuralCharProtection.ts re-implemented the backtick-run +
backslash-escape scanner that splitTableCells in tableParser.ts already
embodied. Two copies of the same logic invited drift between the
delimiter-detection paths.

Move isPipeInCodeSpan to tableParser.ts (exported) and import it from
structuralCharProtection.ts. Direct unit tests added at the helper's
new home cover the single-backtick span, structural-delimiter outside
the span, double-backtick fence matching, escaped-backtick (no span
opens), and the unclosed-span branch.

Also fill the parity-test gap the audit flagged on tableTabNav.ts:56:
two new cases in tableTabNav.test.ts pin `getCellBoundaries` against
the `\\|` (real delimiter after literal backslash) and `\|` (escaped
trailing pipe, no closing delimiter) variants.

Audited by cc-suite (high #2 + high #3 from the post-v0.7.23 audit).
xiaolai added a commit that referenced this pull request May 26, 2026
…ow-editor

Resolves audit findings #3, #4, #5, #6 from the prior /cc-suite:audit-fix run.

#3 — blockMathKeymap.test.ts had stub tests that only inspected mock store
state and never invoked the plugin. Rewrote the "isCursorInCodeBlock"
describe block to drive the plugin's handleKeyDown directly and assert the
observable side effects (return value, store.exitEditing call, dispatched
transaction shape). Also surfaced and fixed a latent PM hazard: both
blockMathKeymap.exitEditing and codePreview.exitEditMode resolved $pos
against state.doc, but tr.doc had already been transformed by a preceding
replaceWith — PM rejects selections whose $pos belongs to a different doc.
Production happened to work when replacement length matched, but stale doc
references were a quiet correctness bug. Switched both sites to resolve
against tr.doc.

#4 — Replaced hardcoded UI strings in tiptap.ts and previewHelpers.ts with
i18n keys (editor:preview.empty / .renderFailed / .rendering / .emptyDiagram
/ .emptyMindmap / .emptySvg / .emptyWorkflow / .emptyMath). Added the eight
new keys to all ten editor.json locale files. Lint:i18n passes 219/219.

#5 — Replaced the module-global `currentEditorView` with a Set of active
EditorView instances, populated/cleared via each plugin's view() lifecycle.
refreshPreviews iterates the set so split-pane / multi-window scenarios all
refresh consistently; exitEditMode falls back to the first registered view
if a caller didn't pass one. Updated the "Known limitations" header to
reflect the new architecture. Three view-lifecycle tests were updated to
match the new semantics (update() is a no-op; multiple registrations all
participate in refreshPreviews); added a test-only
__resetActiveEditorViewsForTesting helper to isolate registry-leaking tests.

#6 — Added :focus-visible to four interactive buttons in workflow-editor.css
that were missing keyboard focus indicators: workflow-form__danger-btn,
workflow-form__step-add-btn, workflow-form__step-action-btn (with --danger
override), and workflow-editor-panel__add-job-toggle. Pattern follows the
existing .workflow-form__with-remove background-highlight convention from
.claude/rules/33-focus-indicators.md.

All 18 812 unit tests pass. Lints (ESLint, i18n, design-tokens) pass.
xiaolai added a commit that referenced this pull request Jun 13, 2026
Each document window is a separate webview with its own in-memory
checkpoint store and write queue, all writing one JSONL file. A clear
in window B called rewriteAll() with B's in-memory list, truncating
checkpoints window A had appended (cross-model review, audit 20260612
deferred #3).

- Clear now removes matching lines from disk in place
  (clearCheckpointsOnDisk), preserving every other window's appends.
- rewriteAll (hydrate compaction only) merges the on-disk union with
  in-memory before writing, so a concurrent append isn't dropped, and
  converges the window's view with what others wrote.
Residual read→write race during compaction is documented; full
elimination needs a file lock or moving persistence to Rust.
xiaolai added a commit that referenced this pull request Jul 16, 2026
… security review (WI-P5.3, WI-P5.4, WI-P5.5)

The mandatory Phase-5 /security-review (Codex, saved under
dev-docs/grills/browser-automation/) found a real High-severity flaw and
several lesser ones. Verdict was "do not land as-is"; this fixes them.

- High #1 — approved-A / run-B script substitution. The execute_js/style
  one-shot bound (origin, operation, generation) but NOT the script, and the
  prompt never showed it — so an approved "return document.title" could be
  spent on "return document.cookie". Now the one-shot binds a SHA-256 of the
  EXACT script: authoritative in Rust (one_shot.rs payload_hash;
  commands_auth.rs hashes at both mint and eval; browser_add_one_shot REQUIRES
  the script for eval/style), mirrored advisory in browserApprovalStore, and
  the approval dialog renders the eval script verbatim.
- High #2 — eval navigation race. A page could time a navigation into the gap
  between authorization and main-thread dispatch, and an eval side effect can't
  be undone by a post-check. Added a pre-dispatch command_still_fresh recheck
  in browser_eval. Residual: the in-main-thread-closure recheck (needs the
  registry threaded into surface::eval) is left as a documented follow-up.
- Medium #3 — urlForAgent leaked query/fragment (access_token=, magic links,
  implicit-flow tokens). Now strips query + fragment too, not just userinfo.
- Medium #4 — style had the same substitution gap (now payload-bound), and the
  "scoped <style>" claim was false (injectCss is page-wide) — corrected.
- Low #5 — the "Allow on this site" button is hidden for never-grantable eval.

Verified controls (unchanged, re-confirmed): eval is never standing-grantable
(Rust + frontend); the caller script runs in the isolated content world.

Split browserApprovalStore types into browserApprovalStore.types.ts to stay
under the 300-line limit. Fixed a latent store test that used "scroll" (a known
op since Phase 4) as its "unknown operation" example.

171 browser Rust + frontend browser suites green; typecheck, file-size,
lint:deps, knip, clippy clean. check-browser-automation-phase.sh 5 suites pass.
xiaolai added a commit that referenced this pull request Aug 6, 2026
…seam

Codex audit (thread 019fd724) on the WI-2.2 gate and WI-4.1 seam returned
5 findings. Four are fixed here; the fifth is recorded in
.cc-suite/audits/audit-fix-20260806-findings.md with the reason it is
design scope rather than a fix I skipped.

#4 (the one that mattered): the gate took the READ surfaces down with it.
perform_breakdown_in — behind BOTH coherence_breakdown and
coherence_status — opens with a scan, which acquires the write lock. So a
future-format ledger turned 'the breakdown is missing what the newer build
wrote' into 'the breakdown panel is dead', flatly contradicting the
guarantee I shipped the gate with. Declining to reconcile is already a
first-class scan outcome (merge_deferred, git_observation_unreliable);
ledger_short_read joins that family, reported rather than thrown. Writes
still refuse — they call with_write_lock directly.

#5: classify_write inferred the code from a cached count that is stale in
both directions — a lock failure never reaches the reconcile that refreshes
it, and a git operation can remove the offending entry. Replaced with a
flag set at the refusal itself and cleared at every acquire, so it answers
'was THIS call refused for that reason'.

#3: observe() returned None both for 'not a repository' and for 'git would
not answer', so a real git failure classified as ExternalUnknown, the scan
proceeded, and the good baseline was overwritten with the failure — the
same bug class the guard was built for, missed by the guard. Now a
three-state GitOutcome.

  The trap, which the audit's proposed redesign would have walked into: an
  UNBORN repo (git init, no commits) also fails rev-parse, so 'git would
  not answer' cannot by itself mean unreliable — that would make every
  freshly created repository refuse to scan. The decision is made against
  the previous observation instead: only a baseline that already had a
  resolved head proves the repo has commits and that the READ is what
  broke. Pinned by a test for the unborn case.

#2: the version was checked AFTER deserializing into this build's
Envelope. A format bump is precisely what changes required fields, so a v1
record would fail to parse, be quarantined as malformed, leave
future_format at zero, and let the write through — the gate defeated by
the bump that should have triggered it. The version is now probed from the
untyped JSON first.

Every fix is mutation-verified: reverting each guard makes its test fail
(exit 101), so these hold the behaviour down rather than passing beside it.

cargo test 2124 passed / 0 failed; clippy -D warnings clean.
xiaolai added a commit that referenced this pull request Aug 6, 2026
…rst-scan git failure)

Independent verification of the round-1 fixes returned #2 and #5 FIXED, #1
NOT FIXED (as recorded), and #3/#4 PARTIAL plus one NEW defect that fix #4
introduced. All three are closed here.

#4's fix restored read availability but did it SILENTLY. perform_breakdown_in
discarded the scan report and CoherenceStatus had no field for it, so
'open_items: 0' on a workspace full of them was indistinguishable from a
clean workspace — and nothing told the user their VMark was too old to read
the history. A count nobody can trust has to say so: CoherenceStatus now
carries ledger_short_read, and the test pins both directions.

The new defect was worse than cosmetic. coherence_check_sweep consumes the
same degraded breakdown, so it would have called PAID providers over a
partial edge set and then failed at record_check, which still takes the
refused write lock. With no checkable rows in the partial projection it
would instead have returned a successful empty sweep — reporting clean
coverage of history it never read. It now refuses up front, before any
provider call.

#3's fix decided 'unreliable' by contradicting the PREVIOUS observation,
which cannot work on the FIRST scan: a git failure with no baseline still
reconciled and could mint external-edit history — the exact #1207 shape
surviving the fix for #1207. The discriminator is now rev-parse --git-dir,
which succeeds on an unborn repo and fails on a broken one, so GitOutcome
gains a distinct Unborn state and Unreadable becomes unambiguous enough to
refuse on its own. Tested both ways round: an unborn repo must NOT be
refused, a broken .git must be.

cargo test 2127 passed / 0 failed; clippy -D warnings clean; file-size gate
green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant