unitbob 0.7.7 → 0.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/runner/bdd.js
CHANGED
|
@@ -94,6 +94,8 @@ const BDD_STRATEGIES = {
|
|
|
94
94
|
'over nothing.',
|
|
95
95
|
'`conftest.py` is picked up by pytest itself whatever else sits beside it, so shared fixtures ' +
|
|
96
96
|
'belong there and there is no load-order trap to work around.',
|
|
97
|
+
'The project root is on `sys.path`, as it is for the structural branch: `from app import …` ' +
|
|
98
|
+
'works in `conftest.py` and in step files, with no `sys.path` insert of your own.',
|
|
97
99
|
],
|
|
98
100
|
},
|
|
99
101
|
},
|
|
@@ -175,13 +177,16 @@ async function runPytestBdd(projectRoot, mainPath) {
|
|
|
175
177
|
writeFileSync(join(projectRoot, PYTEST_BDD_PLUGIN_FILE), PYTEST_BDD_PLUGIN);
|
|
176
178
|
const command = await pickPython(projectRoot);
|
|
177
179
|
const stepsDir = join(BEHAVIORAL_ROOT, STEP_DEFINITIONS);
|
|
178
|
-
|
|
180
|
+
// `python -m pytest`, as the structural branch runs it: `-m` puts the working
|
|
181
|
+
// directory — the project root — on `sys.path`, so the shared conftest's
|
|
182
|
+
// `from app import …` resolves. The `pytest` script does not do that, and on
|
|
183
|
+
// microblog the conftest died on that import before the first Scenario, with
|
|
184
|
+
// no report to read (spec 51-1).
|
|
185
|
+
//
|
|
179
186
|
// `--rootdir .`, not the absolute root: the working directory is the project
|
|
180
187
|
// root in every place, and an absolute host path would name a directory that
|
|
181
188
|
// does not exist wherever the run actually happens.
|
|
182
|
-
const args =
|
|
183
|
-
? ['-c', PYTEST_INI_FILE, '-p', 'no:cacheprovider', '-p', pluginModule(), stepsDir, '--rootdir', '.']
|
|
184
|
-
: ['-m', 'pytest', '-c', PYTEST_INI_FILE, '-p', 'no:cacheprovider', '-p', pluginModule(), stepsDir, '--rootdir', '.'];
|
|
189
|
+
const args = ['-m', 'pytest', '-c', PYTEST_INI_FILE, '-p', 'no:cacheprovider', '-p', pluginModule(), stepsDir, '--rootdir', '.'];
|
|
185
190
|
const survivor = clearReport(join(projectRoot, PYTEST_BDD_REPORT));
|
|
186
191
|
const run = await runInProject(projectRoot, command, args, {
|
|
187
192
|
timeoutMs: BDD_TIMEOUT_MS,
|
|
@@ -212,10 +217,14 @@ function finalize(run, projectRoot, reportRel, survivor) {
|
|
|
212
217
|
report: readFreshReport(join(projectRoot, reportRel), survivor),
|
|
213
218
|
};
|
|
214
219
|
}
|
|
220
|
+
// The sidecar's `pytest` is the sign that suite-prepare installed pytest-bdd
|
|
221
|
+
// there; a venv always has a `python` beside it, and that is what runs. Judging
|
|
222
|
+
// by `python` alone would send an unprovisioned venv into "No module named
|
|
223
|
+
// pytest" instead of the message that names the fix.
|
|
215
224
|
async function pickPython(projectRoot) {
|
|
216
|
-
const
|
|
217
|
-
if (executable(commandFileOnHost(projectRoot,
|
|
218
|
-
return
|
|
225
|
+
const sidecarVenvBin = `${BEHAVIORAL_ROOT}/.venv/bin`;
|
|
226
|
+
if (executable(commandFileOnHost(projectRoot, `${sidecarVenvBin}/pytest`))) {
|
|
227
|
+
return `${sidecarVenvBin}/python`;
|
|
219
228
|
}
|
|
220
229
|
throw missingRunner('pytest-bdd');
|
|
221
230
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unitbob",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.8",
|
|
4
4
|
"description": "Unitbob connector — thin local hands for the Unitbob Rails brain. Owns no domain logic: it runs tools, relays bytes over the wire, and prints what the server returns.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@ markers, or paths. Do not edit production code, host-owned shared files, the
|
|
|
21
21
|
connector-owned harness, or another slice.
|
|
22
22
|
|
|
23
23
|
After every owned edit, run
|
|
24
|
-
`npx -y --loglevel=error unitbob@0.7.
|
|
24
|
+
`npx -y --loglevel=error unitbob@0.7.8 run-local <branch>` and inspect the machine
|
|
25
25
|
report. Look only at examples or scenarios matching your owned paths or case
|
|
26
26
|
markers. Do not require a green exit code from the whole branch: foreign failures
|
|
27
27
|
and an already-confirmed product red do not widen your scope. Repeat the bounded
|
|
@@ -47,7 +47,10 @@ Write strict JSON, and nothing else, to
|
|
|
47
47
|
"known_defect_probe": { "status": "not_supplied" },
|
|
48
48
|
"selection_review": {
|
|
49
49
|
"plan_digest": "<exact plan_digest from the request>",
|
|
50
|
-
"capability_reviews": [
|
|
50
|
+
"capability_reviews": [
|
|
51
|
+
{ "capability_id": "billing", "verdict": "pass" },
|
|
52
|
+
{ "capability_id": "reporting", "verdict": "pass" }
|
|
53
|
+
]
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
```
|
|
@@ -58,11 +61,19 @@ Nesting it one level deeper cost a run its publish; so did inventing values for
|
|
|
58
61
|
top-level keys", which dropped it entirely. Copy the digest, do not compute it.
|
|
59
62
|
|
|
60
63
|
Write `selection_review` only when the request carries a `plan_digest`, and give
|
|
61
|
-
it one entry
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
it one entry for **every capability in the request's `behavioral_assignment`** —
|
|
65
|
+
the map's whole list, not the `worker_plan`'s. The plan takes a few capabilities
|
|
66
|
+
and leaves the rest for a later build; a capability it left out still gets a
|
|
67
|
+
verdict, about the deferral: `pass` when the candidate's `capabilities` mark it
|
|
68
|
+
`unguarded` with an honest reason, `does_not_pass` when a promise was dropped or
|
|
69
|
+
merged away. In the example above, `billing` was planned and `reporting` was
|
|
70
|
+
deferred, and both are there. On soul, 2026-09-11, the reviewer wrote one entry
|
|
71
|
+
per plan item and the publish was refused for every capability it left out.
|
|
72
|
+
Its verdicts are `pass` or `does_not_pass` — there is no `pass_with_reservation`
|
|
73
|
+
at capability level — and `does_not_pass` owes a non-empty
|
|
74
|
+
`reviewer_objection_text` naming the lost promise, the unjustified merge, or the
|
|
75
|
+
dishonest deferral. Selection objections are recorded; they never block the
|
|
76
|
+
publish and never downgrade a lamp.
|
|
66
77
|
|
|
67
78
|
Omit `candidate_run`, `known_defect_context` and any runner report: the connector
|
|
68
79
|
owns those and adds them itself.
|
|
@@ -100,12 +111,21 @@ and no answer that consists of writing nothing.
|
|
|
100
111
|
it never excuses a "loads successfully" assertion for a promised record, state
|
|
101
112
|
change, message, or side effect.
|
|
102
113
|
|
|
103
|
-
`public_surfaces`
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
114
|
+
`public_surfaces` is your check of the worker's claim, not a list of your
|
|
115
|
+
own. Read the steps behind the Scenario and confirm every address in its
|
|
116
|
+
`surface_coverage` is really driven by some step; if every one is, copy that
|
|
117
|
+
list into `public_surfaces` verbatim — the two must be equal, and the server
|
|
118
|
+
refuses a Scenario where they are not. If the manifest names an address **no
|
|
119
|
+
step drives at all**, the verdict is `does_not_pass`, naming the address
|
|
120
|
+
(a2time, 2026-08-17: six Scenarios claimed addresses their steps never
|
|
121
|
+
touched). An address a step drives that the manifest does **not** name never
|
|
122
|
+
goes into the field: a `When` reaching another capability's address goes in
|
|
123
|
+
`reservation` (below); a `Then` re-reading state, a `Given` arriving, an `After`
|
|
124
|
+
leaving go nowhere. A claimed address that a `Then` drives rather than the
|
|
125
|
+
`When` is real — it stays in the list; say which step drove it in `reservation`
|
|
126
|
+
if it matters. On microblog, 2026-09-11, that finding was right and the list
|
|
127
|
+
was shortened to show it, and the publish was refused for the shortened list.
|
|
107
128
|
|
|
108
|
-
The `When`, and only the `When` — the same rule the worker wrote its list by.
|
|
109
129
|
What a `Given` does to arrive (sign in, create the table the Scenario needs) and
|
|
110
130
|
what an `After` does to leave are not the behaviour under test, so an address
|
|
111
131
|
they touch is not missing from `surface_coverage` and not a finding. On soul,
|
|
@@ -152,7 +172,10 @@ shared test database and proves nothing about the candidate that was bound.
|
|
|
152
172
|
|
|
153
173
|
**Do not rewrite anybody's verdict, including on a second pass.** If you find
|
|
154
174
|
yourself weighing whether an objection is worth the trouble, the answer is that
|
|
155
|
-
it costs this run nothing at all.
|
|
175
|
+
it costs this run nothing at all. A second pass that arrives with a
|
|
176
|
+
`validate-build` refusal corrects the field it names — `public_surfaces` brought
|
|
177
|
+
back to the manifest, a missing `selection_review` entry added — and leaves
|
|
178
|
+
every verdict as it was.
|
|
156
179
|
|
|
157
180
|
**Do not widen the review.** Whether a capability deserved more Scenarios is the
|
|
158
181
|
`selection_review` question and is answered per capability; everything else about
|