Release v3.0.0: beehave rewrite (.feature as sole source of truth)#27
Open
nullhack wants to merge 13 commits into
Open
Release v3.0.0: beehave rewrite (.feature as sole source of truth)#27nullhack wants to merge 13 commits into
nullhack wants to merge 13 commits into
Conversation
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`.
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.
This was referenced Jul 21, 2026
Closed
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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).
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):
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`)
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