Repository navigation
T023, U-4: swb-session.js spells the model's two names at its call sites again (plan 038) - #42
Conversation
…tes again (plan 038) submitSession had bound `headersOf` and `repairWith` from the model namespace (Copilot review, round 3 of #18), which respelled the two calls that tests/test_session_confinement.py pins in both write transports. Two nodes there went red in the composed run: test_the_renderer_presents_the_console_token_from_the_capability_probe test_the_renderer_repairs_a_stale_token_by_re_reading_it_never_by_reloading The fix shadows consoleHeaders and withConsoleRepair inside submitSession with the bound namespace, or with MODULE_TRANSPORT (the module-level pair, each reaching the current install at call time) when there is none. The calls read as the pinned spellings again and the bound behaviour is unchanged. No protected suite is edited. Lane: openXfactory-3 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewer's guide (collapsed on small PRs)Reviewer's GuideThe PR refactors Sequence diagram for submitSession transport selectionsequenceDiagram
participant Caller
participant submitSession
participant BoundTransport
participant ModuleTransport
participant consoleHeaders
participant withConsoleRepair
participant Fetcher
Caller->>submitSession: submitSession(affordance, body, fetcher, caps, repair, bound)
alt bound transport provided
submitSession->>BoundTransport: consoleHeaders(caps)
BoundTransport-->>submitSession: headers
submitSession->>BoundTransport: withConsoleRepair(send, repair)
else no bound transport
submitSession->>ModuleTransport: consoleHeaders(caps)
ModuleTransport->>consoleHeaders: consoleHeaders(caps)
consoleHeaders-->>ModuleTransport: headers
ModuleTransport-->>submitSession: headers
submitSession->>ModuleTransport: withConsoleRepair(send, repair)
ModuleTransport->>withConsoleRepair: withConsoleRepair(send, repair)
end
submitSession->>Fetcher: POST sessionRoute(affordance), headers, body
Fetcher-->>submitSession: response
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
🟡 Changes recommended
The new dispatch paths lack committed behavioral tests that distinguish bound and fallback transports.
Review effort: Balanced
Findings: 1
What changed in this PR
Restores pinned model call-site names in the session transport while preserving bound and module-level dispatch behavior.
Changes:
- Adds a late-bound module transport namespace.
- Destructures transport functions under their expected names.
| File | Description |
|---|---|
src/openxdox/web/views/swb-session.js |
Restores pinned transport call-site spellings. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…nsport (Copilot's finding on #42, holder-ruled fix-now) One new file, tests/test_swb_session_transport.py, two node probes against the composed bundle, asserting on the request that reaches the injected fetcher: - BOUND: two firstEditTransport transports built from two models that differ in consoleHeaders and withConsoleRepair, both built before either is called; each request must carry its own model's header and go through its own model's repair policy. - UNBOUND: a mounted session form, submit pressed; the request must carry the mounted model's header and go through its repair policy. The pinned call spellings (tests/test_session_confinement.py) read the file as text, so they cannot tell the right model from the wrong one. With these two, an always-module-pair submitSession (M3) and a module pair wired to the wrong function (M4, M6) go red; the unfixed main passes both, so they describe the behaviour the respelling preserves. No protected suite is edited, and the file is outside the declared exclusion. Lane: openXfactory-3 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 23 hours and 39 minutes by commenting |
|
READY at e8a067f Lane: openxfactory-4 (openXfactory-4-openDox_extraction) Holder, at READY (T023, U-4:
|
Lane: openXfactory-3 Co-Authored-By: Claude Opus 5.5 <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>
…e identity response shape and the single-writer chains (plan 038) (#1257) Lane: openxfactory-4 (openXfactory-4-openDox_extraction) **T002, second bookkeeping batch, plan 038: T094 and the slice entries T095 to T104, the identity response shape, the single-writer chains, and the ticks.** Bookkeeping, so this PR carries no `Arc:` line. It touches no `openspec/changes/` path, so no Rule 6 window applies. It began stacked on #1251's head; #1251 has landed (`1837ea75`) and `main` is merged in (`543f6ce1`), so the diff against `main` is this batch alone. ## From plan 038's `tasks.md`, T002 - **Task**: T002, "Release-2 base, and the re-measure at the ruling" (landed in #1251; this is its second batch of bookkeeping, the brief of the holder's `T002b`). - **Realizes**: none (bookkeeping; FR-025). **Falsifier**: T002 names none. Its gates are the brief's, quoted under "Review and gates". - **Ruled / Decisions**: none of its own. Every item is a ruling already posted on `#656`, or a landing already merged, encoded as worded and cited by comment id or merge commit. ## The items | # | Where | What changed | Record | |---|---|---|---| | 1 | tasks.md: T094, T095 to T104, T073 | T094 ticked, with its result quoted: 436 composed reds over 51 files, outside 12.5's set (Base 2; 533 at Base 1). The `T095+` placeholder becomes ten entries, T095 to T104, each with its slice name, Files, After and Falsifier from R2-INV-R9's outline and the holder's rulings, `Lane: 3 (openXfactory-3)`. T073's After and Files name them. The map's means, the rulings (R9-R1 (a), R9-R2 (a), W1 to W7) and the OQ defaults are recorded once, above the entries | map `R2-INV-R9.md`; `6021830607`; claim `6021862323`; rulings `6021830531` | | 2 | T095, T101, T104, T097 | T095: the two confirmed departures from OQ-R9-1 (`plane_host_for` untouched, an autouse fixture registers the declared host; `DECLARED_HOST_SUITES` guarded structurally). T101: `tests/declared_exclusion.yaml` for ONE entry (reasons `[doc_health]`, note dropped, the `only` list loses the file, count stays 66). T104: `tests/test_binding_stylesheets.py`, the pin 572 to 574. T097: its served-display respelling is R9-R1 (a) applied | `6022291206`; the T097 line is `6023517122` (the holder confirmed the citation, `6026622117`) | | 3 | plan.md: the single-writer table; tasks.md Files and After lines | W7's chains: `tests/conftest.py` T020, then T021 and T026 in landing order, then T095, T073, T074; `tests/test_host_plane.py` T020, T026, T095; `declared_exclusion.yaml` T101, T073, T074; the four T074 test files T103 then T074; the census T025, T104, T015, T016, T057 (the row splits, so `test_web_boundary.py` keeps its own chain). T015's and T074's After gain T104 and T103 | `6021830531`, `6022291206` | | 4 | tasks.md: T072, T074, T075 falsifiers | The four folds of #1253, in the arc change's own wording: T072 asserts that ONLY CR, LF and CRLF split rows; T074's seam tests assert that an empty-seam read names the seam AND its registrar; T075's host-wiring test shows a leg declaring only SOME of the four seams refused, and a leg declaring the six and none of the four accepted; T074's protected-suite oracle computes 12.5's set as 12.5 does (`git grep -l -e 'open-pr' -e 'open_pr' -e 'FakePullRequests'`), from the pre-arc and the final tree, the floor of 16 on each, and takes the union | openxFactory#1253 to `fc4fa0ff`; Copilot `5427373153` | | 5 | spec.md FR-011; tasks.md T041 | FR-011's JSON shape gains `identity`, emitted and bounded as openDox-spec's finding schema bounds it (no `excerpt`, `text`, `content` or `quote` key, no number, strings of at most 200 characters, `{category, entry}` for a pathless finding, `{collided_id}` for a collision). T041's falsifier gains a response-shape test asserting exactly that bound. The coverage row for FR-011 names T041. The serialized-size cap was already T041's at the base (T041's text says so, and T042 "stores what T041 bounds"), so nothing moved | holder `6018624750` | | 6 | tasks.md ticks | T011, T014, T021, T023, T025, T026 and T060 ticked, T070's tick gains #1253, and T002's own landing is recorded. Each merge commit was verified with `gh pr view <n> -R <repo> --json mergeCommit` (listed below) | the landings | | 7 | tasks.md: T072 | A Files line (`src/opendox/lines.py`, `tests/test_lines.py`); the Ruled line gains `6023517122`: `head_sha` (a), its own public `head_sha` parity-tested against `serve._head_of`, `serve.py` untouched; the line split (a), `doxbench_defaults.py` untouched. Folding either private helper is recorded as unscheduled and optional, and is not scheduled | holder `6023517122` | | 8 | contracts/health-finding.md:91 | The sentence reads: a stored `identity` makes a cross-run collision between STORED findings detectable; an exception-only finding is never stored; whether the engine reports it remains T046's concern. Nothing else on the line changed | holder `6018624750`; Copilot `5433292692` | | 9 | plan.md; tasks.md | Copilot's three items at #1251's head `1eb8e9eb`, accepted by the holder at its landing: (a) the pin and landing-order row for T073 names T005 and T095 to T104 as prerequisites, and `T095+` reads T095 to T104 at the critical path, the lane table and the revision note; (b) the combined `conftest.py` and `test_host_plane.py` row splits, and the cross-lane summary gains T026 (and the other chains of item 3); (c) the same split in T020's, T021's and T026's Files lines | holder replies `4201056969`, `4201057311`, `4201057577`; READY note `6026651962` | ### The ticks, verified | task | pull request | merge commit | merged | |---|---|---|---| | T011 | opensoft/openDox-code#92 | `09b8d44a` | 2026-10-06T19:15:55Z | | T014 | opensoft/openDox-code#93 | `a7a1c4d3` | 2026-10-06T22:23:06Z | | T021 | opensoft/openXdox-code#40 | `f70bc9c3` | 2026-10-06T19:07:10Z | | T023 | opensoft/openXdox-code#42 | `d45a0939` | 2026-10-06T17:45:33Z | | T025 | opensoft/openDox-code#91 | `84f8ed83` | 2026-10-06T17:33:31Z | | T026 | opensoft/openXdox-code#43 | `36bbef79` | 2026-10-06T22:35:40Z | | T060 | opensoft/openDox#20, the tag `dox-v1.2` (tag object `58538ff3`, on `6a9f4902`), opensoft/openDox#21 | `6a9f4902`, `c7f75845` | 17:46:59Z, tag 17:58:45Z, 19:02:39Z | | T070 | openxFactory#1253 (T070b, added to its tick) | `fc4fa0ff` | 2026-10-06T18:56:42Z | | T002 | openxFactory#1251 (its own landing) | `1837ea75` | 2026-10-06T22:31:42Z | - **T021** landed with Copilot r4199337845 an ACCEPTED LIMIT (holder READY note `6023492215`); T021's entry records it. - **T025's** entry says its 34 nodes are "quoted at T029"; that stays T029's, and the entry says so. - **T011's** server node is T014's, as its own Falsifier says. **Skipped, because already present at the base:** T020's and T024's ticks (#1251 item 13) and T004's tick (#1245, `260234b5`), all confirmed with `git show <commit>:<tasks.md> | grep`. **T071 is NOT ticked.** Brett's word, "Ratify it (Recommended)" (`#656` `6023375303`), is recorded on T071's entry, but the change's ratification-record PR has not landed (no such PR is open or merged on `opensoft/openxFactory` at this writing). The box stays open. ## Readings the holder accepted (`6026622117`), disclosed as the brief requires - **W7 omits T026 from the openXdox-code chains.** The single-writer table keeps T026 where its own ruling (`6016648451`) puts it, and lane 3's claim (`6021862323`) says T026's PR lands before T095. So the conftest chain reads T020, then T021 and T026 in landing order, then T095, T073, T074; and `test_host_plane.py` reads T020, T026, T095, as W7 gives it with T026 added. - **R9-R2's last sentence** ("If the re-scoped test runs alone, it leaves the declaration, and the count moves with it, as the declaration's own rules require") is recorded in T100 as worded. `declared_exclusion.yaml` is NOT added to T100's Files: the holder's file list at `6022291206` names it for T101 only. - **Edits outside tasks.md**, ruled in scope as each item names its place: plan.md (items 3, 9), spec.md FR-011 (item 5) and `contracts/health-finding.md:91` (item 8). ## Other notes on where an item needed a reading - **Counting.** The task count reads 90 rows: the placeholder `T095+` is replaced by T095 to T104 (81 - 1 + 10). - **Plan 034's task numbers.** The entries cite plan 034's T103 (#80, in T099's `Host` header) and plan 034's T086 (W2, in the rulings) as "plan 034's", because plan 038 now has its own T103. - **OQ-R9-3.** The map did not measure the respelled gate-console and wheel-model skips; T097 and T103 say so, and the map's 127 passed and 5 skipped is quoted as the measurement. - **Counts of slices** are copied in the map's own form (for example "16 nodes, and +5 on R9-R1 (a)"), not summed. - **T095's chain after T026.** T095's After names T021 and T026, both landed. - **Not mirrored from the arc's 4.1:** T075's falsifier gains the some-of-four refusal and the six-and-none acceptance, which #1253 folded. The arc's two older refusals (part-way through the seams; at the column call) are not in T075, since #1253 did not add them. - **T046 and T057** carry no response-shape falsifier for `identity`: item 5 names T041's. ## Review and gates - **`python3 scripts/validate-openspec-cli-pin.py --all`**, in the clone named `openxFactory`, at `691648c6`: exit 0, `Totals: 113 passed, 1 failed (114 items)`, `0 UNDISPOSITIONED failures`. The one failure is the dispositioned `add-chain-attestation` finding, the same on `main`. - **openxFactory pytest.** The full local run was stopped at about 44% at a coordinator drain, so it is NOT a result and is not claimed. The repository's own gate, `pytest-suite` in CI at `691648c6`, completed `SUCCESS` (2026-10-06T23:32:38Z to 23:58:56Z). - **Doc-health**, `scripts/doc-health.py --single-repo . --as-of 2026-10-07`, branch `691648c6` against `origin/main` `0992369a` in the same clone: the two reports are byte-identical (sha256 `ce17aadd6d96e484170e2ec4784e44972e33c7d1a6c65830ea14a1035f2ae976`; `Findings: 31 critical, 26 error, 69 warning, 20 info. New regressions vs previous report: 0.`). So no new error. - **CI at `691648c6`**: all 14 check runs are `SUCCESS` (rollup `SUCCESS`): `pytest-suite`, `merge-master-approval` (the red inherited from `main` since #1252 was cleared by #1138), `openspec-cli-pin`, `doc-health-py314`, `openxdox-consumer-gate`, `wallet-validation`, `clearing-dispatch-gate`, `signed-execution-chain-gate`, `former-id-arrival-gate`, `release-tag-gate`, `openreposhape-pin`, the two `lane-line` runs and SonarCloud. - **`main`** has moved to `0992369a` since the last merge (`543f6ce1`). GitHub reports this PR `MERGEABLE`, and the ruleset is not strict, so `main` is not merged in again (a merge would restart the 27-minute gate). | Copilot at | verdict | findings | answered | |---|---|---|---| | `691648c6` (final; review `5435686665`) | Changes recommended | 1 (thread `4201443690`): T041's falsifier cannot verify what T046's `health list --json` and T057's HTTP route emit; the review summary adds that the new T095 to T104 collide with plan 034's task numbers cited unqualified at `spec.md:662`, `spec.md:1222` and `clarify-questions.md:437` | accepted limit (#656 `5988818366`): plan-record accuracy, not a bypass, and the T041 placement is the holder's own instruction (item 5). Answered (`4201487553`) and resolved | 1 thread, 0 unresolved. The holder ruled both points YES at `6027706377` and has them encoded in a THIRD batch, not here: the `identity` falsifier split across T041, T046 and T057, and the plan 034 references qualified. This PR therefore carries T041's wording as the brief gave it. ## Files changed - `specs/038-opendox-document-tool-self-maintenance/tasks.md`, `plan.md`, `spec.md` - `specs/038-opendox-document-tool-self-maintenance/contracts/health-finding.md` ## What is NOT done - T071 stays open (its ratification record has not landed). No box of #1144 is ticked, and no task other than those listed is ticked. - No scenario or spec delta beyond FR-011's JSON-shape bullet is touched, and nothing is under `openspec/changes/`. `README.md` is untouched. - The T095 to T104 entries record the slices; none of their work is done here. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Lane: openxfactory-4 (openXfactory-4-openDox_extraction) 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 6013609326)
Arc: neutral-product-standalone-operability
T023, U-4:
swb-session.jsspells the model's two names at its call sites again (plan 038, feature038-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.mdsrc/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'stasks.mdentry: 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 16HOST_PLANE_SUITES.What and why
submitSessiontook its two model calls from locals,const headersOf = bound ? bound.consoleHeaders : consoleHeaders;andconst repairWith = ..., called asheadersOf(caps)andrepairWith(send, repair). That is thebound-namespace refinement of Copilot review round 3, which arrived with the S5 carve in #18.tests/test_session_confinement.pypins both write transports (swb-session.jsandswb-create.js) to the literal spellingsheaders: consoleHeaders(caps)andwithConsoleRepair(send, repair), so two nodes went red in the composed run. The pre-shed openxFactory copy spelled both exactly;swb-create.jsstill does. The trace is lane 3's R2-INV-P4F part, Group J.The fix shadows the two module-level names inside
submitSessioninstead of respelling the call sites:With a
boundnamespace the calls go to it, as before. With none,MODULE_TRANSPORTreaches 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.pyThe 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:
firstEditTransports over two models that differ in exactlyconsoleHeadersandwithConsoleRepair, both built before either is called; each request must carry its own model's header (from its owncaps) and go through its own model's repair policy.consoleHeadersbuilt and go through itswithConsoleRepair. (The outcome panel the column writes after the request needs the shell'spage-overlayhost, 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.pydoes.The falsifier
Base measured: openXdox-code
56e1c238(origin/main), openDox-codea9ac96f9(plain install,--no-deps, over openXdox's pin, as R2-INV-P4F did), openxFactory92010d3ewithopenDox,openXdoxandopenXwalletinitialized recursively and itsscripts/onPYTHONPATH(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 isf19e05c; the test file isc3c6a47). Run withFORCE_COLOR,CLICOLORandCOLORTERMunset andTERM=dumb.RED at main (
56e1c238), composed:(each:
AssertionError: swb-session.js, from'headers: consoleHeaders(caps)' in bodyat:290and'withConsoleRepair(send, repair)' in bodyat:383)GREEN at head
c3c6a47, same environment:What else reads
swb-session.js, and did not moveEvery test file in the tree (
tests/test_*.pyandtests/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.pyfails to collect in both, for want ofscripts/proposal-support.py):56e1c238c3c6a47Compared 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.py0 / 0,test_renderer.py25 / 25,test_doxbench_save.py3 / 3,test_session_confinement.py20 / 18 (the two above),test_staging_workbench.py63 / 63,test_doxchat_model_intake.py10 / 10,test_doxbench_transport.py6 / 6,test_assembled_bundle.py4 / 4,test_doxbench_scope.py1 / 1,test_gate_loop_views.py0 / 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, showedResource temporarily unavailableandcan't start new threaderrors 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.pyandtest_renderer.py, compared with the head's own red set):headers:call as(bound ? bound.consoleHeaders : MODULE_TRANSPORT.consoleHeaders)(caps)(behaviour kept)(bound ? bound.withConsoleRepair : MODULE_TRANSPORT.withConsoleRepair)(send, repair)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):
MODULE_TRANSPORT.consoleHeaderscallswithConsoleRepairtest_js_a_mounted_form_sends_through_the_mounted_models_header_and_repair(1 failed, 1 passed)MODULE_TRANSPORT.withConsoleRepaircallsconsoleHeaderstest_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
python -m pytest -q --deselect "$LEFT_OUT"from the root, the CI venv, noPYTHONPATH) atc3c6a47:1097 passed, 4 skipped, 1 deselected; the workflow's triple readsselected=1101 passed=1097 skipped=4 failures=0 errors=0, which meets its pins (floors 1099 / 1095, skipped exactly 4, 0 / 0). Atf19e05cand at main it read 1099 / 1095.python3 scripts/protected_suites.py --landings=<both commits> --suites=<the 98 tests/test_*.py at origin/main>printsok: 0 protected edit(s), each entered and holding, with and without--chains. The new file is not inHOST_PLANE_SUITES.tests/test_dependency_direction.py::test_first_attribute_access_refuses_and_names_the_layering(the T024 writer attributes it toideation_dashboardbeing importable when composed; I did not trace it); red in both the base and head runs.validatepass andSonarCloud Code Analysispass (2 of 2) atc3c6a47.f19e05c8(review 5428398958): 1 finding, that no committed test distinguishes the bound and unbound transports. Fixed byc3c6a47; thread answered and resolved. Atc3c6a470(review 5428827782, COMMENTED, body "Changes recommended"): 2 findings, both on the new file.6017860539: both Copilot findings, the "Changes recommended" label included, are ACCEPTED as they stand; the CI floors are left to T029.e8a067f3(two update-branch merges ofmain, both merge commits:3b6cd13btook T020 T020, U-1: the governed host for the governed suites that drive the gate verbs (plan 038, P4F-5) #39c6d15b27, thene8a067f3took T024 T024, U-5: the H-1 shim, ideation_dashboard.session_git answers with opendox.session_git (plan 038, CF-3) #388b64fae0): 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. Againstc3c6a470the merges changed onlytests/conftest.py,tests/test_host_plane.pyandscripts/ideation_dashboard/session_git.py, the files of T020 and T024.SonarCloud Code Analysischeck passes (quality gate); I did not list individual issues.What is NOT done
MIN_SELECTED/MIN_PASSEDin.github/workflows/validate.yml, 1099 / 1095; this PR's head selects 1101 and passes 1097).validate.ymlis 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 (T020, U-1: the governed host for the governed suites that drive the gate verbs (plan 038, P4F-5) #39, T021, U-2: the gate console's schemas, each read where its owner keeps it, nothing vendored (plan 038, P4F-4) #40, T022, U-3: the runbook placed from the composed tree, git-ignored, nothing vendored (plan 038, P4F-3) #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.Summary by Sourcery
Restore the session transport call-site contract while adding coverage for model-specific request handling.
Bug Fixes:
Enhancements:
Tests:
🤖 Generated with Claude Code