Skip to content

feat(nix): expose openspec as a consumable overlay - #1439

Open
jmuchovej wants to merge 2 commits into
Fission-AI:mainfrom
jmuchovej:add-nix-overlay
Open

feat(nix): expose openspec as a consumable overlay#1439
jmuchovej wants to merge 2 commits into
Fission-AI:mainfrom
jmuchovej:add-nix-overlay

Conversation

@jmuchovej

@jmuchovej jmuchovej commented Jul 24, 2026

Copy link
Copy Markdown

Refactor the flake so the package derivation is defined once, in overlays.default, and every other output consumes it. Previously the derivation lived inline in packages.default, so anyone who wanted openspec in their own package set had to copy the derivation rather than import it.

What changed:

  • Add overlays.default, a standard final: _prev: overlay that exposes pkgs.openspec. The derivation is written against final, so downstream overlay composition and overrideAttrs behave as expected.
  • Route packages.{default,openspec} through the overlay via a pkgsFor helper (import nixpkgs { overlays = [ self.overlays.default ]; }), so the flake's own package resolves exactly as a consumer's would. No more duplicated build definition.
  • Refresh the nixpkgs pin in flake.lock.

apps and devShells are unchanged in behaviour.

Usage — a downstream flake:

nixpkgs.overlays = [ openspec.overlays.default ];
# -> pkgs.openspec

or devenv, via devenv.yaml:

inputs:
  openspec:
    url: github:Fission-AI/OpenSpec
    overlays:
      - default

Summary by CodeRabbit

  • Refactor
    • Improved the Nix flake build configuration for the OpenSpec package.
    • Standardized package builds across supported systems through the default overlay.
    • Preserved existing build behavior and package metadata.

Refactor the flake so the package derivation is defined once, in
`overlays.default`, and every other output consumes it. Previously the
derivation lived inline in `packages.default`, so anyone who wanted
`openspec` in their own package set had to copy the derivation rather than
import it.

What changed:
- Add `overlays.default`, a standard `final: _prev:` overlay that exposes
  `pkgs.openspec`. The derivation is written against `final`, so downstream
  overlay composition and `overrideAttrs` behave as expected.
- Route `packages.{default,openspec}` through the overlay via a `pkgsFor`
  helper (`import nixpkgs { overlays = [ self.overlays.default ]; }`), so the
  flake's own package resolves exactly as a consumer's would. No more
  duplicated build definition.
- Refresh the nixpkgs pin in `flake.lock`.

`apps` and `devShells` are unchanged in behaviour.

Usage — a downstream flake:

    nixpkgs.overlays = [ openspec.overlays.default ];
    # -> pkgs.openspec

or devenv, via `devenv.yaml`:

    inputs:
      openspec:
        url: github:Fission-AI/OpenSpec
        overlays:
          - default

Assisted-by: Claude Opus 4.8 <noreply@anthropic.com>
@jmuchovej
jmuchovej requested a review from TabishB as a code owner July 24, 2026 18:33
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The flake refactors per-system package generation around a default overlay. pkgsFor imports nixpkgs with that overlay, and package outputs expose the resulting openspec derivation as both default and openspec.

Changes

Flake package wiring

Layer / File(s) Summary
Overlay-backed package outputs
flake.nix
Per-system nixpkgs imports apply self.overlays.default, which defines the openspec derivation; package outputs now expose pkgs.openspec under both default and openspec.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: tabishb, clay-good, tabishb

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: exposing openspec through a consumable Nix overlay.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
flake.nix (1)

51-56: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Plan a pnpm/fetcher migration for this derivation.

pnpm_9 is EOL and being phased out in Nixpkgs, while this flake is on nixos-unstable and still uses pnpm_9 plus fetchPnpmDeps with fetcherVersion = 3. Update the build/dev/dependency-fetching path to a supported pnpm version such as pnpm_11 and use fetcherVersion = 4, regenerating the pnpmDeps hash accordingly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flake.nix` around lines 51 - 56, Update the derivation’s pnpm
dependency-fetching and build/development configuration from pnpm_9 to a
supported version such as pnpm_11, change fetchPnpmDeps to fetcherVersion 4, and
regenerate the pnpmDeps hash for the new fetcher output. Ensure all related pnpm
references use the same supported version.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@flake.nix`:
- Around line 51-56: Update the derivation’s pnpm dependency-fetching and
build/development configuration from pnpm_9 to a supported version such as
pnpm_11, change fetchPnpmDeps to fetcherVersion 4, and regenerate the pnpmDeps
hash for the new fetcher output. Ensure all related pnpm references use the same
supported version.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 98cc6ee4-1eab-44dc-82a6-b6998b56fa17

📥 Commits

Reviewing files that changed from the base of the PR and between 19d4171 and bc4abb4.

📒 Files selected for processing (1)
  • flake.nix

@clay-good
clay-good requested a review from a team as a code owner July 27, 2026 17:30
@clay-good

Copy link
Copy Markdown
Collaborator

Code review — verdict: good to merge after one small fix

Reviewed against four criteria: real need, does it work, breaking-change risk, and scope creep.

1. Real need — yes, narrowly

Today the derivation lives inline in packages.default, so a downstream flake can only get openspec into its own package set by copying the derivation. overlays.default is the idiomatic Nix answer to that. Small, real, and it costs npm users nothing (flake.nix isn't in package.json#files, so it never ships).

2. Does it work — yes, and CI proves the overlay path specifically

This is the part I wanted to be sure of, because an overlay can evaluate fine and still not be what nix build uses. It is:

  • packages.default = (pkgsFor system).openspec, and pkgsFor imports nixpkgs with self.overlays.default. So the existing Build with Nixnix buildresult/bin/openspecnix run . -- --version steps in ci.yml are exercising the overlay-produced derivation, not a parallel copy. Nix Flake Validation is green on this PR.
  • No infinite recursion: overlays.default doesn't reference self.packages, only final. Confirmed by the build passing.
  • forAllSystems = f: genAttrs supportedSystems f is a plain eta-reduction of the old (system: f system) — behaviourally identical.
  • Writing the derivation against final (not prev) is the correct choice for downstream overlay composition and overrideAttrs, as the description claims.

3. Breaking changes — none that I can find

  • packages.default still resolves to the same derivation. apps and devShells are untouched, as stated.
  • packages.openspec and overlays.default are purely additive.
  • inherit ((builtins.fromJSON (builtins.readFile ./package.json))) version; is semantically identical to the old version = (...).version;.
  • Zero impact on CLI users — no TypeScript, schema, template, or CLI surface touched. Full suite on main is 2239/2239 and this PR cannot move it.

4. Scope — correctly surgical

Packaging-only. No OpenSpec design surface expanded. This is exactly the shape a change like this should have.


One thing to fix before merge (non-blocking for correctness, but it does regress a script)

scripts/update-flake.sh sanity-checks that the flake still reads its version dynamically:

if ! grep -q "(builtins.fromJSON (builtins.readFile ./package.json)).version" "$FLAKE_FILE"; then
  echo "⚠️  Warning: flake.nix doesn't use dynamic version from package.json"

That pattern matches version = (...).version; but not the new inherit ((...)) version; form. Verified:

--- old (main):     MATCH
--- new (PR 1439):  NO MATCH

So anyone running scripts/update-flake.sh after this lands gets a spurious "doesn't use dynamic version" warning. It's cosmetic — the check is a warning, not a hard failure, so the script continues and CI's Validate update script step still passes — but it's a false alarm on a script maintainers run whenever pnpm-lock.yaml changes, and it'd be easy to misread as a real problem.

Either fix works:

  • Simplest: keep the original attribute form — version = (builtins.fromJSON (builtins.readFile ./package.json)).version;. It reads better than the double-paren inherit anyway, and nothing about the overlay refactor requires the change.
  • Or update the grep in scripts/update-flake.sh to match the inherit form.

I'd take the first — it keeps this PR to purely the overlay restructure.

Nits (no action needed)

  • The description says "Refresh the nixpkgs pin in flake.lock", but flake.nix is the only file in the diff. Worth correcting the body so the changelog doesn't claim a lock bump that didn't happen.
  • pkgsFor re-imports nixpkgs per system while devShells still uses nixpkgs.legacyPackages, so the flake now evaluates nixpkgs twice. Harmless, just slightly slower eval. Not worth churn here.
  • I'm not asking for CodeRabbit's pnpm_9pnpm_11 / fetcherVersion = 4 suggestion. It's a valid future concern but it's unrelated to this PR, it forces a pnpmDeps hash regeneration, and per our conventions the flake hash breaks on any such change — that belongs in its own PR.

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The overlay itself is sound and Nix CI passes, but the new version syntax regresses scripts/update-flake.sh's dynamic-version check; the CI log reproduces the false warning. Please keep the original version assignment or update the checker, and remove the stale flake.lock claim from the PR body.

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.

3 participants