Repository navigation
T024, U-5: the H-1 shim, ideation_dashboard.session_git answers with opendox.session_git (plan 038, CF-3) - #38
Conversation
…s with opendox.session_git (plan 038, CF-3)
A new scripts/ideation_dashboard/session_git.py, three statements, with no
__init__.py beside it:
import sys
from opendox import session_git as _m
sys.modules[__name__] = _m
The carve moved session_git.py verbatim to openDox-code as opendox.session_git.
tests/test_session_transaction.py:296 still spells the old path in its
module-level LOCK_HOLDER string, and a protected suite's module-level text
cannot be respelled under batch C (no entry can hold an edit that lies inside
no test). The shim restores the spelling instead, and edits no protected file.
With no __init__.py, scripts/ideation_dashboard/ stays a portion of the
ideation_dashboard namespace package, so in the composed run it merges with
openxFactory's own scripts/ideation_dashboard/ (which has none) and does not
shadow it. That follows from Python's namespace-package rule.
Of the four nodes in the transaction suite that need it, three go green,
composed. The fourth, test_a_lock_contention_through_the_CLI_is_the_same_refusal,
passes its first cause and stops at its second, the gate subcommand that no
host registers for that suite. tests/conftest.py is T020's, and not touched here.
Refs opensoft/openxFactory#656 (plan 038, CF-3; the ruling is 6013547504).
Lane: openXfactory-3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewer's GuideAdds a one-file H-1 compatibility shim for the pre-carve Sequence diagram for the legacy session_git compatibility importsequenceDiagram
participant Rival as Rival process
participant Legacy as ideation_dashboard.session_git
participant Shim as Compatibility shim
participant Real as opendox.session_git
Rival->>Legacy: import session_git as sg
Legacy->>Shim: Load namespace-package shim
Shim->>Real: from opendox import session_git
Shim->>Shim: sys.modules[__name__] = _m
Shim-->>Rival: Return opendox.session_git module
Rival->>Real: SessionGit
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The draft explicitly depends on pending cross-repository sequencing and composed validation.
Review effort: Balanced
Findings: None
What changed in this PR
Adds a namespace-package compatibility shim so legacy ideation_dashboard.session_git imports resolve to opendox.session_git.
Changes:
- Preserves module identity through
sys.modules. - Avoids an
__init__.pyto retain namespace composition.
| File | Description |
|---|---|
scripts/ideation_dashboard/session_git.py |
Provides the legacy import shim. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…ate verbs (plan 038, P4F-5) (#39) Lane: openxfactory-4 (openXfactory-4-openDox_extraction) Authored by lane openXfactory-3, delegated by lane openxfactory-4 (#656 6013608299) Arc: neutral-product-standalone-operability ## T020, U-1: host registration (HR) (plan 038, feature `038-opendox-document-tool-self-maintenance`) After set met: T004 (opensoft/openxFactory#1245) landed as 260234b5 at 2026-10-06T14:31Z. `tests/conftest.py` gains a governed-suite list beside `HOST_PLANE_SUITES`, `GOVERNED_HOST_SUITES`, with its own guard in `tests/test_host_plane.py`. For the length of each test of those eight suites it registers **the governed host**, and puts openDox's registry back afterwards, as the T086 stand-in fixture does: - **Composed run** (openxFactory's `scripts/` on the path, R1Q23 (a)): openxFactory's own composite, `opendox_host.profile()`. That is the object openxFactory's process-start call registers. It carries openXdox's gate and projection columns and openxFactory's lane column, whose `POST /actions/refresh` two cases request. It is found the way `:413-421` finds `corpus_adapter_openxfactory`: only a missing `opendox_host` itself counts as "absent". - **Elsewhere:** `GovernedStandInHost`, which carries openXdox's three governed columns and nothing else (`cli_gate.GateSubcommands`, `serve_gate.GateRoutesExtension`, `serve_projection.ProjectionRoutesExtension`). Each facet is built on first read. Three design points, each guarded: 1. **Only the composite is registered, never `register_openxfactory()`.** That call also fills openxFactory's process-wide seams. openxFactory's host has no call that takes those seams back, so they would outlive the test into every later module of a multi-file run. It also refuses while openXdox's registry holds the profile the root `conftest.py` registers. Measured: registering the composite alone gives the same node outcomes, node for node, as calling `register_openxfactory()` with an openXdox-registry swap. Both were tried in a prototype; neither was committed. 2. **openxFactory's reach stays out of the process.** Importing openxFactory's lane column runs `carved_reach.install()`, which puts openxFactory's pinned `openXdox/code/src` at the head of `sys.path`. `openxdox` is a namespace package. Measured before this change: after `install()`, `importlib.util.find_spec('openxdox.cli_gate').origin` resolves to openxFactory's nested `openXdox/code/src/openxdox/cli_gate.py`, not to this checkout. So the fixture: - imports openXdox's three columns from this checkout first; - reads the composite's built facets with `sys.path` saved, then restores it; - refuses the case by name if any `openxdox.*` module came from anywhere else. 3. **One registration helper for both lists** (`a_hosts_plane`). A module on both lists runs under the governed host, which is also a host's plane (`plane_host_for`). The four overlapping modules are `test_gateway_provenance.py`, `test_session_confinement.py`, `test_session_gates.py` and `test_session_verbs.py`. ### The contract, from `tasks.md` - **Realizes**: 12.5 (part). - **Falsifier**: the composed run of the named nodes (R2-INV-P4F § host-reg; part-oneoffs § Group H), quoted. - **Ruled**: R2Q8. **Decisions**: P4F-5 ("A governed-suite list with its own guard; the composite registered only in the composed run."), ruled with plan 038 by Brett Heap, everything as recommended (#656 `6013547504`). - **After**: T004. - **Files**: `tests/conftest.py` (first in the chain T020 → T021 → T073 → T074) and `tests/test_host_plane.py`. Nothing else is changed. ### The falsifier **Base measured.** - openXdox-code `56e1c238` (`origin/main`). - openDox-code `a9ac96f9` (`main`), force-reinstalled with `--no-deps` over openXdox's pin, as 12.5's falsifier does. - openxFactory `92010d3e` (`main`), with `openDox`, `openXdox` and `openXwallet` initialized recursively and its `scripts/` on `PYTHONPATH`. - Python 3.12.3 and pytest 8.4.2, installed by the CI command `python -m pip install -e ".[test]"`. - `LANG=C.UTF-8`, no `GIT_*` or `XF_*` variable, and no colour forcing. **Head**: `c52900e9`. **The command.** One process per suite file, over the 69 named nodes: host-reg 62 and Group H 7, read from R2-INV-P4F's node list: ``` PYTHONPATH="$OPENXFACTORY/scripts" python -m pytest -q -p no:cacheprovider -rA <that suite's named nodes> ``` RED at main `56e1c238`: ``` tests/test_branch_session.py: 5 failed in 6.17s tests/test_gateway_provenance.py: 10 failed in 8.64s tests/test_session_confinement.py: 15 failed in 17.78s tests/test_session_gates.py: 9 failed in 12.97s tests/test_session_lifecycle.py: 10 failed in 7.58s tests/test_session_notebook.py: 9 failed in 6.33s tests/test_session_verbs.py: 11 failed in 12.07s PASSED nodes: 0 FAILED-or-ERROR nodes: 69 ``` The causes at main: 41 nodes end in `SystemExit: 2` after `argparse.ArgumentError: argument command: invalid choice: 'gate'`. The others read `{'error': 'unknown_action', 'message': 'no such action route'}`, a `404` where `200` or `403` is expected, or (Group H's `create_document_offers_the_same_repository_flag_as_its_siblings`) no `gate` subparser at all. GREEN at head `c52900e9`: ``` tests/test_branch_session.py: 5 passed in 6.45s tests/test_gateway_provenance.py: 10 passed in 17.59s tests/test_session_confinement.py: 15 passed in 19.34s tests/test_session_gates.py: 2 failed, 7 passed in 11.59s tests/test_session_lifecycle.py: 10 passed in 6.40s tests/test_session_notebook.py: 9 passed in 10.87s tests/test_session_verbs.py: 2 failed, 9 passed in 13.52s PASSED nodes: 65 FAILED-or-ERROR nodes: 4 ``` The four that stay red have each moved past the host to a cause another task owns: - `test_session_gates.py::test_the_cli_offers_no_bypass_flag_for_the_endings`: `AttributeError: module 'opendox.cli' has no attribute 'cmd_gate_abandon_session'`. This is AL, T026. - `test_session_verbs.py::test_the_cli_offers_no_inline_content_and_no_bypass_flag`: `... 'cmd_gate_edit_document'`. AL, T026. - `test_session_verbs.py::test_the_save_cli_offers_no_token_and_no_bypass_flag`: `... 'cmd_gate_open_pr'`. AL, T026. - `test_session_gates.py::test_explicit_release_has_cli_and_http_parity`: `{'error': 'gate_refused', 'message': "the gate-action record schema could not be loaded from .../contracts/schemas/gate-action-record.schema.yaml"}`. This is GA, T021. **An erratum to R2-INV-P4F.** Its host-reg line "59 of 62 clear" was measured with HR, GA and RP simulated together. With HR alone it is **58**: `test_explicit_release_has_cli_and_http_parity` needs T021's GA as well. Its failure moves from `invalid choice: 'gate'` to the schema load above. T021's falsifier carries it. ### The 16 suites, composed, each alone (12.5's loop) | suite | main `56e1c238` | head `c52900e9` | |---|---|---| | `test_branch_session.py` | 5 failed, 127 passed | 132 passed | | `test_doxbench_mutation_boundary.py` | 80 passed | 80 passed | | `test_doxbench_share.py` | 2 failed, 32 passed | 2 failed, 32 passed | | `test_gate_loop_views.py` | 74 passed | 74 passed | | `test_gateway_provenance.py` | 10 failed, 14 passed | 24 passed | | `test_hosted_actor.py` | 5 passed | 5 passed | | `test_session_commits.py` | 22 passed | 22 passed | | `test_session_confinement.py` | 20 failed, 33 passed | 5 failed, 48 passed | | `test_session_gates.py` | 27 failed, 54 passed | 20 failed, 61 passed | | `test_session_lifecycle.py` | 10 failed, 43 passed | 53 passed | | `test_session_notebook.py` | 10 failed, 29 passed | 1 failed, 38 passed | | `test_session_records.py` | 29 passed | 29 passed | | `test_session_runbook.py` | 9 failed, 2 passed | 9 failed, 2 passed | | `test_session_transaction.py` | 4 failed, 53 passed | 4 failed, 53 passed | | `test_session_verbs.py` | 14 failed, 51 passed | 5 failed, 60 passed | | `test_staging_workbench.py` | 59 failed, 20 passed, 4 errors | 59 failed, 20 passed, 4 errors | | **total** | **170 failed, 4 errors, 668 passed** | **105 failed, 4 errors, 733 passed** | - The red set at main equals R2-INV-P4F's 174 rows, node for node. So no node has moved since the inventory, at these tips. - At head, 65 of the 174 turn green: host-reg 58 and Group H 7. - **No node green at main is red at head.** - The 109 still red are the buckets of T021–T026: schema 16, moved-name 16, hosted-index 3, display-js 55, one-off 15, and host-reg 4 (above). ### Group T's second cause and the host half of the four `cmd_gate_*` nodes, by simulation Each of these is masked by a first cause that another slice owns: - Group T: T024's shim, on draft #38. - The four `cmd_gate_*` nodes: T026's respellings in protected files. So they were proved in two scratch copies outside every clone, now deleted. Each copy had T024's three-line shim and the eight `cli_mod.cmd_gate_` → `cli_gate.cmd_gate_` respellings applied. The two copies differ only in `tests/conftest.py`: one at main, one at this head. ``` conftest at main: FAILED test_session_gates.py::test_the_abandon_cli_verb_enforces_the_human_gate_itself - SystemExit: 2 FAILED test_session_gates.py::test_the_cleanup_cli_verb_enforces_the_human_gate_and_deletes_the_branch - SystemExit: 2 FAILED test_session_verbs.py::test_the_cli_verb_enforces_the_human_gate_itself - SystemExit: 2 FAILED test_session_verbs.py::test_the_save_cli_verb_enforces_the_human_gate_itself - SystemExit: 2 FAILED test_session_gates.py::test_the_cli_offers_no_bypass_flag_for_the_endings - SystemExit: 2 FAILED test_session_verbs.py::test_the_cli_offers_no_inline_content_and_no_bypass_flag - SystemExit: 2 FAILED test_session_verbs.py::test_the_save_cli_offers_no_token_and_no_bypass_flag - SystemExit: 2 FAILED test_session_transaction.py::test_a_lock_contention_through_the_CLI_is_the_same_refusal - SystemExit: 2 conftest at head: PASSED (6 of the 7 above), and test_a_lock_contention_through_the_CLI_is_the_same_refusal PASSED FAILED test_session_gates.py::test_the_cleanup_cli_verb_enforces_the_human_gate_and_deletes_the_branch - assert 1 == 0 (stderr: "cleanup-abandoned-branch refused: the gate-action record schema could not be loaded ..." -- GA, T021) ``` ### Mutants of the new guards, all killed Each mutant was applied to a copy of the head and run against `tests/test_host_plane.py` in a lone checkout. Every one exits 1: ``` M1-drop-a-listed-suite: KILLED M2-add-an-unneeded-suite: KILLED M3-stand-in-wins-on-both-lists: KILLED M4-no-sys-path-restore: KILLED M5-no-registry-restore: KILLED M6-no-shadow-refusal: KILLED M7-swallow-any-missing-module: KILLED M8-never-the-composite: KILLED M9-no-pre-import: KILLED M10-gate-verbs-as-a-route: KILLED M11-drop-a-12.5-marker: KILLED M12-facets-not-read-in-window: KILLED ``` ### The suite as CI runs it, and the protected suites - **The required job's own command** (`python -m pytest -q --junitxml=… --deselect "$LEFT_OUT"`), in a venv built exactly as CI builds it (openDox at the `pyproject.toml` pin `dede32b4`), at this head: `selected=1105 passed=1101 skipped=4 failures=0 errors=0`. That is +6 on both floors (the six new cases in `tests/test_host_plane.py`), with skipped exactly 4. None of the new cases skips, lone or composed. `validate.yml` is not this task's file, so its floors are not raised here. - **The governed set is unchanged at 16.** The new code spells 12.5's three markers only in `tests/conftest.py`, which 12.5's pathspec `tests/test_*.py` does not match. So the check that holds the list does not join the set it scans. - **No protected file is edited.** T059's oracle, as 12.5 calls it, over this commit: ``` $ python3 scripts/protected_suites.py --landings="c52900e9eb80324fcecceaf6705a06c057a4d243" --suites="$(cat governed.txt)" ok: 0 protected edit(s), each entered and holding ``` ### CI and Copilot at the head `c52900e9` - Checks: `validate` pass, `SonarCloud Code Analysis` pass (2 of 2). - Copilot (`copilot-pull-request-reviewer[bot]`, requested through the reviewer API): review at `c52900e9`, state COMMENTED, "Findings: None". Its overview reads "Needs a closer look" for one stated reason only: "The draft remains dependent on open cross-repository prerequisite T004 and composed-host integration." That is this PR's After line (T004), not a finding. Review threads: 0, so 0 unresolved. ### Files changed - `tests/conftest.py`: `GOVERNED_HOST_SUITES`, `governed_suites()`, `plane_host_for()`, `GOVERNED_COLUMNS`, `GovernedStandInHost`, `openxfactory_host()`, `governed_host()`, `a_hosts_plane()`, and the autouse fixture `_the_governed_host_for_the_governed_suites`. The T086 fixture now uses `a_hosts_plane` and yields to the governed host for the four overlapping modules. - `tests/test_host_plane.py`: six lone cases holding the list, the precedence, the stand-in, the composed registration and its `sys.path` window, the shadow refusal, and the absent-host guard. ### Not done here - GA (T021), RP (T022), the J and L one-offs (T023, T024), openDox web (T025) and the allow-list (T026) are other slices. - The permanent composed workflow is T029. - In a run where `opendox_host` is absent, these eight suites cannot be imported today (`doc_health`, declared). So `GovernedStandInHost` is reached in no current run. It is exercised by the guard's lone cases, and measured composed in a prototype: host-reg 58 and Group H 5 of 7. The two refresh nodes need openxFactory's own lane route. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Arc: neutral-product-standalone-operability Lane: openxfactory-4 (openXfactory-4-openDox_extraction) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
|
READY at 65c3910 Lane: openxfactory-4 (openXfactory-4-openDox_extraction) Holder, at READY (T024, U-5, the H-1 shim as CF-3; plan 038; authored by lane openXfactory-3, #656 6020756009).
|
Lane: openXfactory-3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tes again (plan 038) (#42) Lane: openxfactory-4 (openXfactory-4-openDox_extraction) Authored by lane openXfactory-3, delegated by lane openxfactory-4 (#656 6013609326) Arc: neutral-product-standalone-operability ## T023, U-4: `swb-session.js` spells the model's two names at its call sites again (plan 038, feature `038-opendox-document-tool-self-maintenance`) After set met: T004 (opensoft/openxFactory#1245) landed as 260234b5 at 2026-10-06T14:31Z. Base brought up to date with main (T020 #39 c6d15b2, then T024 #38 8b64fae) by two merge commits; CI green at e8a067f; the composed falsifier was measured at c3c6a47, before the merges, and was not re-run (the merges brought only T020's host registration and T024's shim; INFERRED, not measured: the composed falsifier reads the same at the new head). ### Contract, from `tasks.md` - **Realizes**: 12.5 (part). - **Falsifier**: the 2 nodes, composed, quoted. - **Ruled**: R2Q8. (The entry names no Decisions line.) - **After**: T004. - **Files**: openXdox-code `src/openxdox/web/views/swb-session.js`, plus ONE new test file, `tests/test_swb_session_transport.py`. The test file is not in the task's `tasks.md` entry: lane openxfactory-4 added it to T023's Files by its ruling on Copilot's first finding on this PR (`lane4-to-lane3.log`, 12:52:51Z, "FIX-NOW, as in T022 (a)"; recorded on #656 in lane 4's latest comment). No protected suite is edited, and the file is not one of the 16 `HOST_PLANE_SUITES`. ### What and why `submitSession` took its two model calls from locals, `const headersOf = bound ? bound.consoleHeaders : consoleHeaders;` and `const repairWith = ...`, called as `headersOf(caps)` and `repairWith(send, repair)`. That is the `bound`-namespace refinement of Copilot review round 3, which arrived with the S5 carve in #18. `tests/test_session_confinement.py` pins both write transports (`swb-session.js` and `swb-create.js`) to the literal spellings `headers: consoleHeaders(caps)` and `withConsoleRepair(send, repair)`, so two nodes went red in the composed run. The pre-shed openxFactory copy spelled both exactly; `swb-create.js` still does. The trace is lane 3's R2-INV-P4F part, Group J. The fix shadows the two module-level names inside `submitSession` instead of respelling the call sites: ```js const MODULE_TRANSPORT = { consoleHeaders: (...args) => consoleHeaders(...args), withConsoleRepair: (...args) => withConsoleRepair(...args), }; // inside submitSession: const { consoleHeaders, withConsoleRepair } = bound || MODULE_TRANSPORT; // headers: consoleHeaders(caps), ... return withConsoleRepair(send, repair); ``` With a `bound` namespace the calls go to it, as before. With none, `MODULE_TRANSPORT` reaches the CURRENT install at call time, exactly as the two module-level consts do. The comment above it does not quote the pinned needles, because a needle quoted in its own source satisfies itself; each pinned needle occurs once in the file, in code (counted). ### The new test file, `tests/test_swb_session_transport.py` The pinned spellings are text assertions, so they cannot tell the right model from the wrong one. Two node probes run the shipped module out of the composed bundle and assert on the request that reaches the injected fetcher: - **bound**: two `firstEditTransport`s over two models that differ in exactly `consoleHeaders` and `withConsoleRepair`, both built before either is called; each request must carry its own model's header (from its own `caps`) and go through its own model's repair policy. - **unbound**: a mounted session form, submit pressed; the request must carry the header the mounted model's `consoleHeaders` built and go through its `withConsoleRepair`. (The outcome panel the column writes after the request needs the shell's `page-overlay` host, which a stub DOM lacks, so the handler rejects after the fetcher is called; the probe records that and asserts on the request only.) Both pass at main's own code (checked in the base clone), so they describe the behaviour the respelling preserves. They run in the lone CI form too: no openxFactory module, node only, as `test_gate_loop_probes.py` does. ### The falsifier **Base measured**: openXdox-code `56e1c238` (origin/main), openDox-code `a9ac96f9` (plain install, `--no-deps`, over openXdox's pin, as R2-INV-P4F did), openxFactory `92010d3e` with `openDox`, `openXdox` and `openXwallet` initialized recursively and its `scripts/` on `PYTHONPATH` (R1Q23 (a)'s form). Python 3.12.3, pytest 8.4.2, the CI install command (`python -m pip install -e ".[test]"`). **Head**: `c3c6a47` (the fix is `f19e05c`; the test file is `c3c6a47`). Run with `FORCE_COLOR`, `CLICOLOR` and `COLORTERM` unset and `TERM=dumb`. RED at main (`56e1c238`), composed: ``` FAILED tests/test_session_confinement.py::test_the_renderer_presents_the_console_token_from_the_capability_probe FAILED tests/test_session_confinement.py::test_the_renderer_repairs_a_stale_token_by_re_reading_it_never_by_reloading 2 failed in 0.45s ``` (each: `AssertionError: swb-session.js`, from `'headers: consoleHeaders(caps)' in body` at `:290` and `'withConsoleRepair(send, repair)' in body` at `:383`) GREEN at head `c3c6a47`, same environment: ``` PASSED tests/test_session_confinement.py::test_the_renderer_presents_the_console_token_from_the_capability_probe PASSED tests/test_session_confinement.py::test_the_renderer_repairs_a_stale_token_by_re_reading_it_never_by_reloading 2 passed in 0.67s ``` ### What else reads `swb-session.js`, and did not move Every test file in the tree (`tests/test_*.py` and `tests/integration`, named on the command line so the declared exclusion does not drop them), composed as above, run SERIALLY on one idle container with `--continue-on-collection-errors` (`test_gate_console.py` fails to collect in both, for want of `scripts/proposal-support.py`): | | nodes | failed | errors | passed | skipped | |---|---|---|---|---|---| | main `56e1c238` | 3671 | 550 | 94 | 3021 | 6 | | head `c3c6a47` | 3673 | 548 | 94 | 3025 | 6 | Compared node by node: the 3671 ids at main all appear at the head, and EXACTLY TWO of them change status, both red to green, the two above. The two extra nodes are the new file's, both passing. Nothing goes green to red. The suites that read `swb-session.js`, red nodes at main and at the head: `test_gate_loop_probes.py` 0 / 0, `test_renderer.py` 25 / 25, `test_doxbench_save.py` 3 / 3, `test_session_confinement.py` 20 / 18 (the two above), `test_staging_workbench.py` 63 / 63, `test_doxchat_model_intake.py` 10 / 10, `test_doxbench_transport.py` 6 / 6, `test_assembled_bundle.py` 4 / 4, `test_doxbench_scope.py` 1 / 1, `test_gate_loop_views.py` 0 / 0. The three suites the trace names read 28 red on both sides, the same node ids, as the trace's trial found. Earlier pairs, run overlapping and while the container was out of process slots, showed `Resource temporarily unavailable` and `can't start new thread` errors in git-fixture and thread-spawning suites; they were discarded and re-run serially after the container was restarted, which is the pair above. ### Mutants of the fix Spelling mutants, killed by the pinned nodes (composed, over `test_session_confinement.py`, `test_gate_loop_probes.py`, `test_doxbench_save.py` and `test_renderer.py`, compared with the head's own red set): | Mutant | Result | |---|---| | M1: respell the `headers:` call as `(bound ? bound.consoleHeaders : MODULE_TRANSPORT.consoleHeaders)(caps)` (behaviour kept) | KILLED: both target nodes go red (the stale-token node pins the same headers spelling) | | M2: respell the return as `(bound ? bound.withConsoleRepair : MODULE_TRANSPORT.withConsoleRepair)(send, repair)` | KILLED: the repairs-a-stale-token node goes red | Behaviour mutants, which keep every pinned spelling and which only the new file can kill (run composed AND in the lone CI venv, same result in both): | Mutant | Killed by | |---|---| | M3: drop `bound ||`, so the module pair is always used | `test_js_each_built_transport_sends_through_its_own_models_header_and_repair` (1 failed, 1 passed) | | M4: `MODULE_TRANSPORT.consoleHeaders` calls `withConsoleRepair` | `test_js_a_mounted_form_sends_through_the_mounted_models_header_and_repair` (1 failed, 1 passed) | | M6: `MODULE_TRANSPORT.withConsoleRepair` calls `consoleHeaders` | `test_js_a_mounted_form_sends_through_the_mounted_models_header_and_repair` (1 failed, 1 passed) | Before the new file, M3 and M4 survived every committed suite (the finding on the first head). ### Other gates - **CI form, lone** (`python -m pytest -q --deselect "$LEFT_OUT"` from the root, the CI venv, no `PYTHONPATH`) at `c3c6a47`: `1097 passed, 4 skipped, 1 deselected`; the workflow's triple reads `selected=1101 passed=1097 skipped=4 failures=0 errors=0`, which meets its pins (floors 1099 / 1095, skipped exactly 4, 0 / 0). At `f19e05c` and at main it read 1099 / 1095. - **Protected suites:** `python3 scripts/protected_suites.py --landings=<both commits> --suites=<the 98 tests/test_*.py at origin/main>` prints `ok: 0 protected edit(s), each entered and holding`, with and without `--chains`. The new file is not in `HOST_PLANE_SUITES`. - **Already red at main, composed, not from this change:** `tests/test_dependency_direction.py::test_first_attribute_access_refuses_and_names_the_layering` (the T024 writer attributes it to `ideation_dashboard` being importable when composed; I did not trace it); red in both the base and head runs. - **CI at the head:** `validate` pass and `SonarCloud Code Analysis` pass (2 of 2) at `c3c6a47`. - **Copilot:** two reviews, one per head. At `f19e05c8` (review 5428398958): 1 finding, that no committed test distinguishes the bound and unbound transports. Fixed by `c3c6a47`; thread answered and resolved. At `c3c6a470` (review 5428827782, COMMENTED, body "Changes recommended"): 2 findings, both on the new file. - "the declared one-file scope": this body said one file and "no test is added"; the body is corrected (Files above), the ruling that widened Files is cited, and the thread is answered and resolved. - "update the workflow coverage floors": see below. An accepted limit, answered and resolved. - 0 unresolved threads at the head. The holder rules on the "Changes recommended" label. - Holder ruling #656 `6017860539`: both Copilot findings, the "Changes recommended" label included, are ACCEPTED as they stand; the CI floors are left to T029. - **At the new head `e8a067f3`** (two update-branch merges of `main`, both merge commits: `3b6cd13b` took T020 #39 `c6d15b27`, then `e8a067f3` took T024 #38 `8b64fae0`): every check-run completed success; Copilot review 5432366190, state COMMENTED, "Approval recommended", `Findings: None` (requested through the reviewer API after about 10 minutes without an auto-review); 3 review threads in all, all resolved, 0 unresolved, none new. Against `c3c6a470` the merges changed only `tests/conftest.py`, `tests/test_host_plane.py` and `scripts/ideation_dashboard/session_git.py`, the files of T020 and T024. - **SonarCloud:** the `SonarCloud Code Analysis` check passes (quality gate); I did not list individual issues. ### What is NOT done - **The CI floors are not raised** (`MIN_SELECTED` / `MIN_PASSED` in `.github/workflows/validate.yml`, 1099 / 1095; this PR's head selects 1101 and passes 1097). `validate.yml` is not in T023's Files, plan 038 gives its floors in this repository to no task (T017 and T051 re-pin openDox-code's), and the open sibling PRs that add tests (#39, #40, #41) would collide with this one on the same two lines. Floors are minimums, so the check passes and the margin is two wider than it was. The workflow's own comment says the commit that adds a test SHOULD raise them with the reason; whether and in which PR is for the holder to sequence. - The other groups of R2-INV-P4F's one-off bucket (H, L, T, W, S) are other tasks. Arc: neutral-product-standalone-operability Lane: openxfactory-4 (openXfactory-4-openDox_extraction) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s it, nothing vendored (plan 038, P4F-4) (#40) Lane: openxfactory-4 (openXfactory-4-openDox_extraction) Authored by lane openXfactory-3, delegated by lane openxfactory-4 (#656 6013608628) Arc: neutral-product-standalone-operability ## T021, U-2: the gate console's schemas (GA), resolved without vendoring (plan 038, feature `038-opendox-document-tool-self-maintenance`) After set met: T004 #1245 → 260234b5; T020 #39 → c6d15b2. **The branch.** - T021's own commits: - `07361582`, U-2; - three Copilot fixes: `ecf0ba9b`, `542a5c40` and `a183dc46`. - Main came in by merges, never a rebase: - `72633f93` (T020, #39 → `c6d15b27`); - `22a77c16` (T024, #38 → `8b64fae0`); - `122bacce` (T023, #42 → `d45a0939`), the head. - The diff against `main` is T021's three files. - The first merge had one conflict, at the end of `tests/conftest.py`: T021's own append after T020's last hunk, with main's side empty. It was resolved to this branch's side, so the merged tree equalled `ecf0ba9b`'s byte for byte. ### What changes Before this change, `gate_console._validate_contract_document` read both of its schemas from `Path(__file__).resolve().parents[2] / "contracts" / "schemas"`. That was openxFactory's own tree before the carve; in this leg it is the checkout root, which holds no `contracts/`. Now each schema is read where its owner keeps it: - **`gate-action-record.schema.yaml` (openXdox-spec's)** is read from this distribution's own packaged copy, through `openxdox.contracts.verified_bytes("gate-action-record")`. That call checks the copy against `copies.yaml`'s digest before a byte is parsed. The copy record already holds this schema; nothing is added to it. - **`demotion-execution-receipt.schema.yaml` (openxFactory's)** is read from a source the HOST registers with the new seam, `gate_console.register_contract_schema_source(directory)`. It follows openDox's seam idiom: - registering the same directory again is a no-op; - a different one raises `ContractSchemaSourceRefused`; - a directory that lacks the schema is refused when it is registered. `unregister_contract_schema_source()` and `contract_schema_source_registered()` complete it, so T030's host wiring can fill and take back the seam the way `opendox_host` treats openDox's. **With nothing registered, validating a receipt refuses by name** (R1Q27 (a)): ``` GateRefused: the demotion execution receipt cannot be validated: its schema, demotion-execution-receipt.schema.yaml, is the host's contract, and no contract schema source is registered. A host registers the directory it keeps its contract schemas in, once, at process start, with openxdox.gate_console.register_contract_schema_source(<directory>) (R1Q27 (a): a kind is validated only where the tree it runs from supplies its schema). ``` - **A host's schema is checked before it is used** (`_schema_errors`, from the Copilot rounds below). It is parsed, checked against the validator's own meta-schema, then applied. ANY failure at any of the three steps refuses as `GateRefused`, naming the step and the exception's class; a `SchemaError` keeps its own "is not a valid JSON Schema" message. This matters because the demotion verb writes its receipt after it has moved files, and it reports only a `GateRefused` there as "DEMOTION EXECUTED BUT RECEIPT NOT WRITTEN". Any other exception ended the verb with a traceback. - **`tests/conftest.py`**, one registration placed after U-1's: where the run is composed, it registers openxFactory's `contracts/schemas/` from the composed tree at process start, as openxFactory's host wiring will from T030. A lone checkout registers none. **Line numbers kept.** `tests/test_no_hardcoded_status_words.py`'s `PROSE_EXEMPT` pins seven prose f-strings in `gate_console.py` by line number (315 to 1851). So: - the seam and the schema helpers sit under their own header near the end of the module, before the PEP 562 section; - `openxdox.contracts` is imported inside `_packaged_schema_text`; - `_validate_contract_document` and its two call sites keep their exact line counts. No line before 1851 moves. That test file is not this task's, so it is unchanged. ### The contract, from `tasks.md` - **Realizes**: 12.5 (part). - **Falsifier**: the 16 schema nodes (R2-INV-P4F § schema), composed, quoted; a lone-checkout test that the receipt refuses by name; `test_packaged_validator.py` unchanged and green. - **Ruled**: R2Q8. **Decisions**: P4F-4 ("`gate-action-record` is read from openXdox-code's own package; `demotion-execution-receipt` from a schema source the HOST registers (the composed conftest, then host wiring at T030); a lone checkout refuses by name. Nothing vendored."), ruled with plan 038 by Brett Heap, everything as recommended (#656 `6013547504`). - **After**: T020 (the conftest), met: #39 → `c6d15b27`. T004 #1245 → `260234b5`. - **Files**: `src/openxdox/gate_console.py` (first: T021 → T074), `tests/conftest.py` (second: T020 → T021 → T073 → T074), and a new `tests/test_gate_console_schema_source.py`. - **Note for the holder:** plan 038's single-writer table (`plan.md` § "Parallel slices") has no row for the new `tests/test_gate_console_schema_source.py`. Lane 3 flagged this to lane 4 as a RECHECK FIX (lane3-to-lane4 log, 2026-10-06T00:30:30Z). The file is created here because T021's Files line names it, and no other task writes it. ### The falsifier, at the head `122bacce` **Environment.** - openXdox-code: `main` `d45a0939`, against the head `122bacce`. - openDox-code `34c20641`, force-reinstalled with `--no-deps`, as 12.5's falsifier does. - openxFactory `51456835`, with `openDox` (`e1e3a3c3`, spec `f7ee3c76`), `openXdox` and `openXwallet` initialized recursively, and its `scripts/` on `PYTHONPATH` (R1Q23 (a)). - Python 3.12.3, pytest 8.4.2, CI's install command, `LANG=C.UTF-8`, no colour forcing. - Two mains moved during the measurement, and neither was taken into it: - openxFactory moved to `e6380965`: one commit, with an empty diff over `scripts/`, the submodule gitlinks and `contracts/`; - openDox-code moved to `84f8ed83`: #91, T025, the staging model, `staging-workbench.js` and their tests, none of them T021's. **1. The 16 schema nodes, composed**, with `test_explicit_release_has_cli_and_http_parity`. That node needs this GA as well as T020's HR (#39's erratum to R2-INV-P4F). All are in `tests/test_session_gates.py`: ``` PYTHONPATH="$OPENXFACTORY/scripts" python -m pytest -q -p no:cacheprovider -rA <the 17 nodes> ``` | at | result | |---|---| | main `56e1c238` (first round) | `17 failed` | | T020 `c52900e9` (first round) | `17 failed` | | T021 `07361582`, `ecf0ba9b` | `17 passed` | | main `c6d15b27` / head `72633f93` | `17 failed` / `17 passed` | | main `8b64fae0` / head `542a5c40` | `17 failed` / `17 passed` | | **main `d45a0939` / head `122bacce`** | **`17 failed` / `17 passed`** | The causes at main: - `{'error': 'gate_refused', 'message': "the gate-action record schema could not be loaded from <checkout>/contracts/schemas/gate-action-record.schema.yaml'"}`; - `FileNotFoundError: … '<checkout>/contracts/schemas/gate-action-record.schema.yaml'`; - the knock-on `assert 409 == 200`. The 16 nodes: - `test_the_abandon_reason_survives_the_fr028_branch_cleanup` - `test_cleanup_deletes_the_branch_once_the_proposal_exists` - `test_matching_proposal_evidence_from_before_abandonment_requires_fresh_release` - `test_a_later_edit_elsewhere_in_the_change_folder_does_not_freshen_the_origin` - `test_a_second_matching_record_recorded_after_abandonment_releases_cleanup` - `test_branch_advanced_after_abandonment_requires_explicit_current_head_release` - `test_active_declared_origin_releases_cleanup_without_a_pick_edge` - `test_archived_exact_origin_releases_cleanup_after_the_tile_disappears` - `test_execution_receipt_releases_cleanup_for_the_exact_destination` - `test_legacy_manifest_requires_an_exact_returned_artifact` - `test_explicit_human_release_cleans_a_true_orphan_and_records_why` - `test_non_staged_orphans_use_the_explicit_human_release_lane[cluster-cl-orphan]` - `test_non_staged_orphans_use_the_explicit_human_release_lane[possible-pos-orphan]` - `test_cleanup_unwinds_its_record_when_branch_deletion_fails` - `test_cleanup_restores_the_exact_ref_when_record_finalization_fails` - `test_the_durable_ending_marker_is_the_second_admissible_proof` **2. The lone-checkout test**, the new `tests/test_gate_console_schema_source.py`, 6 cases. The console imports openxFactory's `doc_health` when it loads (R1Q6 (d)), though nothing the cases run calls it. So each case runs the console in a child interpreter, beside a stand-in `doc_health` that answers the three names the import reads: `corpus.STATUS_SCAN_LINES`, `lines.split_keepends` and `lines.join_rows`. The CI process never imports the console through it. The cases: - with no source registered, a receipt refuses, naming `register_contract_schema_source(<directory>)`; - the record is validated against the packaged copy: - a record the schema does not admit refuses; - a registered source whose own `gate-action-record.schema.yaml` admits nothing is never read for a record; - a packaged copy that drifted from its digest refuses; - a receipt is validated against the registered source's schema; - the seam holds one source, as above; - a host schema the console cannot use refuses as `GateRefused`. That is any of: empty; `required: 1`; an unresolvable `$ref`; an unclosed flow sequence; a 5000-deep nesting; an impossible date; a cyclic mapping alias; a cyclic list alias; - the module reads no `parents[...]` index (by AST). Red at the base `c52900e9`: `5 failed in 1.38s`. The causes: - `AttributeError: module 'openxdox.gate_console' has no attribute 'register_contract_schema_source'`; - `GateRefused: the gate-action record schema could not be loaded from <scratch>/contracts/schemas/…`; - `gate_console.py reads a parents index at lines [642]`. Green at every T021 head; `6 passed` at `122bacce`. **3. `tests/test_packaged_validator.py` is unchanged and green.** `git diff main -- tests/test_packaged_validator.py src/openxdox/contracts/` is empty. ### The 16 suites, composed, each alone (12.5's loop), at `122bacce` against `main` `d45a0939` | | passed | failed | errors | red | |---|---|---|---|---| | main `d45a0939` | 739 | 99 | 4 | 103 | | head `122bacce` | 756 | 82 | 4 | 86 | - **No node green at main is red at the head: 0 regressions.** - The 17 nodes the head clears are exactly the 17 falsifier nodes above. - No node is red outside R2-INV-P4F's 174, at main or at the head. - No log in either run shows `Errno 11` or `cannot fork`. - Since the first round (main 174, T020 109, T021 92), the landings of T024 (#38) and T023 (#42) cleared 4 and 2 nodes. They cleared the same nodes at main and at the head. ### Mutants of the new guards, all killed **Round 1, at `ecf0ba9b`.** Each mutant was applied to a copy of the head and run against `tests/test_gate_console_schema_source.py` in a lone checkout. Every one exits 1: ``` M1-receipt-read-beside-the-module: KILLED M2-record-read-from-the-host-source: KILLED M3-no-digest-check: KILLED M4-second-source-replaces: KILLED M5-no-schema-presence-check: KILLED M6-same-source-by-identity: KILLED M7-unregister-keeps-it: KILLED M8-registered-query-lies: KILLED M9-no-source-means-skip-validation: KILLED M10-no-meta-schema-check: KILLED M11-application-errors-escape: KILLED ``` M10-no-composed-registration drops the conftest's registration. Run composed, it is KILLED: `1 failed, 16 passed`. The failing node is `test_execution_receipt_releases_cleanup_for_the_exact_destination`, the only one of the 16 that validates a receipt. **The Copilot fixes**, each killed by the same case: - at `542a5c40`, dropping `RecursionError`, or dropping `ValueError`, from the parse step's catch; - at `a183dc46`, which replaced that tuple with a catch-all: - parse-catches-YAMLError-only: KILLED (`ESCAPED RecursionError`); - no-check-catch-all: KILLED (`ESCAPED RecursionError`). ### The suite as CI runs it, and the protected suites - **The required job's own command**, in a venv built as CI builds it (openDox at the `pyproject.toml` pin `dede32b4`), at `122bacce`: `selected=1113 passed=1109 skipped=4 failures=0 errors=0`. All three pins hold: selected at least 1099, passed at least 1095, skipped exactly 4. None of the new cases skips. - **The governed set is unchanged at 16**, and the new file names none of 12.5's markers. - **No protected file is edited.** T059's oracle, as 12.5 calls it, over a simulated squash of `122bacce`'s tree onto `d45a0939` (`c3260498`; it touches the three files above). The merge head itself would be diffed against its first parent, which is no landing: ``` $ python3 scripts/protected_suites.py --landings="<the simulated squash>" --suites="$(cat governed.txt)" ok: 0 protected edit(s), each entered and holding ``` ### Copilot, by round - **At `07361582`: r4194497045**, medium, "Validate host schemas before demotion to prevent uncaught errors". - **Fix-now**: a real bypass with a simple payload. Measured at `07361582`, all three cases escaped: - empty: `AttributeError: 'NoneType' object has no attribute 'get'`; - `required: 1`: `TypeError: 'int' object is not iterable`; - an unresolvable `$ref`: `_WrappedReferencingError`. - Fixed in `ecf0ba9b` by checking the schema with `check_schema()` before applying it. Answered and resolved. - **At `ecf0ba9b`**: "Findings: None". - **At `72633f93`: r4197914632**, "Translate all YAML parser failures to GateRefused". - **Fix-now**: `yaml.safe_load` raises past `YAMLError`. A 5000-deep `[[…]]` raised `RecursionError`, and `x: 2001-02-30` raised `ValueError`. - Fixed in `542a5c40`. Answered (4198580351) and resolved. - **At `542a5c40`: r4198638746**, "Catch non-SchemaError exceptions from check_schema". - **Fix-now**: the cyclic mapping `properties: &p {a: {properties: *p}}` loads, and `check_schema` then raised `RecursionError`. Copilot's own payload, `allOf: &loop [*loop]`, is a `SchemaError` under the jsonschema CI resolves today (4.26.0, from `>=4.18`). - Fixed in `a183dc46`: this was the third finding on the boundary, so each step now refuses any failure. Answered (4199311149) and resolved. - **At `122bacce` (the head; review 5433288963, COMMENTED): r4199337845**, "Avoid hard-coding jsonschema's version-specific cyclic failure". - **ACCEPTED LIMIT, under the convergence rule (#656 `5988818366`), for the holder at READY.** It is not a bypass: production behaviour is correct whatever any jsonschema release does with the cycle. The finding concerns the test's precision only. - The assertion `could not be checked: RecursionError` pins the outcome under the jsonschema CI resolves today (4.26.0) on purpose, because it shows the check step's own catch is what refuses. `pyproject.toml` allows `jsonschema>=4.18`, so a later release that refuses the cycle as a `SchemaError` would fail this case loudly, and its message would name the new outcome. - The one-line remedy is to assert only the `GateRefused: ` prefix, as the cyclic-list case does. The dropped-catch mutant stays killed either way. Answered (4199346014) and resolved. **At the head `122bacce`:** - Checks: `validate` pass, `SonarCloud Code Analysis` pass (2 of 2). - Review threads: 4, all answered and resolved. 0 unresolved. ### Files changed (against `main`) - `src/openxdox/gate_console.py`: - the two readers, `_packaged_schema_text` and `_host_schema_text`; - the schema checker, `_schema_errors`; - the seam: `register_contract_schema_source`, `unregister_contract_schema_source`, `contract_schema_source_registered` and `ContractSchemaSourceRefused`; - `GATE_ACTION_RECORD_SCHEMA` and `DEMOTION_RECEIPT_SCHEMA`. `_validate_contract_document` now takes the schema's text. - `tests/conftest.py`: the composed run's registration of openxFactory's `contracts/schemas/`. - `tests/test_gate_console_schema_source.py`: new, 6 lone cases. ### Not done here - openxFactory's host wiring registers the same source at T030 (lane 4). It becomes an entry in `opendox_host.seams()`, with `contract_schema_source_registered` and `unregister_contract_schema_source` as its take-back pair. - The runbook (T022) and the allow-list respellings (T026) are other slices. `test_the_cleanup_cli_verb_enforces_the_human_gate_and_deletes_the_branch` needs T020's HR, T026's AL and this GA together. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Arc: neutral-product-standalone-operability Lane: openxfactory-4 (openXfactory-4-openDox_extraction) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…to T022 T020 (#39 c6d15b2), T024 (#38 8b64fae), T023 (#42 d45a093) and T021 (#40 f70bc9c) have landed. This branch carried T021's ecf0ba9 versions of src/openxdox/gate_console.py and tests/test_gate_console_schema_source.py, and main holds T021's final squash. Both conflicts are resolved to main's side, since neither file is T022's. Against main the branch now differs only in T022's three files: .gitignore, scripts/composed_placements.py and tests/test_composed_placements.py. Lane: openXfactory-3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…span kind (plan 038, H-2) (#43) Lane: openxfactory-4 (openXfactory-4-openDox_extraction) Authored by lane openXfactory-3, delegated by lane openxfactory-4 (#656 6015602254) Arc: neutral-product-standalone-operability ## T026, U-7: the allow-list for 12.5's governed suites, with R-1 (a)'s span kind (plan 038) The After set has landed: - T020 (#39, at c6d15b2). This branch is up to date with `main` f70bc9c, which also holds T024 (#38), T023 (#42) and T021 (#40). `main` was merged in at e6d99691, 3226d5c, 9e0979c and 9c29855, never by rebase; - T004 (opensoft/openxFactory#1245, at 260234b5); - T005's batch Q (opensoft/openxFactory#1248, at 91e961a0), which gives 12.5's oracle call `--chains`. openxFactory `main`'s `add-neutral-product-standalone-operability/tasks.md:3055` now reads `python3 scripts/protected_suites.py --chains --landings="$(cat "$W/x-arc.txt")" --suites="$(cat "$W/governed.txt")"`. - **Realizes**: 12.5 (part). - **Falsifier** (tasks.md): the oracle prints `ok: … each entered and holding`; the nodes, composed, quoted. - **Ruled**: - R2Q8, CF-4 (H-2), R-1 and W-1, with plan 038 by Brett Heap, everything as recommended (opensoft/openxFactory#656 comment 6013547504). - Brett's ruling on this task's four points (comment 6016648451, every answer the recommended one): 1. 12.5's call takes `--chains`, and T026 lands as one PR. 2. and 3. R-1 (a)'s spans widen to six, the helpers hand each harness the SERVED display, and T020's host registration covers `tests/test_staging_workbench.py`, edited by T026 in T020's two files once #39 landed. 4. T026's Files gain `tests/test_protected_suite_check.py`. - The holder respelled the 'cluster' node to `group neighbourhood`, as measured. ### What this PR does 32 entries are appended to `tests/protected_suite_respellings.yaml`, giving 51 in all. Each is one edit inside its named test or span, with its exact old and new text, and the entries for each suite are chained by blob. | suite | entries | what | |---|---|---| | `test_session_gates.py` | 3 | CF-4: `cmd_gate_*` read from `openxdox.cli_gate` | | `test_session_verbs.py` | 5 | CF-4: `cmd_gate_*` 4, and Group W `openxdox.generator.live_topic_health` | | `test_session_confinement.py` | 3 | CF-4: `serve_projection.hosted_index` | | `test_doxbench_share.py` | 2 | CF-4: each swept module at its one home; the record schema read through `openxdox.contracts.verified_bytes`, digest-checked against `copies.yaml` (Copilot's finding) | | `test_staging_workbench.py` | 19 | see below | The 19 staging entries, in chain order (allow-list entries 33 to 51): - **33**: the 'cluster' node, respelled to `group neighbourhood`. - **34, 37, 38 and 39**: CF-4 Group S2, the four slice-S5 spellings of the create and session columns. - **35 and 36**: R-1 (a)'s two in-test relocations, `:1291` and `:1333`. Each route is one `export const …_ROUTE` of `swb-session.js`, and neither the view nor the model names one. - **40 to 49**, R-1 (a)'s six spans (seven `span:` entries) and three in-test respellings: - `_CREATE_HARNESS` (two entries), `_SESSION_HARNESS` and `_HOSTILE_HARNESS`: each imports its route from the column RULED Q3 moved it to (`CREATE_ROUTE` from `swb-create.js`, `sessionRoute` from `swb-session.js`) and the reader from `display.js`. Each installs `setDisplay(readDisplay({ display: cases.display }))` before anything derives, as the shell does. `BRAINSTORM_AREA` and `STAGING_AREA` become the model's `capturedArea()` and `organizedArea()`. - `_run_create`, `_run_session` and `_hostile_descriptors`: each copies the sibling files its harness imports (and what they import), with a `package.json` that makes node read them as modules, as `test_session_confinement.py`'s harness does. Each hands its harness `/capabilities["display"]`, by the statement `serve.build_server()` makes, replayed as `test_gate_loop_views.py`'s `_served_display` replays it. - Three in-test respellings, **flagged below**. - **50 and 51**: the docstrings of the two relocated tests now say what their assertions hold (Copilot's findings). **`scripts/protected_suites.py`, R-1 (a)'s span kind.** An `admitted` entry may name, in place of `test`, a `span`: one module-level constant, or one module-level helper function (its decorators included). The entry's `old` text must lie inside that statement before the landing, and its `new` text inside it at the landing. Three further rules apply: - **Counted fail-closed.** The span's name must have exactly one binding in the module, counting every binding form in every scope: assignment, augmented or annotated assignment, `for` or `with` target, walrus, `del`, `def` or `class`, an import or a star import, an `except … as` alias, a `match` capture, a type parameter and a parameter. - **Alone on its lines.** No other statement may share a line with the span. - **Never a span:** a class, an import, a test, two targets, an annotation alone, or an added name. Only an admitted entry may name a span. The module's usage block and help now give 12.5's call with `--chains`, as batch Q has it. **`tests/test_protected_suite_check.py`**: 33 span cases (87 in the module). #### FLAGGED for the holder's word: three in-test respellings beyond the plan's 17 These are CF-4's kind. T026's measurement reported them at comment 6016415390 (point 2), and comment 6016648451 ruled that point. They close the last three of the suite's nodes, which the served display alone does not reach. Each is entered as a `respelling` naming that history, and each stands or falls on your word: 1. `test_gate_off_descriptor_is_the_real_cli_invocation`: `cli.cmd_gate_create_document` becomes `cli_gate.cmd_gate_create_document`, imported in the test from `openxdox.cli_gate`. The old spelling raised `AttributeError`. 2. `test_gate_off_session_affordances_are_the_real_cli_invocations`: `cli.cmd_gate_edit_document`, `cmd_gate_open_pr` and `cmd_gate_abandon_session` become `cli_gate.…`, likewise. 3. `test_the_notebook_refresh_is_a_descriptor_in_BOTH_gate_postures`: `REPO_ROOT / "scripts" / "sync-notebooklm-books.py"` becomes the one directory on the import path that holds the script, which is openxFactory's `scripts/` in the composed run (R1Q23 (a)). A run that finds it in none, or in more than one, fails. The old path raised `FileNotFoundError`. No assertion is weakened in any of the three, and every argument and flag assertion stays. ### T020's two files, edited once #39 landed (commit 242889b) T020's registration has to cover `tests/test_staging_workbench.py`. Without it every harness helper refuses `ProfileNotRegistered`, as measured below. The edit is to T020's `tests/conftest.py` and to the scan in `tests/test_host_plane.py`: - `"test_staging_workbench.py"` joins `GOVERNED_HOST_SUITES`, with a comment giving the reason, kept as a block of its own apart from T021's lines; - the scan also recognises a `gate` verb spelled as a command line (`_GATE_COMMAND = re.compile(r"""["']gate ([a-z][a-z-]*)""")`), the way this suite's descriptors spell it. Measured over the 16 governed suites, only `test_staging_workbench.py` matches it and not the argument-vector spelling. T021 (#40) also edits `tests/conftest.py`. Whichever of the two lands second merges `main`. ### The falsifier **The oracle, in 12.5's call as `main` now has it (`--chains`), at a simulated squash landing of this head.** The landing is one local commit on `main` f70bc9c carrying exactly this branch's tree and the `Arc:` trailer, and it is never pushed. The inputs are 12.5's own: - the governed set by its `git grep`: 16, the same 16; - the `Arc:` landings since `e28930bf`: 14, T020's, T024's, T023's and T021's among them. ``` admitted: c41063d tests/test_gate_loop_views.py, by entry 1 of tests/protected_suite_respellings.yaml admitted: 839492d tests/test_gate_loop_views.py, by entry 2 of tests/protected_suite_respellings.yaml admitted: 56e1c23 tests/test_branch_session.py, by entry 18 of tests/protected_suite_respellings.yaml admitted: 2f7003912aa7 tests/test_doxbench_share.py, by entries 31, 32, in that order, of tests/protected_suite_respellings.yaml admitted: 2f7003912aa7 tests/test_session_confinement.py, by entries 28, 29, 30, in that order, of tests/protected_suite_respellings.yaml admitted: 2f7003912aa7 tests/test_session_gates.py, by entries 20, 21, 22, in that order, of tests/protected_suite_respellings.yaml admitted: 2f7003912aa7 tests/test_session_verbs.py, by entries 23, 24, 25, 26, 27, in that order, of tests/protected_suite_respellings.yaml admitted: 2f7003912aa7 tests/test_staging_workbench.py, by entries 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, in that order, of tests/protected_suite_respellings.yaml ok: 8 protected edit(s), each entered and holding ``` rc=0. In the pre-batch-Q form, which passes no `--chains`, a landing of this branch reads `FAIL: the arc edited protected suites outside the reviewed allow-list: tests/test_doxbench_share.py, …` with rc=1. That is why batch Q was ruled. **The oracle on mutants of the real list** (`--chains`), each refused: ``` A a chain step's before_blob broken: rc=2 FAIL: tests/protected_suite_respellings.yaml breaks its own rules, so nothing is subtracted: entry 44: its before_blob is not the after_blob of the entry before it for tests/test_staging_workbench.py, so the two do not chain B the last staging step dropped: rc=1 refused: f7ebecbcc37e tests/test_staging_workbench.py: entries 33-50: tests/test_staging_workbench.py at the landing is 66d16b7, not the chain's 54852fa5250b2c4d0630088154aa4bac6517bffe; entries 3… FAIL: the arc edited protected suites outside the reviewed allow-list: tests/test_staging_workbench.py C a span entry names another span: rc=1 refused: f7ebecbcc37e tests/test_staging_workbench.py: entries 33-51: step 10: the entry's old text is not inside _run_session before the landing; entries 34-51: tests/test_staging_workbench.py before the landing is 655afc8… FAIL: the arc edited protected suites outside the reviewed allow-list: tests/test_staging_workbench.py D a span entry names a test instead: rc=1 refused: f7ebecbcc37e tests/test_staging_workbench.py: entries 33-51: step 10: the entry's old text is not inside test_toggle_keyword_is_order_preserving_and_idempotent before the landing; entries 34-51: tests/test_staging_workbe… FAIL: the arc edited protected suites outside the reviewed allow-list: tests/test_staging_workbench.py E a respelling entry names a span: rc=2 FAIL: tests/protected_suite_respellings.yaml breaks its own rules, so nothing is subtracted: entry 43: names a span, and only an admitted entry may (R-1 (a)); a respelling entry names its test ``` **R-1 (a)'s span guards: 22 mutants** of `scripts/protected_suites.py`, each run against the committed `tests/test_protected_suite_check.py`. 21 are killed. M4 survives because it is equivalent: a nested binder also lies inside its compound statement's lines, so the shared-line guard refuses it too. M22, which removes both guards, is killed. ``` M1 a respelling may name a span: killed (first: test_a_span_entry_that_breaks_the_rules_subtracts_nothing[respelling]) M2 a span may be spelled as a test: killed (first: test_a_span_entry_that_breaks_the_rules_subtracts_nothing[a-test]) M3 a name bound twice may be a span: killed (first: test_what_is_never_a_span_is_refused[shadowed]) M4 a block-only binding may be a span: SURVIVED M5 a del is not a binding: killed (first: test_a_second_binding_in_any_form_or_scope_is_never_a_span[del]) M6 an except alias is not a binding: killed (first: test_a_second_binding_in_any_form_or_scope_is_never_a_span[except-alias]) M7 a match capture is not a binding: killed (first: test_a_second_binding_in_any_form_or_scope_is_never_a_span[match-capture]) M8 a star import is not a binding: killed (first: test_a_second_binding_in_any_form_or_scope_is_never_a_span[star-import]) M9 a parameter is not a binding: killed (first: test_a_second_binding_in_any_form_or_scope_is_never_a_span[parameter]) M10 a class may be a span: killed (first: test_what_is_never_a_span_is_refused[class]) M11 a helper function is never a span: killed (first: test_an_edit_inside_a_named_helper_function_is_admitted) M12 two targets may be a span: killed (first: test_what_is_never_a_span_is_refused[two-targets]) M13 an annotation alone may be a span: killed (first: test_what_is_never_a_span_is_refused[annotation-only]) M14 a helper's decorators lie outside it: killed (first: test_a_helpers_decorator_is_inside_its_span) M15 a shared line is allowed: killed (first: test_a_span_that_shares_a_line_with_another_statement_is_refused) M16 old outside the span is not checked: killed (first: test_an_old_text_outside_the_span_is_refused) M17 new outside the span is not checked: killed (first: test_a_new_text_that_leaves_the_span_is_refused) M18 an added span is not refused: killed (first: test_what_is_never_a_span_is_refused[class]) M19 the span's first line is outside it: killed (first: test_an_edit_spanning_a_whole_named_constant_is_admitted) M20 a span entry keeps the test key: killed (first: test_this_repositorys_allow_list_keeps_its_rules) M21 span entries take the test path: killed (first: test_an_edit_spanning_a_whole_named_constant_is_admitted) M22 M4 and M15 together: killed (first: test_what_is_never_a_span_is_refused[block-only]) M0 unmutated: 87 passed in 9.05s ``` **The served display, measured in the test process** (Brett's (2)+(3): "INFERRED … MEASURE it"). This was an uncommitted probe test, run composed at 3eead0fe (242889b on the integration of that time; the helpers' statement is unchanged since): - with no host registered, the helpers' statement refuses `ProfileNotRegistered`; - under the governed host (`OpenxFactoryProfile`), it reads `host_facet: declared` and `host_profile: openxfactory-engineering`, with areas `captured: ideation/brainstorm/` and `organized: ideation/staging/`; - that result equals the `display` of a live `GET /capabilities` from `serve.build_server()` under the same registration: `EQUAL: True`, 3163 bytes of sorted JSON. **The nodes, composed**, in R1Q23 (a)'s form: - openxFactory `main` 92010d3e, with its submodules initialized recursively and its `scripts/` on `PYTHONPATH`; - openDox-code#91 45f5c55d installed (W-1 (A)); - T022's runbook placement linked (its script, #41), the colour variables unset and `TERM=dumb`; - each suite run alone; - openXdox-code `main` f70bc9c (T020, T021, T023 and T024 landed) as the base, and this head merged onto it on a local branch that is never pushed (0478b5f1). | run | 16 governed suites | `test_host_plane.py` | `test_protected_suite_check.py` | |---|---|---|---| | without this PR (`main` f70bc9c) | 842 nodes: **800 passed, 42 red** (staging 29, verbs 5, confinement 3, gates 3, share 2) | 9 passed | 54 passed | | with this head 9c29855 (0478b5f1) | 842 nodes: **842 passed** | 9 passed | 87 passed | | earlier, this PR WITHOUT the edit to T020's files (at 856047a, on `main` 56e1c23 with #41 and #42 merged) | `test_staging_workbench.py`: 37 failed, 46 passed, every failure `ProfileNotRegistered` | 9 passed | n/a | 42 nodes go red to green, none goes green to red, and none is still red. ### CI at the head 9c29855 - **Checks**: `validate` success (run 37517336710), SonarCloud success, `copilot-pull-request-reviewer` success. - **CI's own pytest step, run alone** (CI's install, openDox at its pin dede32b4): `selected=1146 passed=1142 skipped=4 failures=0 errors=0`, against the floors `MIN_SELECTED 1099` and `MIN_PASSED 1095` and `EXPECT_SKIPPED 4`. The holder ruled (lane log, 13:57Z) that T029 raises the floors once, and T020 to T026 leave them. - **Copilot**: reviewed at 9c29855, "Needs a closer look" (a governance-sensitive validator and a large chained allow-list), **Findings: None**. 9 threads over its reviews, each answered with its fixing commit and resolved; 0 unresolved. ### Files changed `scripts/protected_suites.py`, `tests/protected_suite_respellings.yaml`, `tests/test_session_gates.py`, `tests/test_session_verbs.py`, `tests/test_session_confinement.py`, `tests/test_doxbench_share.py`, `tests/test_staging_workbench.py` (T026's seven), plus three files the ruling at comment 6016648451 adds to T026's Files: - `tests/test_protected_suite_check.py`, the holder's answer to point 4; - T020's `tests/conftest.py` and `tests/test_host_plane.py`, Brett's points 2 and 3, edited once #39 landed. ### What remains, and its trigger - **The holder's word on the three flagged respellings.** Nothing else in this PR waits. - If `main` moves before landing, the lander merges it, never by rebase. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Arc: neutral-product-standalone-operability Lane: openxfactory-4 (openXfactory-4-openDox_extraction) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings T024 (#38), T023 (#42), T021 (#40), T026 (#43) and T095 (#46). In tests/conftest.py, T095's composed harness and T073's rail block were both appended after T021's lines. Both are kept, T095's as main has it and T073's after it, so this branch's diff against main is a pure append there. Lane: openXfactory-3 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>



Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Authored by lane openXfactory-3, delegated by lane openxfactory-4 (#656 6013609696)
Arc: neutral-product-standalone-operability
T024, U-5: the H-1 shim (plan 038, feature
038-opendox-document-tool-self-maintenance)After set met: T004 (opensoft/openxFactory#1245) landed as 260234b5 at 2026-10-06T14:31Z.
Base brought up to date with main (T020 #39 c6d15b2) by a merge commit; CI green at 65c3910; the composed falsifier was measured at 873792a, before the merge, and was not re-run (the merge brought only T020's host registration).
A new
scripts/ideation_dashboard/session_git.py, with NO__init__.pybeside it:(the task's three statements; the file carries a comment block above them saying why).
Contract, from
tasks.mdtest_dependency_direction.pygreen (it does not scanscripts/).6013547504).scripts/ideation_dashboard/session_git.py. One file, nothing else.Why
The carve moved
ideation_dashboard/session_git.pyverbatim to openDox-code asopendox.session_git.tests/test_session_transaction.py:296still spells the old path, in the module-level stringLOCK_HOLDERthat a rival process runs (from ideation_dashboard import session_git as sg). A protected suite's module-level text lies inside no test, so no batch C entry can hold an edit to it; the shim restores the spelling instead, and edits no protected file. The trace is lane 3's R2-INV-P4F part, Group L.With no
__init__.py,scripts/ideation_dashboard/stays a portion of theideation_dashboardnamespace package, so in the composed run it merges with openxFactory's ownscripts/ideation_dashboard/(which has none either) and does not shadow it. That follows from Python's namespace-package rule (ADV-39).sys.modules[__name__] = _mmakesideation_dashboard.session_gitandopendox.session_gitthe same module object.The falsifier
Base measured: openXdox-code
56e1c238(origin/main), openDox-codea9ac96f9(force-reinstalled with--no-depsover openXdox's pin, as 12.5's falsifier does), openxFactory92010d3ewithopenDox,openXdoxandopenXwalletinitialized recursively and itsscripts/onPYTHONPATH(R1Q23 (a)'s form). Python 3.12.3, pytest 8.4.2, the clone's CI install command (python -m pip install -e ".[test]"). Head:873792a9.The four nodes, all in
tests/test_session_transaction.py:RED at main (
56e1c238), composed:Their cause is
assert '' == 'held'(the rival child dies onImportError: cannot import name 'session_git' from 'ideation_dashboard'; the first node'sfinallythen raisesBrokenPipeError).GREEN at head (
873792a9), composed:Three of the four close. The fourth,
test_a_lock_contention_through_the_CLI_is_the_same_refusal, passes its first cause (LOCK_HOLDER) and stops at its second, attests/test_session_transaction.py:1148:That is the missing
gateregistration:tests/test_session_transaction.pyis not inHOST_PLANE_SUITES, so no host carrying openXdox'scli_gate.GateSubcommandsis registered for it.tests/conftest.pyis T020's file and is NOT touched here. The fourth node closes when T020 lands. To show that, a throwaway pytest plugin kept outside the clone registered a host carryingGateSubcommandsfor that one suite only (the pattern attests/test_hermeticity_gate_verbs.py:48-52), and the same four nodes at the same head read:The composed whole, 66 files. Each of the 66 files in
tests/declared_exclusion.yaml, run alone and composed, at main and at head: 2571 cases at both, 720 red at main and 717 at head. The set that moved is exactly the three nodes above (red at main, green at head); no node is newly red.test_dependency_direction.py: 17 passed in the CI form (the lone run). Composed it reads 16 passed, 1 failed, and the one istest_first_attribute_access_refuses_and_names_the_layering. It fails the same way at main (56e1c238), composed, so the shim does not cause it: that node asserts a refusal whenideation_dashboardcannot be imported, and composed the module is importable. It is a property of the composed form, noted here for the T029 composed workflow.Mutants
sys.modules[__name__] = _mline: all four nodes go red again (4 failed, with the throwaway registration in place, so the registration is not what is being tested). The shim body then imports but exposes noSessionGit, and the rival child dies.__init__.pybeside the shim (a regular package):ideation_dashboardresolves to one portion, andimport ideation_dashboard.serve_openxfactory_lanesraisesModuleNotFoundError. As shipped, it has two portions and the openxFactory lane module resolves. That is the shadowing the missing__init__.pyprevents.The protected-suite oracle
scripts/protected_suites.py, called with the head commit as the landing and everytests/test_*.pyas the suites, exits 0:ok: 0 protected edit(s), each entered and holding. The branch touches no path undertests/, and no__init__.pyis added anywhere underscripts/.The full suite, as CI runs it
python -m pytest -q --junitxml=pytest-report.xml --deselect "$LEFT_OUT"afterpython -m pip install -e ".[test]":1095 passed, 4 skipped, 1 deselected in 83.41s. The "Pin the triple" step's own arithmetic over that report:selected=1099 passed=1095 skipped=4 failures=0 errors=0, against the pinsselected>=1099 passed>=1095 skipped==4. The shim adds no test, so the triple does not move.CI, Copilot, threads (at head
873792a9)validatepass (1m30s; the full suite, with the "Pin the triple" step);SonarCloud Code Analysispass (Quality Gate passed, 0 new issues, 0 security hotspots).Findings: None. Its summary line reads "Needs a closer look: the draft explicitly depends on pending cross-repository sequencing and composed validation", which is this PR's own draft note and its After set (T004), not a finding.65c3910f(the merge ofmainc6d15b27, brought in by GitHub's update-branch as a merge commit):validate,SonarCloud Code Analysis,Sourcery reviewandcopilot-pull-request-reviewerall completed success (4 of 4); Copilot review 5431537774, state COMMENTED, "Approval recommended",Findings: None; 0 review threads, 0 unresolved. Against873792a9the merge changed onlytests/conftest.pyandtests/test_host_plane.py, T020's two files.What is NOT done
tests/conftest.pyand thegateregistration: T020's. The fourth node closes there.ideation_dashboard.*spelling in openXdox-code'sscripts/.tests/test_dependency_direction.pydoes not scanscripts/, so nothing refuses it; this is the reintroduction CF-3 confirms. Hosting the shim in openxFactory instead would contradict the carve manifest'smoved_verbatimrow.Summary by Sourcery
Add a namespace-safe compatibility shim that maps the legacy
ideation_dashboard.session_gitimport toopendox.session_git.Bug Fixes:
ideation_dashboard.session_gitpath.Enhancements:
opendox.session_git.🤖 Generated with Claude Code