Skip to content

Extract Commercial Features #119

@Dimwiddle

Description

@Dimwiddle

Plan: Extract Commercial Features → Apache 2.0 Only

Context

SpecLeft is currently dual-licensed (Apache 2.0 for the open core, a commercial license for enforcement/signing). The goal is to make the project fully Apache 2.0 by moving the three commercial modules (enforcement/, license/, specleft_signing/) and their two CLI commands (enforce, license) out of the main source tree and into .future_features/. All cross-cutting references to these modules must also be cleaned up.


Step 1 — Create .future_features/ skeleton

Create the directory structure and a brief README.md explaining its purpose:

.future_features/
├── README.md
├── LICENSE-COMMERCIAL
├── src/
│   ├── specleft_signing/
│   ├── license/
│   ├── enforcement/
│   └── commands/
├── tests/
│   ├── license/
│   ├── commands/
│   └── acceptance/
│       └── fixtures/
└── features/

Step 2 — Move commercial source modules

From To
src/specleft/specleft_signing/ .future_features/src/specleft_signing/
src/specleft/license/ .future_features/src/license/
src/specleft/enforcement/ .future_features/src/enforcement/
src/specleft/commands/enforce.py .future_features/src/commands/enforce.py
src/specleft/commands/license.py .future_features/src/commands/license.py

Step 5 — Update src/specleft/commands/__init__.py

Remove:

  • from specleft.commands.enforce import enforce (line 11)
  • from specleft.commands.license import license_group (line 15)
  • "enforce" and "license_group" from __all__ (lines 27, 31)

Step 6 — Update src/specleft/cli/main.py

Remove:

  • enforce and license_group from the import block (lines 14, 18)
  • cli.add_command(enforce) (line 53)
  • cli.add_command(license_group) (line 54)

Step 7 — Update src/specleft/commands/plan.py

Remove:

  • Line 18: from specleft.license.status import resolve_license
  • Lines 725–732: The entire license notice block at the end of the command (resolve_license() call + the click.echo() lines prompting users to buy a license)

Step 8 — Update src/specleft/pytest_plugin.py

Line 463 contains a print statement referencing https://specleft.dev/enforce. Remove it or replace with the GitHub docs URL since the commercial product page no longer applies.


Step 9 — Update LICENSE

Replace LICENSE (currently a dual-license pointer) with the full Apache 2.0 text from LICENSE-OPEN. pyproject.toml already uses { file = "LICENSE" }, so the build config stays valid without further changes.


Step 10 — Update NOTICE.md

Rewrite to reflect a single Apache 2.0 license only. Remove all references to the commercial license tier and LICENSE-COMMERCIAL.


Step 11 — Update pyproject.toml

  1. Remove cryptography>=41.0.0 from [project] dependencies — no longer needed once signing code is gone.
  2. Remove the mypy override block for specleft_signing.* (lines 171–173).
  3. Add the OSI classifier:
    "License :: OSI Approved :: Apache Software License",
    

Step 12 — Update README.md

Remove the commercial feature section (lines ~130–154) that documents specleft enforce and the licensing tier.


Step 13 — Update docs/cli-reference.md

Remove the specleft enforce section (lines 241–296) and the specleft license section (lines 349–365).


Verification

After all changes, verify in order:

# 1. No broken imports
python -c "from specleft.cli.main import cli"

2. enforce and license must be absent from CLI

specleft --help

3. Full test suite passes

pytest tests/ -x

4. Agent contract still healthy

specleft contract --format json
specleft doctor --format json

5. No remaining references to commercial modules in src/

grep -r "specleft_signing|from specleft.license|from specleft.enforcement" src/

Must return zero matches

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions