Skip to content

Refactor#149

Merged
owjs3901 merged 103 commits into
mainfrom
refactor
Jun 8, 2026
Merged

Refactor#149
owjs3901 merged 103 commits into
mainfrom
refactor

Conversation

@owjs3901

Copy link
Copy Markdown
Contributor

No description provided.

owjs3901 added 30 commits May 20, 2026 16:02
impl parallel
impl erd svg
# Conflicts:
#	.gitignore
#	Cargo.lock
Two audit documents recording the Wave 10 and follow-up work:

- PERFORMANCE-AUDIT.md: planner diff -44%% via Vec capacity hints + BTreeSet adjacency + index-lookup sort

- PERFORMANCE-AUDIT-FOLLOWUP.md: SeaORM FK target cache + entity_count optimization (1.08x-1.90x on new benches); Task D reverted; skipped items documented
Empty .gitkeep files reserve apps/vscode-extension/ and apps/zed-extension/ for Session B scaffolding (VSCode + Zed extensions). LICENSE already at workspace root satisfies Zed's Oct 2025 license requirement.
Pure data layer for the LSP — no LSP handlers yet:

- ParserPool wraps tree-sitter-json + tree-sitter-yaml in Mutex<Parser>

  (Parser is !Send; V1 full reparse is fine for <5KB model files)

- DocumentState couples FullTextDocument (UTF-16-aware) with Tree

- DocumentStore is a DashMap<Uri, DocumentState> — justified hot path

  exception to BTreeMap policy; documented inline

- Backend now holds Arc<DocumentStore>; handlers come in W2-T2

lsp-textdocument 0.5 uses upstream lsp_types 0.97 directly (distinct from

tower-lsp-server's re-exported ls_types), so lsp-types is pulled in as a

direct dependency for change-event construction at the data layer.
…kspace index

- position.rs: UTF-16 <-> byte offset + ls_types <-> lsp_types bridges

- workspace_index.rs: BTreeMap<table_name, Uri> via tree-sitter walk

- store.rs: with_doc() accessor returning (text, tree)

- backend.rs: did_open/did_change/did_close handlers + reindex helper

Property-style position tests cover CJK + emoji surrogate pairs.
- diagnostics/ module is LSP-types-free domain layer

- 3-tier validation: tree-sitter syntax → serde parse → planner validate_schema

- mapper translates DomainDiagnostic → ls_types::Diagnostic at I/O seam

- Backend wires publish on did_open/did_change, clears on did_close

- Diagnostic codes: syntax-error, parse-error, validate-schema
- hover/: column metadata + FK target preview markdown

- definition/: cross-file FK ref_table → target file location

- Backend declares hover_provider + definition_provider

- All domain modules LSP-types-free; bridges in position.rs
…e refs)

- completion/ module is LSP-types-free domain layer

- Context detection via tree-sitter key path ancestry

- Hardcoded vespertide schema knowledge (V1; V2 may read schemas.json)

- Cross-file ref_columns: deserialize target table, list its columns

- Sort priority follows sqls pattern (specific > generic)

- New backend handler + completion_provider capability
Drift detection — unique to vespertide-lsp, no competitor offers this:

- Loads models + applied migrations via vespertide-loader

- Reconstructs baseline via schema_from_plans

- Diffs against current models; emits Info diagnostic per drifted table

- Pure file-based — no live DB connection required

Formatting:

- JSON: serde_json pretty (2-space indent), round-trip semantic equivalence

- YAML: serde_yaml default

- Falls through silently on parse errors (diagnostics surface those)
Triggers on tag push matching lsp-v* (or workflow_dispatch). Builds:

- linux-x86_64 / linux-aarch64 (via cross)

- darwin-x86_64 (macos-13) / darwin-aarch64 (macos-latest)

- windows-x86_64 (msvc)

Packages as tar.gz (Unix) or zip (Windows), with SHA256.

Uploads assets to the matching GitHub Release via softprops/action-gh-release@v2.

Consumed by Zed extension's latest_github_release() lookup.
Scaffolds apps/vscode-extension/ with:

- package.json: vscode-languageclient 9.0.1, esbuild 0.28, vsce 3.9

- engines.vscode ^1.105.0 (2026-05 baseline)

- contributes.languages: vespertide-json + vespertide-yaml with file glob patterns

- src/extension.ts: LSP client launch with stdio transport, binary resolver (vespertide.serverPath override -> bundled platform-specific binary)

- Commands: vespertide.restartServer

- Status bar item showing connection state

- Platform-specific binaries via bin/<platform>/ (populated by Session B Wave 9 CI)

- DevFive-branded README
Scaffolds apps/zed-extension/ as a standalone crate (NOT workspace member):

- Cargo.toml: cdylib + zed_extension_api 0.7.0, edition 2021 (Zed convention)

- workspace Cargo.toml: exclude = [\"apps/zed-extension\"] to preserve edition + target

- extension.toml: registers vespertide-lsp language server for JSON + YAML

- src/lib.rs: language_server_command + GitHub Release binary auto-download

  - PATH lookup -> cached binary -> download from latest GH Release

  - Cleans up old versions on update

  - Pure asset-matching unit test

- languages/{vespertide-json,vespertide-yaml}/config.toml: file associations

- LICENSE: Apache-2.0 (REQUIRED by Zed registry since Oct 2025)

- DevFive-branded README
Triggers on tag push matching vscode-v* (or workflow_dispatch with lsp_tag input).

5-platform matrix:

- Downloads matching vespertide-lsp asset from latest (or specified) lsp-v* GH Release

- Extracts to apps/vscode-extension/bin/<platform>/

- Builds via bun + esbuild

- Packages platform-specific .vsix via vsce

- Publishes to VS Code Marketplace (VSCE_PAT) + Open VSX (OVSX_PAT)

- Uploads .vsix to the vscode-v* GH Release for manual install
Documents three release channels with their tag patterns and workflows:

- lsp-v* -> cross-compile + publish 5 binaries to GH Release

- vscode-v* -> bundle binaries into platform-specific VSIX, publish to Marketplace + Open VSX

- zed-v* -> manual PR to zed-industries/extensions (with layout fallback)

Includes prerequisites, rollback procedures, and pre-release vs stable versioning convention.
Hardens vespertide-lsp's pure compute_* domain functions against

arbitrary byte input:

- compute_diagnostics / compute_hover / compute_definition

- compute_completion / format_text

Sweeps byte offsets and both JSON+YAML formats per input.

Wired into the existing nightly fuzz workflow.
owjs3901 and others added 28 commits June 6, 2026 20:13
RUSTDOCFLAGS=-D warnings cargo doc fails on a public->private intra-doc link
(rustdoc::private_intra_doc_links). The macro entry doc referenced the private
vespertide_migration_impl via a doc-link; demote it to a plain code span.
Doc-only change, no behavior delta.
…e 100% gate

The --fail-under 100 tarpaulin gate was non-deterministic: the same commit
measured 99.81% on CI vs 99.97% locally with disjoint uncovered-line sets.
Root causes: (1) parallel test execution + LLVM coverage instrumentation give
slightly different line attribution per run/machine; (2) proptest branch
coverage is seed-random (proptest 1.x has no seed env var). Pin
RUST_TEST_THREADS=1 for stable attribution and PROPTEST_CASES=1024 so
property-test branches are hit every run. Deterministic source-level coverage
of must-cover branches is handled by unit tests, not env.
…er 100 gate

The CI coverage job reported 21 deterministic uncovered lines (identical
across runs once RUST_TEST_THREADS=1 stabilised attribution). Add focused
deterministic unit tests (NOT proptest-seed-dependent) driving each branch:
- lsp: locator missing-constraint fallback, network simple types, drift
  models-load-failure path, inlay-hint non-scalar ref_columns, percent-decode
  valid triplet, find_all URI/range sort, classify_yaml non-scalar type walk,
  symbols table emission + YAML quoted-range trim
- planner: check_expr_parser literal-start/keyword/eat_keyword arms,
  check_self_contradiction flatten_and leaf branch, schema PK-presence
- cli/core/query: erd dot relationship label, normalize idempotence (fixed
  fixture), MySQL FK ON DELETE/ON UPDATE 3-backend rstest

cargo test green across all 5 crates; fmt/clippy/line-budget clean.
Adds a direct test exercising every DOT-record metacharacter escape arm
plus the plain passthrough arm in escape_record_field. Real branch coverage
of a previously test-less escaping path.
…/query

Genuine missing tests:
- planner/check_expr_parser: OR/AND second-operand unparseable paths
- planner/check_self_contradiction: OR-conjunct skip in group_predicates_by_column
- planner/schema: nullable primary-key column rejection
- lsp/position: has_windows_drive_prefix (call site is cfg!(windows)-gated)
- lsp/{drift::compute,references::search,symbols}: path_to_uri leading-slash branch

Eliminate defensive/unreachable branches (mirrors symbols.rs while-let pattern):
- check_expr_parser: drop unreachable parse_predicate guard + dead bump()
- unwrap_yaml else (types/inlay_hints/classify_yaml): fused while-let
- locator: fuse pair_key_matches let-else; symbols: drop dead let _ = stripped

Restructure LLVM attribution-flaky regions to single coverage regions:
- cli/erd/dot.rs: escape arm -> escaped.extend
- query/add_constraint/foreign_key: closure chains -> explicit for loops
- type_narrowing: equal-length/Varchar->Char boundary + exact per-backend
  impact strings (verified 0 missed)
- check_default: per-op boundary tests for apply_op_i64/f64/str/bool,
  evaluate_op + literal_equals arms, incl. exact-EPSILON (1.0+f64::EPSILON)
  cases for the float-tolerance < comparisons (verified 0 missed)
- check_strengthening: AND/OR conjunct set-logic, BETWEEN narrowing bound
  tests, exact-EPSILON literal_equals (verified 0 missed); exclude the
  equivalent classify_strengthening || guard via mutants.toml
- check_expr_parser: trailing-operator/incomplete-exponent EOF (buffer
  over-read guards) + 70-group sequential-paren depth-leak test
…lent

The *_par_*_threshold() functions in parallel_config.rs (planner + query)
are perf-only dispatch knobs: replacing the returned threshold with 0/1
only changes when the Rayon path is taken, and the sequential vs parallel
paths produce byte-identical output. Same category as the existing E1
cmd_export perf-threshold exclusion.
- topological_sort_tables: cycle error lists only the cyclic tables (pins
  the not-yet-placed filter !result.any(name == t.name))
- sort_delete_tables: child-before-parent FK delete ordering (pins the
  <=1 short-circuit, Kahn's *degree -= 1 decrement, and == 0 enqueue)
- sort_enum_default_dependencies: no reorder when old default is not a
  removed enum value (pins the &&); exclude the equivalent < -> <=
  index compare (two distinct action indices, never equal)
find_plan_violations / find_missing_fill_with / find_missing_enum_fill_with
/ find_schema_violations / diff_schemas / build_plan_queries all dispatch
sequential vs Rayon on len() < *_par_*_threshold(). Mutating < only changes
the path, not the output (both paths proven identical by result tests).
Perf-only equivalents, same category as the E1 cmd_export exclusion.
…tch numeric label

- check_self_contradiction: eq-then-ne and ne-then-eq same-literal
  contradictions (pins both disjuncts of the equality-vs-negation check);
  exclude the equivalent self-pair loop (i+1).. -> (i*1)..
- check_type_mismatch: numeric-column mismatch renders numeric(P, S) label
  (pins complex_type_label Numeric arm); exclude the (Json,_) / (Custom,_)
  arms that duplicate the _ => false catch-all
- pk_additions: exclude the NewColumns/Mixed arm (both -> continue)
…to-inc PK guards

- fk_addcolumn_nullable: an out-of-line FK on a different column of the
  same table must not implicate an unrelated AddColumn (pins the table&&col
  match against a || mutant)
- sequence_exhaustion: a composite (2-column) auto-increment PK is not a
  single-sequence exhaustion risk (pins columns.len()==1 in both the
  AddConstraint and CreateTable paths)
…on/constraint_ops

- constraint_type_changes: a PK-less CreateTable must not absorb a PK removal
  (pins the CreateTable PrimaryKey match guard against a -> true mutant)
- drop_resolution: rename candidates scoped to the dropped column's table
  (pins add_table == table guard against a -> true mutant)
- apply/constraint_ops: clear_inline_constraint_fields clears the NAMED PK
  column only (pins &c.name == col_name against !=); exclude the equivalent
  clear_index_fields < -> <= (filtered == names when nothing removed)
- core: validate_unique_column_names accepts distinct names (pins the
  !seen.insert negation); exclude normalize_proptest.rs (proptest oracle
  behind feature=arbitrary, test infra)
- loader: non-extension files are ignored by collect_model_paths /
  collect_migration_paths (and their _internal variants) - pins the
  is_file() && has_*_extension filter against a || mutant
- loader: exclude the perf-only len() < LOAD_FILES_PAR_THRESHOLD dispatch
- foreign_key_constraint_exists: an inline single-column FK is still
  materialized despite a pre-existing composite FK sharing the first column
  (pins the columns.len()==1 && guard against a || mutant)
- index_constraint_exists: an inline named index is deduped against an
  existing same-name table-level index (pins the (Some,Some)=>n1==n2 arm
  against deletion and == -> !=)
Kills:
- timezones: validate_offset :59 boundary, parse_two_digit digit guards
- fill_with::wrap_if_spaces / narrowing::quote_value_for_target half-quoted &&
- svg/model: measure_table_width exact title-dominated width, build_boxes
  first-PK-row tracking; svg/layout: rebalance_groups boundary split
- cmd_revision_core: single Edge#1 error stays bare (not Multiple)

Exclusions (equivalent / interactive / display-only, see mutants.toml):
- compute_ranks > -> >= (redundant assign) and n+1 -> n-1 (DAG converges)
- print_fill_with_header/footer, print_strategy_descriptions (pure println)
- prompt_drop_resolution (interactive Select dialoguer)
…migration

Adding a NOT-NULL, no-default, non-FK column requires an interactively
collected fill_with value. A value-supplying mock provides a sentinel; the
written migration must embed it. Pins the if !missing.is_empty() gate
(mod.rs:492) - a delete-! mutant skips collection, omitting the value.
… exclude regex)

The first mutation CI pass left 3 ineffective fixes:
- svg/model build_boxes: original test asserted pk_row==Some(1), but the loop
  overwrites pk_row on the later PK row so the || mutant also yields Some(1).
  Use a PK-less table (orig None vs mutant Some(0)) - verified kills the mutant.
- sequence_exhaustion AddConstraint guard: baseline_existing_risky_pk suppressed
  the smallint PK so the mutant emitted nothing. Use a safe bigint baseline PK
  with risky non-PK columns - verified kills the mutant.
- check_type_mismatch:366 exclude_re used .Json (one char) but the arm is
  (SimpleColumnType::Json, _); widen to .* in is_definitely_mismatch.
The earlier test only exercised the (Some,Some) name-match arm (line 394).
The surviving mutant is line 395, the (None,None) arm comparing UNNAMED
indexes by columns (cols.as_slice() == columns). Add a dedup test for an
inline \index: true\ (auto-name -> None) against an existing unnamed
table-level index on the same columns; verified it fails on the != mutant.
The original gate was red on BOTH 155 missed AND 55 timeout entries; the
timeouts were masked on shards 4/9/10 by a transient install-action 504.
These 24 mutants replace a monotonic advance (i += 1 / self.pos += 1) or the
scanner's loop condition with a non-advancing op -> infinite loop. cargo-mutants
flags them TIMEOUT (a hang the test can't assertion-catch). Exclude the
tokenize_spanned scanner advances + line-408 loop condition, the parse_predicate
IN-list advance, and the rebalance_groups loop counter.
@owjs3901 owjs3901 merged commit dce9fef into main Jun 8, 2026
37 checks passed
@owjs3901 owjs3901 deleted the refactor branch June 8, 2026 09:24
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