Skip to content

feat: stabilize shared-project types across products - #8

Merged
oevery merged 10 commits into
mainfrom
feat/shared-project-type-contracts
Oct 19, 2026
Merged

oevery merged 10 commits into
mainfrom
feat/shared-project-type-contracts

Conversation

@oevery

@oevery oevery commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

改动目的 / Purpose

Closes #7.

Keep shared-project declarations stable across full/selected preparation and single-product dev/build, while preserving legitimate field removal and active-product runtime isolation.

主要变化 / Changes

  • Collect all associated products/variants for affected project ownership groups in the current environment, resolving canonical development and production host contexts.
  • Coordinate standard dev/build generation once per affected project and validate a private type-contract handshake before plugins can overwrite declarations.
  • Mark inferred product/context-specific fields optional; preserve explicit schema semantics and public-prefix/scope boundaries.
  • Reject ambiguous host-root/output ownership before writing. Resolve symlinked and future output paths consistently.
  • Serialize collection/publication, recover verified dead lock owners conservatively, and abort/drain preparation when dependencies fail.
  • Add deterministic concurrency/cancellation, actual CLI dev, shared Electron main/preload/renderer, deletion, ownership, and mixed-host regressions. Preserve hostless/non-Vite behavior.
  • Update English/Chinese documentation and migration guidance.

验证情况 / Verification

Current head: 2646a7ba3f6e2bf07575e3956685a17203d264d4.

  • Latest test-only cleanup: 15 affected tests, ESLint and TypeScript passed locally; no cases were removed. Production code is unchanged from e8ee058, whose full local 701-test suite, package build and publint passed.

  • Independent review completed for the four initial findings, portable lock recovery, and the follow-up shared-owner cache issue.

  • Project preparation invalidates all cached contracts conservatively; a real A → B → A shared-root regression verifies fresh handshakes, while ordinary targets retain cache reuse.

  • Fixed deferred preparation cache invalidation, global raw-env optionality across owned scopes/aliases, non-Vite adapter isolation, and schema-declared handshake keys.

  • Lock recovery uses an exclusive per-owner directory claim; regressions cover contention, late recoverers, abandoned claims, and killed-process recovery. Twenty coordination-suite repetitions passed during development.

  • Simplified canonical context collection and removed a redundant stateless test assertion; retained compiler and real CLI regression coverage.

  • CI: 11/11 jobs passed for this exact head, including Linux/macOS/Windows on Node 22.18.0 and 24.x, packaged compatibility, and all three Electron acceptance jobs.

  • Packaged profiles: full on Linux/Node 24, daily on Linux/Node 22, platform on macOS and Windows.

  • Documentation checks: passed.

  • Both findings from Copilot re-review were reproduced and fixed: config paths resolve from the configured project path before canonicalization; generic owners contribute to raw-key reconciliation without automatic publication. Regression coverage includes symlink misselection, hostless and no-Matrix-plugin ancestors, stable declarations, absent-output preservation, and strict TypeScript compilation.

  • Independent review found no blockers or further worthwhile simplifications. The final local suite passed with two workers after a default-concurrency run timed out in an existing CLI test; default-concurrency CI passed all six OS/Node combinations without that failure.

  • Fixed and verified the subsequent fallback schema finding: generic and worker declarations share normalized metadata while original schema defaults remain available at runtime. A shared-output prepare/build regression covers required, optional, and defaulted fields. The review discussion is resolved.

  • Final cleanup removes unused unlink-mock error plumbing and four redundant assertions (net −12 lines), without changing production behavior. No further Copilot review cycle is planned for this scope.

风险 / Risks

  • Deferred compatibility edge case: standalone Vite with a symlinked root and a parent-relative custom declaration output can resolve the output against the physical parent (Vite 7, or Vite 8 with resolve.preserveSymlinks: true). The proposed path patch is excluded from this PR by scope choice; the finding remains unresolved.

  • Approved compatibility constraint: coordinated declarations require the resolved host root and output to stay within project.root. Align project.root with the actual shared project directory and use project.configFile for configurations stored elsewhere. Unsupported ownership fails before writes instead of silently narrowing a shared declaration.

  • Host-aware automatic coordination covers discoverable Vite/electron-vite configurations for standard dev/build. Custom targets and non-Vite hosts retain their existing generation behavior.

  • Host configuration code executes for both canonical lifecycles; this adds startup work and is not a side-effect-free static scan. Uncollected contract changes fail explicitly.

  • Dev/build preparation remains per-project to preserve existing just-in-time preparation order; this is not a multi-file transaction.

  • Live or uncertain lock owners are never reclaimed by age. A process interrupted while holding a recovery claim fails closed; the timeout identifies the lock and sibling claim directories for manual cleanup only after confirming no preparation or recovery is running.

@oevery
oevery marked this pull request as ready for review October 19, 2026 04:17
@oevery
oevery requested a balanced review from Copilot October 19, 2026 04:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved cache, handshake, and declaration-optionality issues can narrow shared types or reject valid builds.

4 open issues
What changed in this PR

Stabilizes shared-project declarations across prepare, dev, and build while preserving active-product runtime isolation.

Changes:

  • Collects shared type contracts across products and host lifecycles.
  • Adds ownership checks, coordinated publication, and cancellation handling.
  • Expands regression coverage and English/Chinese guidance.

Validation here was limited to targeted source inspection; no tests, builds, or Electron acceptance were run.

File Description
test/​unplugin/​index.test.ts Tests coordinated handshake consumption.
test/​typegen/​typegen.test.ts Tests inferred optionality and deletion.
test/​typegen/​shared-contract.test.ts Covers lifecycle stability and runtime isolation.
test/​typegen/​prepare-types.test.ts Expands host-context and shared-root coverage.
test/​typegen/​ownership.test.ts Tests output ownership boundaries.
test/​typegen/​coordination.test.ts Tests locking, cancellation, and recovery.
test/​README.md Documents expanded coverage.
test/​execution/​prepare.test.ts Tests preparation cleanup after service failure.
test/​cli/​cli.test.ts Updates shared-field expectations.
src/​utils/​path.ts Resolves canonical future output paths.
src/​unplugin/​index.ts Consumes coordinated contracts.
src/​typegen/​worker.ts Resolves both host lifecycles.
src/​typegen/​project.ts Collects associated products and validates ownership.
src/​typegen/​prepare.ts Merges inferred field optionality.
src/​typegen/​lock.ts Serializes preparation and recovers dead owners.
src/​typegen/​generate.ts Renders optional inferred fields.
src/​typegen/​coordination.ts Defines and validates private handshakes.
src/​typegen/​collector.ts Tracks automatic collection context.
src/​runtime/​public-env.ts Excludes private contract metadata.
src/​execution/​exec.ts Supports cancellable pre-task coordination.
src/​config/​env-schema.ts Defines the private contract key.
src/​cli/​selection.ts Retains loaded configuration for coordination.
src/​cli/​index.ts Coordinates declarations before dev/build tasks.
docs/​site/​zh-CN/​reference/​plugins.md Links shared declaration guidance.
docs/​site/​zh-CN/​reference/​configuration.md Clarifies host configuration usage.
docs/​site/​zh-CN/​reference/​cli.md Links type preparation rules.
docs/​site/​zh-CN/​guides/​integrations.md Explains contracts and migration boundaries.
docs/​site/​zh-CN/​guides/​execution.md Updates preparation guidance.
docs/​site/​en/​reference/​plugins.md Links shared declaration guidance.
docs/​site/​en/​reference/​configuration.md Clarifies host configuration usage.
docs/​site/​en/​reference/​cli.md Links type preparation rules.
docs/​site/​en/​guides/​integrations.md Explains contracts and migration boundaries.
docs/​site/​en/​guides/​execution.md Updates preparation guidance.

Review effort: Balanced

Comment thread src/cli/index.ts
Comment thread src/typegen/generate.ts Outdated
Comment thread src/unplugin/index.ts Outdated
Comment thread src/typegen/coordination.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Deferred preparation can leave another shared owner's cached handshake stale, causing subsequent valid builds to fail.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Invalidate shared ownership caches after deferred preparation

src/​cli/​index.ts:126

Invalidate cached contracts for all projects sharing the affected ownership group. With projects A/B sharing a physical root, an A → B → A build sequence fails if B's deferred preparation adds a public field: B publishes the updated declaration, but the final A build reuses its old handshake and rejects the field as uncollected. Clearing the cache after preparation is a conservative fix, though it may recollect unrelated projects; group-aware invalidation would avoid that extra work. Add an interleaved two-project regression.

4 resolved since last review

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-process recovery and cross-host coordination need human review, with unresolved correctness issues requiring fixes.

No open issues 🎉

2 previously missed issues

In code that hasn't changed since last review

Medium severity Resolve configFile relative to the configured project path

src/​typegen/​project.ts:110

Resolving configFile against the canonical root changes relative paths when project.root is a symlink. For example, if links/app points to packages/app and configFile is ../configs/vite.config.mjs, the automatic precheck selects links/configs/vite.config.mjs, but this call looks under packages/configs. Preparation can then fail or load a different configuration. Resolve the config against the configured project path first; keep canonicalizing the selected file and worker directory separately.

Medium severity Retain hostless owners for consistent raw-key optionality

src/​typegen/​project.ts:112

Skipping hostless owners makes prepare and dev/build disagree about global raw-key optionality. Suppose an ancestor project belongs to alpha (VITE_X present) and gamma (VITE_X absent), and a nested Vite project belongs to beta (VITE_X present). Full prepare makes ImportMetaEnv.VITE_X optional in both outputs. Building beta skips the ancestor here and rewrites the nested property as required, leaving conflicting interface declarations when both files are included. Retain generic owners as inputs to raw-key reconciliation without automatically publishing their outputs. Add a full-prepare → nested-build regression that compiles both declarations.

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

A schema-merging conflict remains unresolved, and cross-process recovery and cancellation warrant final human review.

1 open issue

Review effort: Balanced

Comment thread src/typegen/project.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

An unresolved compatibility regression and cross-process coordination changes warrant fixes and final human review.

1 open issue
1 resolved since last review

Review effort: Balanced

Comment thread src/unplugin/index.ts
return
}
await generateMatrixTypes(typeOptions(cwd))
const declaration = typeOptions(await resolveExistingPath(cwd))
@oevery
oevery merged commit d3cd0f0 into main Oct 19, 2026
12 checks passed
@oevery
oevery deleted the feat/shared-project-type-contracts branch October 19, 2026 08:41
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.

feat: keep shared-project types stable across product-specific prepare and builds

2 participants