Skip to content

Release v3.0.0: beehave rewrite (.feature as sole source of truth)#27

Open
nullhack wants to merge 13 commits into
mainfrom
release/beehave-v2
Open

Release v3.0.0: beehave rewrite (.feature as sole source of truth)#27
nullhack wants to merge 13 commits into
mainfrom
release/beehave-v2

Conversation

@nullhack

@nullhack nullhack commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Release v2.0.0 → v2.1.0

Publishes the `beehave-v2` rewrite plus the `beehave-v3` scoping follow-up from `dev` → `main`. The head carries both the seven-cycle v2.0.0 rewrite (squash-merged to `dev`) and the v2.1.0 minor bump that scopes generated tests to `tests/features/`.

v2.1.0 — scoped generated tests

Generated `*_test.{pyi,py}` now land in `tests/features/` (parallel to `docs/features/`), making the subdirectory the ownership marker: anything inside `tests/features/` is beehave-owned (wiped/regenerated), anything outside is consumer-owned (untouched).

  • `generate` emits into `tests/features/`, creates the dir on demand, and wipes stale `*_test.pyi` in that subdir before each run so deleted scenarios/features stop orphaning files.
  • `check` scans `tests/features/` for `*_test.py` without a `.pyi` sibling (orphans) and fails with one stderr line per orphan before the AST binding loop. Hand-written tests outside `tests/features/` are never inspected.
  • `status` counts `*_test.pyi` under `tests/features/` (was `tests/`).
  • `.pyi` signatures unchanged — behavior + path scope only.
  • Migration: `mkdir -p tests/features && git mv tests/*_test.py{i,} tests/features/` before re-running `beehave generate`.

v2.0.0 — breaking rewrite

The 1.x source modules are removed: `beehave/{clean,config,discover,models}.py`, the 1.x test surface (`test_{check,clean,cli,config,discover,generate,gherkin}.py`, `tests/conftest.py`, and the legacy `tests/features/{case_insensitive_matching,comb_construction,hive_activity,status_command,title_validation}/` suites).

Rewritten as a thin layer over Hypothesis with a Gherkin-style BDD pipeline. Seven source modules built across five TDD cycles (red → green → refactor → ship):

Module Responsibility
`beehave/step.py` the `with step(...)` BDD pipeline primitive
`beehave/gherkin.py` Gherkin parsing into the step model
`beehave/generate.py` Hypothesis strategy inference + example generation
`beehave/check.py` example checking against invariants
`beehave/status.py` run status / outcome reporting
`beehave/cli.py` command-line entry point
`beehave/init.py` public surface re-exports

Type-safety surface: `.pyi` stubs for every source module (the binding contract), `py.typed` marker (PEP 561), `with step(...)` BDD pipeline as the public API entry, Hypothesis strategy inference from annotations.

Gate (verified green on PR head `efa3d51`)

  • `mypy --strict` clean across the package.
  • Whole-package `stubtest` — 21 modules clean (source + tests), no drift.
  • `ruff check`, `ruff format --check`, `lint-merge` clean.
  • Whole suite: 59 tests green; no pending markers remain.

Version

`pyproject.toml` → `2.1.0` (single source of truth; minor bump for the scoped-tests behavior change — no external consumers yet, so no breaking-class semver impact). No git tag and no PyPI publish are part of this PR — both deferred pending separate approval.

Commits on the PR head ahead of `main`

```
efa3d51 feat(beehave-v3): scope generated tests to tests/features/
6454329 fix(beehave-v2): move pytest_plugins to root conftest
47f629f chore: strip docstrings for next dev cycle
dbc770e docs: regenerate living spec for 2.0.0
9bd497e feat(beehave-v2)!: rewrite as thin Hypothesis layer with Gherkin BDD pipeline
90b66ef docs(beehave-v2): regenerate docstrings + format
d614250 chore(beehave-v2): sync uv.lock to 2.0.0
2b55974 plan(beehave-v2): author contract surface
```

Reviewer notes

  • Opened for review — not auto-merged. Stakeholder review is the gate to `main`.
  • No PyPI publish. No git tag. Those await separate approval.

nullhack added 8 commits July 18, 2026 05:28
Source .pyi stubs (7): __init__, step, gherkin (8 parse-model shapes +
parse_feature, Q2 collapse — no models.pyi), generate, check, status, cli.

Test contract (9 pairs .pyi/.py + 2 conftests):
- tests/integration/: step_cm, title_derivation, idempotency, roundtrip,
  strategy_inference, parsing (55 falsifiable claims, @pytest.mark.pending)
- tests/e2e/: check, generate, status (pytester-driven CLI surface)

docs/glossary.md: 3 contexts, 23 terms.

Build-entry setup (v1 deletion, mypy/stubtest/taskipy wiring, py.typed)
follows on feature/beehave-v2.
…pipeline

Squash-merges the seven-contract v2 build cycle into dev.

Modules:

- beehave/step.py: step context manager wiring Gherkin steps to Hypothesis

- beehave/gherkin.py: Gherkin parse model

- beehave/generate.py: test-file generator

- beehave/check.py: contract verifier

- beehave/status.py: status command

- beehave/cli.py: CLI surface

- beehave/__init__.py: __version__ = 2.0.0

Removes clean.py, config.py, discover.py, models.py (consolidation to thin layer).

Adds .stubtest_allowlist, beehave/py.typed; activates integration + e2e test bodies.

BREAKING CHANGE: beehave 2.0.0 is a rewrite; 1.x modules removed.
Authored by refresh-cycle from the 55-test green suite (33 integration + 22
e2e) plus the source stubs, glossary, and absence of cassettes/migrations.
Derived view per the simplicity-discipline rule; never hand-edited.
Module/class/function docstrings stripped from the 7 source .py modules
(21 docstrings total) via AST line-range removal. Tests already docstring-
free. ruff format applied; lint-merge + 55-test suite + whole-package
stubtest all clean.
pytest 9.0.3 forbids `pytest_plugins` in non-top-level conftests; the
declaration in tests/e2e/conftest.py broke bare `task test` / `uv run
pytest` collection (ERROR: Defining 'pytest_plugins' in a non-top-level
conftest is no longer supported). Move it to a top-level conftest.py at
the repo rootdir as pytest's deprecation notice recommends. The e2e
conftest keeps its pending-marker hooks (nested hooks remain allowed).

Verified: `task test`, `task lint`, `task lint-merge`, `task stubtest`
all green; 55 passed.
Emit beehave-generated *_test.{pyi,py} into tests/features/ (parallel
to docs/features/) so the subdirectory is the ownership marker:
anything inside is beehave-owned (wiped/regenerated), anything outside
is consumer-owned (untouched).

generate: emit into tests/features/, mkdir on demand, and wipe stale
*_test.pyi in that subdir before each run so deleted scenarios/features
stop orphaning files.

check: _check_all now scans tests/features/ for *_test.py without a
.pyi sibling (orphans) and fails with one stderr line per orphan before
the AST binding loop. Hand-written tests outside tests/features/ are
never inspected.

status: counts *_test.pyi under tests/features/ (was tests/).

Bumps version to 2.1.0. Signatures in the .pyi files are unchanged;
the change is behavior + path scope only.

Migration: move existing generated files from tests/ to tests/features/
(e.g. `mkdir -p tests/features && git mv tests/*_test.py{i,} tests/features/`)
before re-running `beehave generate`.
@nullhack nullhack changed the title Release v2.0.0: beehave-v2 rewrite as thin Hypothesis layer with Gherkin BDD pipeline Release v2.0.0 → v2.1.0: beehave rewrite + scoped generated tests Jul 18, 2026
nullhack added 3 commits July 18, 2026 15:01
Replace v2's Hypothesis-strategy-inference path with
@pytest.mark.parametrize for Scenario Outlines. The generator emits a
parametrize decorator over each Outline's test function whose arg-names
are the Examples headers and whose rows are the Examples cells (string
tuples). All parameters are now typed `str`; the strategy-inference
helpers (_is_int/_is_float/_is_bool/_infer_param_type) are removed.
Plain scenarios (no Examples) emit no parametrize and no `import pytest`.

Extend check.py to parse @pytest.mark.parametrize from each test
function's decorator list and verify the arg-names and rows round-trip
the feature's Examples table. Examples present without a matching
parametrize now fails check (closing the Examples-row binding gap).
Step-template binding (count/keyword/text/placeholder-names) is checked
first; parametrize verification runs only for scenarios that have
Examples.

Bumps version to 2.2.0. .pyi signatures unchanged in shape (still flat
typed signatures); comment prose updated to describe parametrize.

Drops tests/integration/strategy_inference_test (the concept is gone)
and replaces it with tests/integration/parametrize_test covering
emission + check roundtrip. Adds one e2e test each in generate_test
(parametrize emission via the CLI) and check_test (check catches edited
parametrize rows via the CLI).
Two-layer enforcement: `beehave check` (always-on signature gate via
mypy.stubtest + regenerate-and-diff) and opt-in `step` runtime (step
block + parametrize row verification against the parsed feature).

v2.3.0 — dual-mode rearchitecture:
- check.py rewritten to signature-only regenerate-and-diff (zero AST);
  the .pyi is the sole binding surface; `beehave check` runs stubtest
  internally (mypy is now a runtime dependency).
- step.py rewritten for Mode B: frame walk-up finds the test_<slug>
  caller, a frame-keyed counter tracks step position, each block
  verifies (keyword, text, placeholder-name-set) at position N against
  the scenario's parsed steps, and @parametrize rows are verified
  against Examples. Unknown callers raise NoActiveScenarioError.
- _index.py (new): lazy module-level scenario index keyed by function
  name, built once per process from docs/features/*.feature.
- gherkin.py: _reject_duplicate now keys on the slug (stripped word
  sequence), so titles differing only in whitespace runs collide.
- cli.py: _check_all runs stubtest (subprocess with MYPYPATH +
  PYTHONPATH = tests/features), regenerate-and-diff, and the orphan
  pass (.py without .pyi sibling in tests/features/).

v2.3.1 — strict Gherkin subset:
- Placeholder extraction is now Examples-aware: <foo> in a plain
  Scenario is literal text, only matched in Scenario Outlines where
  the name appears in an Examples header (closes the footgun).
- Multiple Examples tables are merged (was: only first kept); per-row
  tags tracked for conditional pytest.param emission.
- Tags surface as pytest marks: Feature/Rule tags -> module-level
  pytestmark; Scenario tags -> @pytest.mark.<tag> decorator.
- Docstrings surface as body-local `docstring = "..."` inside the
  step block (not passed to the step CM).
- Data tables surface as body-local `data_table = [{'col': ...}]`
  inside the step block.
- @parametrize uses pytest.param(..., marks=...) ONLY when Examples
  tables have differing tags; plain tuples otherwise.

60 tests green; ruff/format/mypy/stubtest(8+22 modules)/lint-merge clean.

BREAKING CHANGE: check.py no longer inspects step bodies; step
runtime verification is opt-in via `from beehave import step`.
Hypothesis strategies removed; all params typed str with
@parametrize rows.
v3.0.0 superset model: .feature is the sole source of truth. The .py
non-private function signatures must match feature-derived signatures
1-1 (names + types + order); private functions are exempt superset.
Orphan detection collapses INTO check (a .py non-private fn with no
matching scenario IS the orphan signal).

generate: emit only *_test.py skeleton (drop .pyi emission + wipe).
check: AST-based 1-1 superset (was signature-only regenerate-and-diff
against .pyi text).
cli: drop _run_stubtest (no consumer-side stubtest); add orphan-module
detection by filename stem on full sweep; accept feature path args for
scoped check (`beehave check docs/features/x.feature`) — skips orphan
detection so consumers can wire incremental scope via git diff without
forcing a full feature sweep.
status: count *_test.py (was *_test.pyi); print "skeleton file(s)".
Drop mypy runtime dep (back to dev-only).

BREAKING CHANGE: v3.0.0 removes consumer-side .pyi emission and the
stubtest subprocess in check. Existing tests/features/*_test.pyi files
become stale; consumers delete manually. Projects relying on the
.pyi-driven gate must migrate.
@nullhack nullhack changed the title Release v2.0.0 → v2.1.0: beehave rewrite + scoped generated tests Release v3.0.0: beehave rewrite (.feature as sole source of truth) Jul 21, 2026
This was referenced Jul 21, 2026
nullhack added 2 commits July 20, 2026 22:19
Remove 75 stale paths that pre-date the v2/v3 work and were carried as
uncommitted deletions across every cycle:

- 73 .cache/{interview-notes,sim}/* files (gitignored; tracked before
  the .gitignore rule was added; never should have been committed).
- CHANGELOG.md (v1.0.0 content; stale. Will be re-authored at v3.0.0
  release time).
- =1.1.0 (stray empty file from a botched `pip install` log).

No source or test changes; pure housecleaning.
The docs were frozen at v1.x/v2.0.0 and extensively wrong about v3.0.0
behavior. This commit brings them in line with the superset model.

README.md: full rewrite. Drops Hypothesis (replaced by @parametrize in
v2.2), enforced literals/placeholders (dropped in v2), `beehave clean`/
`list`/`--json`/`--force` (never existed in v2+), `[tool.beehave]` config
block (never implemented), PyPI badge (not on PyPI). Adds accurate CLI
surface (generate / check [feature...] / status), Mode A/B distinction,
real emit format (@parametrize + with step blocks), real file naming
(flat tests/features/<feature_slug>_<group>_test.py).

docs/state.md: regenerated for v3.0.0 (was v2.0.0). Updated version,
test count (66), contract index for signature-only check, dropped
Hypothesis + consumer-side stubtest from dependencies, updated data
flow narrative.

docs/glossary.md: rewrote stale entries (Examples, generate, check,
status, .pyi, mypy.stubtest, parse model, structural binding, default
group, skeleton). Added v3.0.0 concepts (superset model, scoped check,
orphan module, Mode A, Mode B, StepError, NoActiveScenarioError,
scenario index, Scenario Outline, .stubtest_allowlist).

LICENSE: added MIT license file (README already claimed MIT; file was
missing).

Removed v1-era cruft:
- docs/spec/{domain_model,domain_spec,glossary,product_definition,
  status_command_spec}.md (v1 specs describing Hypothesis + step defs).
- docs/spec/v3/beehave_v3_spec.md (despite the name, v1-era content).
- docs/interview-notes/IN_20260513_discovery.md (v1 discovery interview;
  git history preserves).
- docs/branding.md (v1-era; assets/banner.svg + logo.svg retained).
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