Skip to content

DRAFT: T029, U-9: the composed run, permanent: composed.yml and its openxFactory pin (plan 038) - #44

Draft
brettheap wants to merge 25 commits into
mainfrom
r2/t029-composed-workflow
Draft

brettheap wants to merge 25 commits into
mainfrom
r2/t029-composed-workflow

Conversation

@brettheap

@brettheap brettheap commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Authored by lane openXfactory-3, delegated by lane openxfactory-4 (#656 6015602584)

Arc: neutral-product-standalone-operability

T029 [US3] [oXc] U-9, the composed run, permanent (F12.1)

Plan 038 (openxFactory specs/038-opendox-document-tool-self-maintenance/tasks.md, T029), ruled by Brett Heap with the plan, every item as recommended (openxFactory#656 6013547504).

Files (this PR's own commits touch only these two)

  • .github/workflows/composed.yml (new; job composed).
  • tests/composed_host_pin.yaml (new): schema_version: 1, kind: composed-host-pin, commit: 92010d3e67f2c556687e1c3583ed3ca1867f4b8b, openxFactory main's head when authored (read with git ls-remote at 2026-10-06T11:56:20Z). It advances after T030, T064 and T075, each in an openXdox-code PR of its own.

T029's commits:

  • d6f4a814: the two files;
  • 541852d6: on a pull request, the oracle judges the PR as the landing it would be (Copilot r4195440016);
  • 82d95924: the edited trigger (Copilot r4195546545);
  • b4993d10: the oracle call takes --chains (#656 6016648451).

Both files are unchanged since b4993d10. The message of 541852d6 also credits the named empty-set refusal, but that refusal was already in d6f4a814.

Base and merges (never rebased). The branch started at #41's head 8d02a1b3, so that the workflow could call T022's scripts/composed_placements.py before T022 landed. It has since merged:

At the head 9a3b8ed1 the branch differs from main 9720925e only in T029's two files.

What the job does, in order

It runs on pull_request (types opened, synchronize, reopened and edited), on push to main (where N-7b's first green run happens), and on workflow_dispatch. Its permissions are contents: read.

  1. Checks this repository out with its whole history (the oracle reads the arc's landings since ARC_BASE e28930bf, plan 034 T003).
  2. Builds a fresh venv and runs validate.yml's install command (pip install -e ".[test]"), so openDox arrives at the pin pyproject.toml names. F9.2's pin assertion block, verbatim, holds it there.
  3. Reads tests/composed_host_pin.yaml, refusing it by name unless its keys are exactly schema_version (the integer 1), kind (composed-host-pin) and commit (40 lowercase hex), with no key given twice.
  4. Checks out opensoft/openxFactory at that commit. It initializes ONLY openDox, openXdox and openXwallet, recursively: without them corpus_adapter_openxfactory refuses at import. openxFactory's fourth submodule, installs/omnigent-install, is private (gh api repos/opensoft/Omnigent-Install --jq .private → true). The run never imports it, and this repository's token cannot read it. openxFactory, openDox(-code/-spec), openXdox(-code/-spec) and openXwallet all read false, so no secret is needed. The three .gitmodules URLs are ssh, so the run rewrites them to https in the checkout's own .git/config. It then asserts the checkout's HEAD equals the pin.
  5. Runs T022's scripts/composed_placements.py --openxfactory "$OPENXFACTORY".
  6. Computes 12.5's governed set exactly as its falsifier does (git grep -l -e 'open-pr' -e 'open_pr' -e 'FakePullRequests' -- 'tests/test_*.py') and holds it to the floor of 16. It does not type the list. Today it has 16 entries.
  7. Runs each governed suite alone, with PYTHONPATH="$OPENXFACTORY/scripts" (R1Q23 (a)'s form). Every suite runs whatever the one before it did. The step prints per-suite counts and every red node id from the JUnit reports, and fails if any suite is red.
  8. Runs the oracle in 12.5's call as batch Q amends it, with --chains (#656 6016648451): python scripts/protected_suites.py --chains --landings=… --suites=…. It refuses an empty landing list.
    • On a pull request (Copilot r4195440016), GitHub's merge commit is the landing as it would be: the oracle judges a landing by its diff against its first parent (the base) and by the blobs on either side. So that commit joins the landings wherever the body carries the exact line Arc: neutral-product-standalone-operability, which the lander copies into the landing's message.
    • The step first asserts that HEAD's second parent is the pull request's head.
    • Because the body decides, an edited body runs the job again (Copilot r4195546545).
  9. Runs tests/integration/test_assembled_surface.py::test_the_assembled_help_tree_is_the_32_entry_tree_the_manifest_records against its recorded golden (R2Q3 (a)). validate.yml leaves this test out (LEFT_OUT).

Steps 8 and 9 run even when step 7 is red, so one run reports every red.

A choice in step 9, for the holder's eye. The help-tree test builds the tree in a child process given the run's environment less PYTHONPATH (its SCRUBBED_ENVIRONMENT), and the child needs doc_health (cli_gate → gate_console:63). So a PYTHONPATH composition can never reach it. The step therefore writes composed-openxfactory-scripts.pth (naming $OPENXFACTORY/scripts) into the run's own venv, after the governed suites have run in R1Q23 (a)'s form, and nothing runs after it. The test file is unchanged. Red first, measured locally:

  • the step with that one line removed: FAILED …test_the_assembled_help_tree_is_the_32_entry_tree_the_manifest_records - AssertionError: at openDox-code@dede32b4…: the assembled command line could not be built. The child ended with: ModuleNotFoundError: No module named 'doc_health', step exit 1;
  • the step verbatim: 1 passed, step exit 0. The 32-entry golden is unchanged at the pin.

The falsifier, measured now at 9a3b8ed1 (EXPECTED red until T028 lands)

The falsifier proper (16 suites, 0 red, and the oracle's ok) is quoted once T028 has landed and been merged in.

CI at this head:

  • composed run 37808303982 (synchronize), and run 37808329039, started by Sourcery's body edit: red on the suites step ONLY. Every other step, the oracle and the help tree included, is a success.
  • validate run 37808304034: success.
  • SonarCloud: success.

CI ran on python 3.12.15, pytest 8.4.2 and node v22.23.3. Its pull-request merge commit is 31ce700f, which merges this head into main 9720925e. Quoted from run 37808303982:

openDox installed at the pin: openDox-code@dede32b4b6f3d0f147d599776f83628c5af8ff3d
the composed run's host: opensoft/openxFactory@92010d3e67f2c556687e1c3583ed3ca1867f4b8b
governed suites: 16 (the floor is 16)
tests/test_branch_session.py: 132 passed, 0 failed, 0 errors, 0 skipped
tests/test_doxbench_mutation_boundary.py: 80 passed, 0 failed, 0 errors, 0 skipped
tests/test_doxbench_share.py: 34 passed, 0 failed, 0 errors, 0 skipped
tests/test_gate_loop_views.py: 74 passed, 0 failed, 0 errors, 0 skipped
tests/test_gateway_provenance.py: 24 passed, 0 failed, 0 errors, 0 skipped
tests/test_hosted_actor.py: 5 passed, 0 failed, 0 errors, 0 skipped
tests/test_session_commits.py: 22 passed, 0 failed, 0 errors, 0 skipped
tests/test_session_confinement.py: 53 passed, 0 failed, 0 errors, 0 skipped
tests/test_session_gates.py: 81 passed, 0 failed, 0 errors, 0 skipped
tests/test_session_lifecycle.py: 53 passed, 0 failed, 0 errors, 0 skipped
tests/test_session_notebook.py: 39 passed, 0 failed, 0 errors, 0 skipped
tests/test_session_records.py: 29 passed, 0 failed, 0 errors, 0 skipped
tests/test_session_runbook.py: 11 passed, 0 failed, 0 errors, 0 skipped
tests/test_session_transaction.py: 57 passed, 0 failed, 0 errors, 0 skipped
tests/test_session_verbs.py: 65 passed, 0 failed, 0 errors, 0 skipped
tests/test_staging_workbench.py: 62 passed, 17 failed, 4 errors, 0 skipped
TOTAL over 16 suites: 821 passed, 17 failed, 4 errors, 0 skipped; 21 red, in 21 red nodes
the arc's landings since ARC_BASE: 25
the pull request's body carries the arc's trailer, so its merge commit 31ce700f9d140a1b248685813c707f64a44ec790 is judged as the landing it would be
admitted: c41063d6aaad tests/test_gate_loop_views.py, by entry 1 of tests/protected_suite_respellings.yaml
admitted: 839492d905f4 tests/test_gate_loop_views.py, by entry 2 of tests/protected_suite_respellings.yaml
admitted: 56e1c238681a tests/test_branch_session.py, by entry 18 of tests/protected_suite_respellings.yaml
admitted: 36bbef794c52 tests/test_doxbench_share.py, by entries 31, 32, in that order, of tests/protected_suite_respellings.yaml
admitted: 36bbef794c52 tests/test_session_confinement.py, by entries 28, 29, 30, in that order, of tests/protected_suite_respellings.yaml
admitted: 36bbef794c52 tests/test_session_gates.py, by entries 20, 21, 22, in that order, of tests/protected_suite_respellings.yaml
admitted: 36bbef794c52 tests/test_session_verbs.py, by entries 23, 24, 25, 26, 27, in that order, of tests/protected_suite_respellings.yaml
admitted: 36bbef794c52 tests/test_staging_workbench.py, by entries 33, 34, …, 51, in that order, of tests/protected_suite_respellings.yaml
ok: 8 protected edit(s), each entered and holding
1 passed in 0.43s        (the help tree)

The last admitted: line is shortened to 33, 34, …, 51; the run names all nineteen entries. Each run's log also shows an earlier copy of the oracle's lines inside the step's header: that is this body, echoed through PR_BODY, not output.

Local run 7 agrees. It ran the workflow's own run: blocks verbatim at 9a3b8ed1, through a harness that emulates GITHUB_ENV, GITHUB_PATH, GITHUB_OUTPUT and the if: conditions. It used full clones, LANG=C.UTF-8, TERM=dumb and CI=true, with every GIT_*, XF_* and colour variable unset; python 3.12.3, pytest 8.4.2, node v24.21.0.

  • Suites: the same per-suite counts, the same TOTAL, and the same 21 red node ids as both CI runs (the sorted lists are equal).
  • Oracle: run 7 is a workflow_dispatch at the branch head, where its oracle reads the branch's own first parents (9 landings, ok: 3). The pull-request reading was mirrored locally instead: GitHub's merge of this head into main 9720925e, run through the oracle step's own text as pull_request with the live body. It read 25 landings, judged the merge commit, and read the same eight admitted: lines and ok: 8, as CI did.

The 21 reds are T028's to clear. All 21 are in tests/test_staging_workbench.py, and all of them read openDox's bundle as installed (opendox_bundle.composed()), which is still at the pin dede32b4 (pyproject.toml).

  • 18 (14 failed, 4 errors) are the node harness's ERR_MODULE_NOT_FOUND … display.js. At dede32b4 the staging model opens with import { SCOPE_KINDS, … neutralDisplay } from "./display.js";, which the harness's standalone copy cannot resolve.
  • 3 are text asserts on the same bundle:
    • test_staging_workbench_view_has_no_write_path: POST is found inside the view's "EDITING POSTURE" comment;
    • test_the_session_surface_added_no_transport_to_the_view_or_the_model: the model's import line;
    • test_only_the_plane_postures_still_stand_as_an_inline_note: showPostureNote( occurs 5 times where 3 are expected.

All 21 are T025's W-1 (A), the import-free staging model, together with its T102 posture rewording. That work has landed in openDox-code (#91, 84f8ed83), but it reaches this repository only when T028 moves pyproject.toml's opendox pin. Read against T025's landed bundle at 84f8ed83:

  • the model is import-free;
  • the view names no POST, fetch( or XMLHttpRequest;
  • showPostureNote( occurs 3 times, the definition and the stands line both present;
  • the model holds chat: true, 3 times.

At dede32b4, three of those fail: the model imports, the view's text contains POST, and showPostureNote( occurs 5 times.

--chains is load-bearing on main today. T026 landed as ONE PR (36bbef79) whose entries chain by blob, and with --chains the oracle admits it on five suites (above). Re-run in the local mirror of this head's merge with main 9720925e, the same oracle step without --chains exits 1 with refused: 36bbef794c52 … on the same five: test_doxbench_share.py (entry 31), test_session_confinement.py (28), test_session_gates.py (20), test_session_verbs.py (23) and test_staging_workbench.py (33). With --chains it exits 0 with ok: 8.

Before T020–T026 landed (heads d6f4a814 to b4993d10), the same runs read 82 red, again equal between CI and local node for node. By owner: T023 2, T024 4, T025 34, T026 42. T023, T024 and T026 have since landed here, and T025 in openDox-code; the 82 fell to these 21.

The known pre-existing composed red is tests/test_dependency_direction.py::test_first_attribute_access_refuses_and_names_the_layering. It is outside the 16: that file names none of open-pr, open_pr or FakePullRequests, so the governed set never selects it, and this workflow does not run it.

Guards, red first, and mutants

Each guard was run on bad input through the workflow's own step text, then against a one-line code mutant that disables it. All 23 mutants are KILLED: 17 on the guards (31 cases) and 6 on the pull-request path (8 cases). The workflow text has been unchanged since b4993d10, where these last ran.

  • The pin reader (13 cases): kind wrong; schema_version 2, '1' and true; commit short, uppercase, or carrying a newline (an output-injection attempt); an extra key; commit missing; commit given twice; a list; not YAML. Each is refused by name, and the real file passes. Killed: the kind check; the schema_version check; its type check; the commit-shape check; fullmatch→search; exact key set → subset; the duplicate-key loader; the mapping check.

  • HEAD == pin: another commit is refused. Killed: the check's exit removed.

  • The governed floor: 15 and 0 suites are refused by name. Killed: floor 16→15, and the floor's exit removed.

  • A red suite fails the step, in five shapes: a red suite before or after a green one, a collection error, and pytest dying with no report. Killed: the red flag never set; exit 0; the rc not captured.

  • The oracle's non-empty landings: ARC_BASE=HEAD is refused. Killed: the exit removed.

  • F9.2's pin assertion: another full commit, a short pin and two opendox pins are each refused. Killed: each of its two asserts removed.

  • The pull request judged as its landing (8 cases on simulated GitHub merge commits):

    • a weakening edit with no entry is refused;
    • the same edit entered is admitted;
    • two edits to one suite in two blob-chained entries are admitted;
    • a body without the line, an indented line, a push, and a PR touching no governed suite all pass;
    • a HEAD that is not the merge commit is refused.

    Killed: the merge commit never added; every body counted; substring instead of line; the HEAD check's exit removed; the pull_request branch never taken; --chains dropped.

actionlint 1.7.12, with shellcheck, reports nothing on the workflow.

Copilot

Not done here, and whose it is

  • The falsifier proper (0 red, oracle ok) waits for T028, the last of the After set. main is merged in once T028 has landed, and the quote follows. Until then composed is red on the suites step only, on T028's 21.
  • validate.yml's MIN_SELECTED and MIN_PASSED floors. Per the holder's ruling (#656 6017860539), T029's Files gain those two lines only, and T029 raises them ONCE. That happens at DRAFT: T029, U-9: the composed run, permanent: composed.yml and its openxFactory pin (plan 038) #44's final head, after main is merged in, and only once the After set has landed and been merged in. The counts are measured at that head, and the CI-form run is quoted. Trigger: the coordinator's message once T028 has landed (T022 already has). Nothing in validate.yml changes before then.
  • Making composed a REQUIRED check is the holder's act after the first green run on main (N-7b (a)), recorded in T029's evidence. This PR changes no repository setting, secret or ruleset.
  • Pin advances (T030, T064, T075) and T073's composed declarations are separate PRs.
  • Inferred, not run: the two created files may need created: admissions in openxFactory's docs/opendox-carve-admissions.yaml at the pin bump (T030).
  • A note for the holder: each Sourcery body edit also runs composed, through edited.

🤖 Generated with Claude Code

brettheap and others added 7 commits October 6, 2026 10:39
…ate verbs (plan 038)

tests/conftest.py registers THE GOVERNED HOST for the length of each test
of the eight governed suites that drive openXdox's gate verbs
(GOVERNED_HOST_SUITES): openxFactory's composite, opendox_host.profile(),
where the run is composed (R1Q23 (a)), and GovernedStandInHost, which
carries openXdox's three governed columns, elsewhere. The composite is
registered with openDox's registry alone, so no openxFactory seam outlives
the test. Its facets are read with sys.path saved and restored, because
importing openxFactory's lane column runs carved_reach.install(), which
would otherwise put openxFactory's pinned openXdox leg ahead of this
checkout's namespace package. An openxdox module from anywhere else
refuses the case by name.

tests/test_host_plane.py holds the list to its scan (12.5's governed set
that names a gate verb of cli_gate.py) and the mechanism to its word, all
in a lone checkout. Both lists share one registration helper, a_hosts_plane;
a module on both runs under the governed host.

Composed, 12.5's sixteen suites go from 174 red to 109: host-reg 58 and
one-off group H 7 turn green, and no green node turns red.

Refs opensoft/openxFactory#656 (CLAIMED 6013608299).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s it, nothing vendored (plan 038)

gate_console._validate_contract_document read both of its schemas from
parents[2] / "contracts" / "schemas", which in this leg is the checkout
root and holds no contracts/. Now each schema is read where its owner
keeps it:
  * gate-action-record.schema.yaml, openXdox-spec's, from this
    distribution's own packaged copy, through
    contracts.verified_bytes(), which checks it against copies.yaml's
    digest first;
  * demotion-execution-receipt.schema.yaml, openxFactory's, from a source
    the HOST registers with the new register_contract_schema_source().
    The same directory again is a no-op, a different one is refused, and
    a directory without the schema is refused when it is registered.
    unregister_contract_schema_source() and
    contract_schema_source_registered() complete the seam. With nothing
    registered, a receipt refuses, naming the call (R1Q27 (a)).

Nothing is added to copies.yaml, and test_packaged_validator.py is
unchanged and green. The seam sits near the end of the module, so that
no line before it moves. tests/test_no_hardcoded_status_words.py pins
seven prose f-strings in this module by line number.

tests/conftest.py registers openxFactory's contracts/schemas/ from the
composed tree, at process start, where the run is composed; a lone
checkout registers none. tests/test_gate_console_schema_source.py runs
the console in a child interpreter beside a stand-in doc_health, so it
runs in a lone checkout.

Composed, the 16 schema nodes and test_explicit_release_has_cli_and_http_parity
turn green on T020's host. 12.5's suites read 92 red of the 174, and no
green node turns red.

Refs opensoft/openxFactory#656 (CLAIMED 6013608628).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…thing vendored (plan 038)

tests/test_session_runbook.py (its RUNBOOK) and tests/test_session_notebook.py
read docs/ideation-dashboard-session-runbook.md at this checkout, where the
runbook sat before the carve sent it to openDox-spec. The new
scripts/composed_placements.py links it, for a composed run, from the
composed tree (openDox/spec/docs/ under the openxFactory checkout) into
docs/. .gitignore names the placed path, so the checkout stays clean and
nothing placed can be committed.

Before it places anything, the script checks four things and refuses
(exit 2), naming the remedy, if any fails:
  * the spec checkout's HEAD is the commit the openDox root's spec
    gitlink pins;
  * the runbook is tracked there, unmodified;
  * the destination is ignored by .gitignore;
  * anything already at the destination is the script's own link to that
    same source.
It places all or nothing. --remove takes back the script's own links and
nothing else. The composed workflow (T029) and the documented local
composed run both call it.

Composed, the 10 runbook nodes turn green on T021's tree, 12.5's suites
read 82 red of the 174, and no green node turns red.

Refs opensoft/openxFactory#656 (CLAIMED 6013608959).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Copilot r4194497045)

A registered source's receipt schema was checked only for existence. A
document that is valid YAML but no valid JSON Schema escaped as a raw
exception: an empty file raised AttributeError, `required: 1` raised
TypeError, and an unresolvable $ref raised jsonschema's referencing
error. cli_gate's demotion reports only GateRefused, BoundaryViolation or
OSError as "executed but receipt not written", after files have moved, so
any of these ended the verb with a traceback.

_schema_errors now parses the schema, checks it against the validator's
own meta-schema, and applies it. A failure at any of the three refuses as
GateRefused. _validate_contract_document keeps its line count, so no line
that test_no_hardcoded_status_words.py's PROSE_EXEMPT pins moves. A new
lone case covers the three schemas, and two more mutants are killed.

Refs opensoft/openxFactory#656 (CLAIMED 6013608628).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lacements.py (Copilot r4194596317; holder ruling (a))

The holder's ruling on opensoft/openxFactory#656 (comment 6016356225,
option (a)) adds tests/test_composed_placements.py to T022's files. The
script changes the file system, so its guards now live in the suite CI
runs, not in a scratch harness.

Each case runs the script in a child interpreter, as the composed run
calls it. The script under test is a copy of this checkout's script,
inside a scratch checkout that carries this checkout's own .gitignore,
and the tree it reads is a scratch openxFactory/openDox/spec tree pinned
to commit A. No case writes into this checkout. 14 cases cover:
  * placing, with the checkout left clean;
  * the no-op of placing again;
  * --remove;
  * a spec off its pin;
  * a modified runbook;
  * an untracked runbook;
  * an uninitialized spec leg;
  * an uninitialized openDox root;
  * a destination .gitignore does not name;
  * no git;
  * a file, or a link elsewhere, at the destination, kept under both
    place and --remove.
No case skips, and the file carries none of 12.5's governed-suite markers.

Writing the uninitialized-leg case found a misreport in the script, now
fixed. An uninitialized spec leg is an empty directory, so git answered
for the openDox root above it, and the refusal read "spec is at <the
root's HEAD>". The script now asks git about the leg only when the leg
has its own .git, so the refusal reads "is not checked out" with the
remedy. It refused before as well; only the message was wrong.

Refs opensoft/openxFactory#656 (CLAIMED 6013608959).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tory pin (plan 038)

A new workflow, `.github/workflows/composed.yml` (job `composed`), runs
F12.1, the falsifier of #1144's 12.5, composed with openxFactory, as F5.2
runs under R1Q23 (a). ARC-Q2 (a) (openxFactory#656 6003918488) makes the
composition permanent (CF-5); N-7a gives it its own file and its own pin,
`tests/composed_host_pin.yaml` (`schema_version`, `kind`, `commit`), first
value openxFactory main 92010d3e. Both ruled with plan 038 (6013547504).

The job: the package and its test extra in a fresh venv, with F9.2's pin
assertion; openxFactory at the pinned commit, with only openDox, openXdox
and openXwallet initialized recursively (installs/omnigent-install is
private and never imported); T022's placement script; 12.5's governed set,
computed with its floor of 16, each suite alone with
PYTHONPATH="$OPENXFACTORY/scripts"; the oracle in 12.5's call; and the
32-entry help tree against its golden (R2Q3 (a)), which `validate.yml`
leaves out. Its child scrubs PYTHONPATH, so the composed `scripts/` reaches
it through a `.pth` in the run's own venv, written after the suites ran.

Making `composed` a required check is the holder's act after the first
green run on main (N-7b (a)); this commit changes no setting.

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@brettheap
brettheap requested a balanced review from Copilot October 6, 2026 12:43
@sourcery-ai

sourcery-ai Bot commented Oct 6, 2026

Copy link
Copy Markdown

Reviewer's Guide

This draft establishes the permanent composed validation path: it pins and composes openxFactory with openDox, safely links carved documents, runs and diagnoses the governed suite set plus oracle/help-tree checks, and updates schema and host-registration plumbing so tests execute against the correct owner-provided contracts and host facets. The workflow is intentionally expected to remain red until the dependent T020–T026 and T028 changes land.

Flow diagram for the permanent composed validation run

flowchart TD
    Checkout[Checkout openXdox-code with full history] --> Install[Create venv and install pinned openDox]
    Install --> Pin[Validate composed_host_pin.yaml]
    Pin --> Host[Checkout pinned openxFactory and selected submodules]
    Host --> Place[Run composed_placements.py]
    Place --> Governed[Compute governed suites and enforce floor of 16]
    Governed --> Suites[Run each governed suite with openxFactory scripts]
    Suites --> Oracle[Run protected_suites.py oracle]
    Oracle --> Help[Run assembled help-tree golden test]
    Suites --> Help
Loading

File-Level Changes

Change Details Files
Adds a permanent composed CI workflow that validates openDox against a pinned openxFactory checkout and runs the governed integration surface.
  • Creates a full-history, Python 3.12 workflow triggered on pull requests, main pushes, and manual dispatch.
  • Installs openDox from its declared commit pin and strictly validates the host pin YAML.
  • Checks out the pinned openxFactory revision with only the required public submodules, rewrites SSH URLs locally, and verifies HEAD.
  • Places the pinned session runbook into the composed tree, computes the governed suite set with a 16-suite floor, runs each suite independently, reports JUnit failures, and executes the protection oracle and assembled help-tree check.
  • Keeps the oracle and help-tree diagnostics available after governed-suite failures, while gating the oracle on successful suite collection/execution.
  • Adds workflow-level permissions, timeout, environment propagation, and virtualenv path setup.
  • Files: .github/workflows/composed.yml, tests/composed_host_pin.yaml, .gitignore
.github/workflows/composed.yml
tests/composed_host_pin.yaml
.gitignore
Introduces guarded placement of carved documents from the pinned openDox-spec checkout into the composed test tree.
  • Adds a placement manifest currently containing the session runbook link.
  • Verifies the openDox root's spec gitlink, checked-out spec HEAD, tracked and clean source, ignored destination, and safe existing-link state before making changes.
  • Implements all-or-nothing placement and conservative removal of only links created by the script.
  • Adds the destination to .gitignore so composed artifacts cannot be committed.
  • Files: scripts/composed_placements.py, .gitignore
scripts/composed_placements.py
.gitignore
Moves contract-schema resolution from checkout-relative files to owner-controlled packaged or host-registered sources.
  • Validates packaged gate-action schemas through contracts.verified_bytes() and host demotion schemas through an explicit registration API.
  • Enforces one active host schema source, supports idempotent registration and explicit unregistering, and rejects missing schema files.
  • Validates schema parsing, JSON Schema validity, and application failures as named GateRefused errors.
  • Registers openxFactory's schema directory during composed test setup while leaving lone-checkout behavior explicitly refusing host-owned receipt validation.
  • Files: src/openxdox/gate_console.py, tests/conftest.py, tests/test_gate_console_schema_source.py
src/openxdox/gate_console.py
tests/conftest.py
tests/test_gate_console_schema_source.py
Adds governed-host selection and isolation for suites that exercise openXdox gate verbs.
  • Derives the governed suite set using the same markers and floor as the composed workflow, then verifies the explicit governed-host suite list matches gate-verb usage.
  • Registers either openxFactory's composite host in composed runs or a local stand-in carrying openXdox's command, gate-route, and projection columns.
  • Builds host facets only after importing this checkout's openXdox modules, restores sys.path, and rejects modules loaded from another openXdox checkout.
  • Preserves prior host registrations around each test and retains stand-in behavior for non-governed host-plane suites.
  • Adds tests for list coverage, host precedence, column composition, path isolation, and missing/failed openxFactory host imports.
  • Files: tests/conftest.py, tests/test_host_plane.py
tests/conftest.py
tests/test_host_plane.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The PR oracle skips current pull-request commits, and the placement script lacks persistent automated coverage.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity

Open (2)
What changed in this PR

Establishes permanent composed validation against pinned openxFactory, including stacked host, schema, and document-placement prerequisites.

Changes:

  • Adds governed-host and contract-schema integration.
  • Adds safe runbook placement from the composed tree.
  • Adds pinned composed CI with governed suites, oracle, and help-tree checks.
File Description
.github/​workflows/​composed.yml Defines composed CI validation.
.gitignore Ignores the placed runbook.
scripts/​composed_placements.py Safely places composed-tree documents.
src/​openxdox/​gate_console.py Resolves schemas from their owners.
tests/​composed_host_pin.yaml Pins the openxFactory host.
tests/​conftest.py Registers governed hosts and schemas.
tests/​test_gate_console_schema_source.py Tests schema-source behavior.
tests/​test_host_plane.py Tests governed-host selection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/composed.yml
Comment thread scripts/composed_placements.py
…Copilot r4195440016)

On `pull_request` GitHub checks out a merge commit whose first parent is
the base, so the oracle's first-parent walk found only what had already
landed, and a pull request's edit to a governed suite was judged only once
it was on main. The oracle judges a landing by its diff against its first
parent and by the blobs on either side, so the merge commit is the landing
as it would be: it now joins the landings where the pull request's body
carries the arc's trailer line (exact line), which the lander copies into
the landing's message. The step first asserts HEAD's second parent is the
pull request's head, so HEAD really is that merge commit.

Also: an empty governed set is refused by name (git grep's no-match exit is
read as zero suites; any other git grep failure stops the step).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

PR-body edits do not rerun the workflow, allowing the oracle result to become stale.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (2)

Comment thread .github/workflows/composed.yml
The oracle reads the pull request's body for the arc's trailer line, and a
bare `pull_request` trigger runs on opened, synchronize and reopened only,
so a trailer added after a green run left the head's check green without
the change ever being judged as an arc landing. The workflow now also runs
on `edited`, beside the default three.

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

It remains a draft with cross-repository sequencing dependencies and an intentionally failing composed check.

Review effort: Balanced
Findings: None

Resolved since last review (1)

…451)

Brett Heap ruled "Amend F12.1 with --chains (Recommended)" (openxFactory#656
comment 6016648451). Batch Q (T005, openxFactory#1248) records it at 12.5's
falsifier, and its note says T026 and T029 carry it out. The oracle step now
passes `--chains`, so several entries chained by blob may together admit one
landing's edits to one governed suite, each still one edit inside its own
named test or span, under batch K's rule unchanged. T026 lands as one pull
request on exactly that.

Proved on simulated pull-request merge commits: two edits to one governed
suite in one landing, entered as two chained entries, are admitted
(`ok: 4 protected edit(s), each entered and holding`); with `--chains`
dropped the same landing is refused, so that mutant is killed. Today's nine
landings still read `ok: 3`.

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

It remains draft, cross-repository, sequencing-dependent, and intentionally has a failing composed check.

Review effort: Balanced
Findings: None

…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>
brettheap and others added 2 commits October 6, 2026 22:22
…s checks (Copilot r4200975244)

Git honours GIT_DIR, GIT_WORK_TREE, GIT_INDEX_FILE and their kin over the
directory each check runs in, and it sets GIT_DIR and GIT_WORK_TREE for
every hook it runs. Run from a hook, the script's pin, clean and ignore
checks would have inspected another repository. _git now drops the same
variables openxdox.domain_corpus_adapter's AMBIENT_GIT_VARIABLES drops, for
every call.

tests/test_composed_placements.py adds a regression case, clean and edited.
It runs the script with GIT_DIR, GIT_WORK_TREE and GIT_INDEX_FILE pointed at
a decoy repository:
  * clean: the pinned runbook is placed;
  * edited: the edit is refused as "differs from openDox-spec's pinned
    commit".
Against the script before this commit both cases fail (2 failed, 18
passed). With it, 20 passed. MR-ambient-git-variables-kept is killed, and
all 18 mutants are killed.

Refs opensoft/openxFactory#656 (CLAIMED 6013608959).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap added a commit that referenced this pull request Oct 6, 2026
…38, T094's R2-INV-R9) (#46)

Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Authored by lane openXfactory-3, delegated by lane openxfactory-4 (#656 6021862323)

Arc: neutral-product-standalone-operability

## T095: the composed harness for the declared integration tests (plan 038; T094's map R2-INV-R9)

**Task.** T094's slice T095 (R2-INV-R9, "Slice outline"): the composed conftest. It owns `tests/conftest.py` (chain T020 → T021 → **T095** → T073 → T074) and `tests/test_host_plane.py` (T020 → **T095**). Lane openxfactory-4 records the slice in plan 038's `tasks.md` after T002 (#1251). Until then the contract is the outline row plus the holder's rulings below. After: T021 (#40), LANDED as `f70bc9c3`.

**Realizes.** HR, SR, CS and LI from the map, in `tests/conftest.py`, with their guard in `tests/test_host_plane.py`. It completes requirement 9's declared composed tests (T073, F9.1), so this PR carries the arc's line.

**Falsifier.** The 119 nodes the outline names (HR 105, HR+CS 4, CS 5, SR 1, LI 4), run composed, each file alone, must be red at the base and green at the head. 12.5's 16 governed suites, F5.2's 5 protected suites and `test_column_contributions_governed.py` (9 passed) must read unchanged, and `tests/test_declared_exclusion.py` must be green lone.

**Ruled.**
- openxFactory#656 comment 6021830531 (T094's rulings): OQ-R9-1, OQ-R9-2 and OQ-R9-4 at their defaults, and W2 (extend T086's Q4 (a) local-install fixture to `test_renderer.py` and `test_repo_root_guard.py`).
- openxFactory#656 comment 6022291206, the holder CONFIRMED T095's two departures from OQ-R9-1's default:
  1. T020's `plane_host_for` stays untouched. A separate autouse fixture registers the declared host inside T020's stand-in plane.
  2. `DECLARED_HOST_SUITES` (17) is guarded STRUCTURALLY: declared entries outside 12.5's set, and never `test_column_contributions_governed.py`. No text scan selects exactly the 17.

**Decisions.**
- **Every piece is composed-only.** Where `opendox_host` is absent, which is every lone checkout, nothing is registered, built or set. So a lone run, and the evidence `tests/test_declared_exclusion.py` holds each listed file to, are unchanged.
- **The edits are self-contained.** No T020, T021, T026 or T073 line is touched. The new block is appended after T021's. T086's launch-suite fixture changes in one line (W2).

### The interface (tests/conftest.py)

- **HR:**
  - `DECLARED_HOST_SUITES`, the 17 declared files;
  - `declared_host_for(suite)`;
  - the autouse fixture `_the_governed_host_for_the_declared_host_suites`. It requests T020's stand-in fixture, so the governed host (`governed_host()`) is the one registered for the test.
- **SR:** `HOST_SEAM_SUITES`, `HEALTH_CHECK_SEAM`, `BINDING_TRUST_SEAM`, `HOST_SEAM_HOLDERS`, `openxfactory_seams(wanted)` and `a_hosts_seam(module, call, what)`.
  - `test_authoring_agent.py` gets openxFactory's scoped health check, and `test_doxchat_model_intake.py` its binding-trust policy.
  - Both are read off `opendox_host.seams()` with `sys.path` put back, and an `openxdox` module from elsewhere refuses the case.
  - Each seam is put back after the test: a host's earlier registration again, openDox's default not.
- **CS:** `CONTRACTS_DIR`, a `Path` to `<farm>/contracts`, or None lone. It is built once at import, outside the checkout (a temporary directory removed at exit, and a temporary root inside the checkout is refused before anything is created), and exported as the `CONTRACTS_DIR` environment variable. The farm holds:
  - `contracts/schemas/`, 55 links: each carve row under `contracts/schemas/` through `carved_reach.sources_under`, then openxFactory's own;
  - `examples/ideation-dashboard/` beside it, 139 links: the three owners' YAML examples, less openDox's `opendox-snapshot-*`. Two owners naming one path is refused.

  The builders are `build_contracts_farm()` and `compose_the_contracts_farm()`. The reach must be the composed tree's own.
- **OQ-R9-4:** `openxfactory_root()`, the directory above `opendox_host.py`, or None lone.
- **LI (W2):** `LOCAL_INSTALL_SUITES` (`test_renderer.py`, `test_repo_root_guard.py`) and `runs_a_local_install(suite)`. T086's fixture now reads `runs_a_local_install`: the launch suite everywhere, as before, and the two suites only composed.

### The falsifier, measured

The composed form is R1Q23 (a):
- openXdox-code: the base is `main` `f70bc9c3` (T021 landed), and the head is this branch;
- openDox installed from openDox-code `main` `84f8ed83` (T025);
- openxFactory at `92010d3e` (#44's `tests/composed_host_pin.yaml`), with `openDox`, `openXdox` and `openXwallet` initialized recursively and its `scripts/` on `PYTHONPATH`;
- T022's one placement (the runbook link) made before each run and removed after;
- the colour variables unset, and `TERM=dumb`.

Each of the 66 declared entries ran alone, four at a time: `python -m pytest -q -p no:cacheprovider -rfE --junitxml=<file>.xml <file> < /dev/null`.

```
base f70bc9c: TOTAL over 66 files: 2001 passed, 496 failed, 72 errors, 2 skipped; 568 red
head c5d07c6: TOTAL over 66 files: 2120 passed, 377 failed, 72 errors, 2 skipped; 449 red
node for node (2571 nodes at both):
  the 119:  red -> passed 119  (HR 105, HR+CS 4, CS 5, SR 1, LI 4)
  green at base, red at head: 0
  any other node red -> passed: 0
head c6982e3 (adds only the in-checkout refusal and its case): the 17 falsifier files again,
  each alone: the 119 passed 119 of 119
```

**Unchanged, each line the same at base and head:**

```
12.5's 16:
tests/test_branch_session.py: 132 passed, 0 failed
tests/test_doxbench_mutation_boundary.py: 80 passed, 0 failed
tests/test_doxbench_share.py: 32 passed, 2 failed
tests/test_gate_loop_views.py: 74 passed, 0 failed
tests/test_gateway_provenance.py: 24 passed, 0 failed
tests/test_hosted_actor.py: 5 passed, 0 failed
tests/test_session_commits.py: 22 passed, 0 failed
tests/test_session_confinement.py: 50 passed, 3 failed
tests/test_session_gates.py: 78 passed, 3 failed
tests/test_session_lifecycle.py: 53 passed, 0 failed
tests/test_session_notebook.py: 39 passed, 0 failed
tests/test_session_records.py: 29 passed, 0 failed
tests/test_session_runbook.py: 11 passed, 0 failed
tests/test_session_transaction.py: 57 passed, 0 failed
tests/test_session_verbs.py: 60 passed, 5 failed
tests/test_staging_workbench.py: 54 passed, 29 failed
F5.2's 5:
tests/test_generator.py: 45 passed
tests/test_session_snapshot.py: 23 passed
tests/test_snapshot_determinism.py: 6 passed
tests/test_snapshot_registry.py: 40 passed
tests/test_snapshot_validation_launch.py: 9 passed
tests/test_column_contributions_governed.py: 9 passed, 0 failed (OQ-R9-1: off the declared-host list)
```

The governed reds are 12.5's own, node for node the same at both ends. T026 and the rulings own them.

**Lone:**
- `tests/test_declared_exclusion.py` in validate.yml's environment, the CI form: 207 passed.
- `tests/test_host_plane.py`: 28 passed lone and 28 composed at the head (T020's 9, plus 19 new); 9 composed at the base.
- The CI form of the whole suite at `c5d07c6b`: `selected=1131 passed=1127 skipped=4 failures=0 errors=0`. `c6982e34` adds one case. The floors are 1099 and 1095, and skipped is exactly 4. The floors are left alone, since only T029 raises them.

**Mutants: 35 of 35 killed.** Each is one exact replacement in a scratch clone, then restored.
- 33 were run at `70ed40c6`:
  - 30 are killed by `tests/test_host_plane.py` run lone:
    - **HR:** the composed condition dropped; membership dropped; the stand-in dependency dropped; `test_column_contributions_governed.py`, a governed suite, or an undeclared file added; the wrong parent; the fixture's check inverted.
    - **SR:** `sys.path` not put back; the elsewhere-refusal dropped; the exactly-one-row check weakened; the lone guard dropped; a host's earlier registration not restored; openDox's default restored; no unregister first; a governed suite added.
    - **CS:** `opendox-snapshot-*` kept; nested carve rows linked; a collision passed; openxFactory's own schemas dropped; examples not walked recursively; the environment not set; the reach check dropped; no removal at exit; `under` ignored; the lone guard dropped.
    - **LI:** the composed condition dropped; the launch suite dropped; T086's condition reverted; a non-`generate-and-open` suite added.
  - 3 are killed by a named node of the composed run: the seam fixture inverted; `CONTRACTS_DIR = None`; the declared host never registered.
  - The 30 lone mutants were re-run at `c2192e3e`, and 10 were spot-checked at `c5d07c6b`.
- 2 more at `c6982e34`: the in-checkout check dropped, and TMPDIR ignored.

### Review

- **CI at `c6982e34`:** `validate` passed (run 37540071853); SonarCloud's Quality Gate passed with 0 new issues. At `70ed40c6` SonarCloud read 5 smells in `tests/test_host_plane.py`, which `c2192e3e` fixed. Its sixth was in T021's file, now on `main`.
- **Copilot:**
  - At `c5d07c6b`: "Changes recommended", 1 finding, r4200978505 (a temporary root inside the checkout would build the farm in the working tree). FIX-NOW: `c6982e34` refuses it before anything is created, with a new case and 2 mutants. The thread is answered and resolved.
  - At `c6982e34`: COMMENTED, "Findings: None".
  - Threads: 1, unresolved 0.
  - At `c2192e3e` an earlier review listed one "previously missed" item in T021's `src/openxdox/gate_console.py`, which is on `main` and outside this PR's diff.

### Files changed

- `tests/conftest.py`
- `tests/test_host_plane.py`

### Not done

- Nothing in this slice. The other slices' nodes in these files stay red until those slices land: T096 to T103 code against this interface, and T104 rides the openDox pin.
- The EN node, `test_repo_root_guard.py`'s refusal-path case, is red under a local TMPDIR in the home directory only, and green on a runner.

🤖 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>
brettheap and others added 2 commits October 6, 2026 23:35
Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ch leg's own work tree (Copilot r4201032934, r4201424060, r4201032886)

Three findings Copilot raised at 146ae92 and c22b5df. Four of the new
cases were real bypasses against the script before this commit: it placed
the link instead of refusing.
  * r4201032934: git diff calls a file clean without reading it where the
    index marks it assume-unchanged or skip-worktree. The check now
    compares git hash-object of the source with the pinned blob HEAD:<path>.
  * r4201424060: a source that is a symbolic link is refused, whether the
    pinned commit tracks it as a link or it replaced the file. A link's
    bytes are not fixed by the pin.
  * r4201032886: the openDox root and the spec leg must each be their own
    work tree. Their own .git, and git rev-parse --show-toplevel resolving
    to the directory itself, as openxdox.domain_corpus_adapter checks. So an
    enclosing repository cannot answer HEAD:spec, and core.worktree cannot
    point the checks elsewhere.

tests/test_composed_placements.py adds seven cases:
  * an edit under --assume-unchanged, and one under --skip-worktree;
  * a runbook the pinned commit tracks as a link, with its target edited;
  * a runbook replaced by a link to identical bytes;
  * an openDox root answered by an enclosing repository;
  * an openDox root without a spec gitlink;
  * a spec leg whose core.worktree is elsewhere.
Against the script before this commit, six of them fail; four of those
placed the link (rc 0). With it, 27 passed. All 21 mutants are killed, each
by its own case, including the new MS (no symlink check), MU (root may be
any repository), MV (toplevel not compared) and MB (content not compared).

Refs opensoft/openxFactory#656 (CLAIMED 6013608959).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap added a commit that referenced this pull request Oct 6, 2026
…; T094's map R2-INV-R9) (#48)

Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Authored by lane openXfactory-3, delegated by lane openxfactory-4 (#656 6021862323)
Arc: neutral-product-standalone-operability

## T101 (plan 038): the doxBench texts

One of the R9 repair slices, T095 to T104, which repair every composed red outside 12.5's set in this leg's declared exclusion, so that T073's composed step 5a goes green (requirement 9's declared composed tests, T073, F9.1).

**Contract.** There is no `tasks.md` entry yet: lane 4 records T095 to T104 after T002 (#1251). The contract is T101's row in T094's map, `R2-INV-R9.md` § "Slice outline" (#656 `6021830607`), plus the holder's rulings:
- `6021830531`: W5, drop the manifest home; W6, rewrite over both owners.
- `6022291206`: T101's Files gain `tests/declared_exclusion.yaml` for one entry only, `test_doxbench_blank_reason.py`.

- **Realizes:** T094's row T101: PP 25 (threads 21, thread_wiring 2, knowledge_service 2), CP 6 (thread_wiring 1, transport 1, request_handling 1, doxchat 3), CS constants 5 (knowledge_service 2, doxchat 3) and HH 1, so 37 in all; plus 1 on W5 and 1 on W6.
- **Falsifier:** these files composed, each file alone, red at the base and green at the head; plus the unchanged set.
- **After:** T095 (#46) for `CONTRACTS_DIR` and `openxfactory_root()`, LANDED as `e2340bb6`; T021 (#40), LANDED as `f70bc9c3`. Met.

## What changes (test text only; no `src/` change)

| means | where | what |
|---|---|---|
| PP | `test_doxbench_threads.py` (21), `test_doxbench_thread_wiring.py` (2), `test_doxbench_knowledge_service.py` (2) | Pre-carve `scripts/ideation_dashboard/` reads respelled to their carved homes, through the conftest's `carved_module_path` (plan 034 T040's resolver). The push sweep also reads its three openxFactory-only modules (`nightly_lane.py`, `doxbench_status_exemption.py`, `serve_openxfactory_lanes.py`) from the composed tree, `openxfactory_root()`. Each swept module is read from **exactly one** of its three homes; a module in two or none refuses the sweep by name. |
| CP | `test_doxbench_thread_wiring.py` | Liveness is now asked through the scope seam (T084, openDox-code `e49b17c`): `self._is_live_session_ref(` → `column_seams.scope.current().is_live_session_ref(`. openXdox's registration there IS `doxbench_scope` (`column_contributions.SCOPE is doxbench_scope`). `live_session_branches(` stays absent. |
| CP | `test_doxbench_request_handling.py` | `compute_capabilities` for the local human is asked with this leg's own gate routes (`GateRoutesExtension().routes()`), since T084 makes `gate` true only where a contributed binding answers `POST /actions/gate/<verb>`. The pinned dict is unchanged. |
| CP | `test_doxbench_transport.py` | The save seam is re-pinned at its rulings' placements. Q3 (`5642758731`): the route is in `swb-session.js`, not openDox's model. Q10 (`5648065587`), S5 (openDox-code `8efb3cf`): no static import in `app.js`; one `.session.firstEditTransport(` call site with the `refusalTransport()` fallback; and `firstEditTransport` declared in the `gate.workbench.session` binding's `exports` (`VIEW_BINDING_SPECS`). This was the map's one INFERRED node, now measured. |
| CP | `test_doxchat_model_intake.py` (3) | Both auth-kind pins read the broker-enrolled kinds, every member but `none` (T080, openDox-code `1130e99`; #1144 16.3). The approval case, after approving, reads the model listed and NOT yet available. It then records this machine's trust as `opendox model-binding trust` does (`doxbench_trust.recording_for`, through the case's own `stand_in_host_trust`), and reads a fresh catalog in which the model is available (T100, openDox-code `38d3350`; #1144 16.3a). A factory takes its verdict when it is built, so each catalog comes from a factory built at that moment. |
| CS | `test_doxbench_knowledge_service.py` (2), `test_doxchat_model_intake.py` (3) | The chat-turn and gate-action-record schemas are read from T095's farm, `CONTRACTS_DIR`. |
| HH | `test_doxbench_transport.py` | The raw client sends ONE `Host` line (`skip_host=True`). openDox's loopback Host guard (openDox-code `390e2c2`) refuses a second one as `403 invalid_host`. |
| W5 | `test_doxbench_blank_reason.py` | The manifest home is dropped; the rule stays stated in its other three homes. |
| W5 | `tests/declared_exclusion.yaml` | One entry: `test_doxbench_blank_reason.py` → `reasons: [doc_health]`, and its note is dropped. `count` stays 66, and `openxfactory-contracts` keeps its four contracts-only entries. That reason carries no `only` list at `main`, so nothing moves there. |
| W6 | `test_doxchat_model_intake.py` | The v1.45 release test is read over both owners in the composed tree. Each row is in its owner's manifest and in no other of the three: `gate-action-record` in the openXdox root's, `xfactory-workbench-chat-turn` in the openDox root's. `release_member` and `contract_schema_version: 1` hold, and the digest is recomputed from the spec leg's bytes. The immutable half, the CHANGELOG entry and the digest inventory, is read from openxFactory's tree. The two `consumption_rule` asserts left with openxFactory's rows at contract-v4.0. |

## Falsifier (measured; quoted)

**Composed form, R1Q23 (a):**
- openxFactory `92010d3e` (`tests/composed_host_pin.yaml` in #44), with `openDox`, `openXdox` and `openXwallet` initialized recursively and its `scripts/` on `PYTHONPATH`.
- openDox installed from openDox-code `main` `84f8ed83`.
- The colour variables unset, and `TERM=dumb`.
- Each file alone: `python -m pytest -q -p no:cacheprovider -rfE --junitxml=… tests/<file>` from the checkout root.
- T022's placement is immaterial here: it places only the session runbook, which none of these files reads.

**Base:** `main` `e2340bb6` (T095 landed). **Head:** `6f157db7`, this PR: one change commit, the Copilot fix `2fdbd96`, and merges of `main`. Measured 23:34Z to 23:43Z.

| file | base | head |
|---|---|---|
| `test_doxbench_threads.py` | 78 passed, **21 failed** | 99 passed, 0 failed |
| `test_doxbench_thread_wiring.py` | 47 passed, **3 failed** | 50 passed, 0 failed |
| `test_doxbench_knowledge_service.py` | 47 passed, **4 failed**, 1 skipped | 51 passed, 0 failed, 1 skipped |
| `test_doxbench_transport.py` | 27 passed, **2 failed**, 1 skipped | 29 passed, 0 failed, 1 skipped |
| `test_doxbench_request_handling.py` | 45 passed, **1 failed** | 46 passed, 0 failed |
| `test_doxchat_model_intake.py` | 36 passed, **7 failed** | 43 passed, 0 failed |
| `test_doxbench_blank_reason.py` | 119 passed, **1 failed** | 120 passed, 0 failed |

- **39 nodes go red → green:** the row's 37, plus W5 1 and W6 1. **0 nodes turn red.**
- Earlier, measured at `main` alone (`8b64fae0`, before T095), the main-measurable part read 49 → 17 over these files. The 17 were T095's HR 7, T021's 3, CS 5 and the approval node.
- **The approval node does not need T095's binding-trust seam.** Over a scratch integration of this change with #40 and #46, with `HOST_SEAM_SUITES` emptied by a throwaway plugin, it passes, and `test_doxchat_model_intake.py` reads 43 passed. Its own `stand_in_host_trust` carries the trust.

**The unchanged set, base vs head, same form, node for node (0 newly red):**
- **12.5's 16 governed suites** (`git grep -l -e open-pr -e open_pr -e FakePullRequests -- 'tests/test_*.py'`): identical per node. The reds that remain are other slices', and none is changed.
- **F5.2's files** (`test_generator.py`, `test_snapshot*.py`, `test_session_snapshot.py`; 7 files): all green at both.
- **`test_column_contributions_governed.py`:** 9 passed at both.
- **Over the 31 files:** 49 red at the base, 10 at the head (all in 12.5's governed suites, unchanged), 39 fixed (all in T101's seven), 0 newly red.

**Lone, `tests/test_declared_exclusion.py`** (CI form: `pip install -e ".[test]"`, openDox `dede32b4` from the pin), at `6f157db7`: **207 cases, 0 red**, inside the whole-suite run below. The six other files still fail lone at collection on `doc_health`, unchanged.

## Mutants (14 of 14 killed)

Measured over a scratch integration of this change with #40 and #46. Each fault was planted in the subject, its node run composed over the integration, and everything restored and digest-checked.
- **Push sweep:** a push token in openxFactory's `nightly_lane.py` → `('nightly_lane.py', 'git push')`. `nightly_lane.py` planted at this leg's `src/openxdox/` too → "expected in exactly one of …". The resolver without openxFactory's home (test mutant) → the same.
- **Liveness:** the serve asking `doxbench_scope` directly → the seam pin fails. A shape-valid copy of the question registered as the scope → the identity pin fails.
- **Save seam:** a static `swb-session.js` import back in `app.js`; `firstEditTransport` dropped from the binding's `exports`; the route constant back in openDox's model; the `refusalTransport()` fallback gone. Each fails the test.
- **W6:** a second manifest claiming `gate-action-record` → `('gate-action-record', ['openXdox', 'openDox'])`. A drifted byte in the spec leg's schema → `gate-action-record`. A drifted recorded digest → `xfactory-workbench-chat-turn`.
- **Approval:** the catalog read off the pre-trust factory (test mutant) → `assert False is True`. The trust never recorded → the same.

## CI

- **`validate` at `6f157db7`: success** (run 37548131461). SonarCloud: success.
- CI form locally at `6f157db7`: selected=1165 passed=1161 skipped=4 failures=0 errors=0, above `validate.yml`'s floors 1099/1095/4. The floors are untouched.
- **Copilot at `6f157db7`:** COMMENTED, "Needs a closer look", Findings: None. One thread in all, r4199422320 (Low, the release test's summary), fixed in `2fdbd96` and resolved: 0 unresolved.

## Files (8)

`tests/test_doxbench_threads.py`, `tests/test_doxbench_thread_wiring.py`, `tests/test_doxbench_knowledge_service.py`, `tests/test_doxbench_transport.py`, `tests/test_doxbench_request_handling.py`, `tests/test_doxchat_model_intake.py`, `tests/test_doxbench_blank_reason.py`. The eighth, `tests/declared_exclusion.yaml`, is one entry only (holder `6022291206`). Its chain is now T101 → T073 → T074; T073 (#45) edits only the header and `reasons` block, a different hunk.

None of these is in 12.5's computed 16 or in F5.2's set.

## Not done

- Nothing owed by this slice.
- **Left as is, stale but harmless:** `tests/test_declared_exclusion.py`'s docstring (`:28-30`) and comment (`:261`) still name `test_doxbench_blank_reason.py` as a two-reason entry, and `_CONTRACT_FILES` keeps the checkout's `contracts/manifest.yaml` with its evidence case. That file is not T101's.

🤖 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>
brettheap added a commit that referenced this pull request Oct 7, 2026
…erved facet (plan 038, T094's R2-INV-R9) (#51)

Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Authored by lane openXfactory-3, delegated by lane openxfactory-4 (#656 6021862323)

Arc: neutral-product-standalone-operability

## T098: display harnesses (A), and the server's scope labels from the served facet (plan 038)

This is a slice of T094's map R2-INV-R9 (opensoft/openxFactory#656 `6021830607`). `tasks.md` has no entry for it yet: lane 4 records the T095 to T104 entries after T002 (#1251). Until then, its contract is the map's slice-outline row plus the holder's rulings, `6021830531`.

- **Realizes:** 21 of the composed reds outside 12.5's set in the declared exclusion, toward requirement 9's declared composed tests (T073, F9.1):
  - canvas: DJ 8 and DJ + HR 1;
  - explorer: DJ 5 and CP 2;
  - the 5 nodes held by R9-R1: canvas 2, explorer 2, scope 1.
- **Falsifier (the outline row):** the composed run of the three files, each alone, at `main` + T095, takes the named 21 to 0 red. These read unchanged:
  - 12.5's 16 governed suites;
  - F5.2's 5 suites;
  - `test_column_contributions_governed.py` (9 passed).

  `tests/test_declared_exclusion.py` reads green when run lone.
- **Ruled:**
  - **R9-R1 (a):** `doxbench_scope.py`'s labels and notes derive from the facet the browser reads, and every pin that asserts words is respelled through VR to the SERVED words.
  - **R9-W1 (A″):** the harnesses copy `display.js` beside the module they copy, as #43 does.
  - Both are in `6021830531`. They conform to the ruled overlay (`5784683830`, `5801057769`) and to the served-display rule for the staging suite (`6016648451`).
- **After:** T095 (#46), LANDED as `e2340bb6`. The served display needs the governed host that T095's `DECLARED_HOST_SUITES` registers for these three files when the run is composed. This branch merges `main` `e2340bb6`, and every number below is measured over it.

## What changes

**`src/openxdox/doxbench_scope.py`** is this slice's under R9-R1 (a), as claim `6021862323` names it.
- **The labels now come from the facet.** The section labels and notes were one domain's words ("cluster documents", "topic folder documents"). They are now built from the facet by `_section_meta(_served_display())` once per `resolve_scope` call.
  - The six sentences are word for word the ones openDox's staging model has built since § 3.4 slice S7 (`views/staging-workbench-model.js` `SECTION_META`).
  - Keys, flags and paths are unchanged.
- **Where the words come from.** `_served_display()` is the `display` block that `/capabilities` serves: `display_manifest(host_display())`.
  - It is read at call time, never at import and never cached, as `opendox.canvas_drafts` reads its one shared sentence.
- **Decision: no host means neutral words.** With no host registered there is no served page. The browser model given no facet speaks openDox's neutral words, so the server answers with those too rather than refusing.
  - The words label sections of an authorization answer and never decide it.
  - A library or test caller with no host therefore keeps its paths.
- **Who reads the labels.** Server-side, no module reads a section's label or note except the parity test: no prompt and no wire field carries them.

**`tests/test_doxbench_scope.py`**
- The browser harness copies `display.js` beside the two models and writes a `package.json` with `"type": "module"` (DJ). It installs the display through `setDisplay(readDisplay(...))`, as the shell does at mount.
- The parity test hands the browser the served display (VR).
- **Two new cases**, because the parity test alone cannot catch one thing. The composite serves openDox's neutral stage words for these three roles, since openXdox overlays only `completion` and the `values`. So parity cannot tell a server that reads the facet from one that always speaks neutral words. The new cases can:
  - **A host that declares its own stage words.** It declares openxFactory's pre-S7 words. The server labels read "cluster documents", "topic folder documents" and so on, and match the browser byte for byte.
  - **No host.** The server reads the neutral words, which equal the browser's with no facet.

**`tests/test_canvas.py`**
- `_run_node` copies `display.js` and writes the `package.json` (DJ). It hands the harness the served display, so every canvas derivation runs under the words the page is served (VR).
- The two gap pins now read the kind canvas-model.js emits under every host, `unsupported-candidate`, where they read `unsupported-possible` before (R9-R1).
- `test_js_and_python_draft_constants_agree` also cross-checks the seed state of a new draft. Both halves read it off one facet: the composer plan's `state` against `canvas_drafts.register_state("captured")`. This assertion is what makes the served-display hand-off observable (mutant H4 below).

**`tests/test_explorer_viewer.py`**
- **DJ:** each explorer harness copies `display.js` and writes the `package.json`.
- **VR:** `_EXPLORER_HARNESS` is handed the served display.
- **R9-R1, the served words.** The two label pins read the words the page is served, from the composite's artifact axis:
  - groups `proposal.md` and `tasks.md`;
  - classification `proposal.md`, `design.md`, `tasks.md`, `Spec deltas`, `Supporting docs`, `other`.
- **CP: the import-free pin**, respelled to S7's design:
  - no import names the viewer;
  - the only module imported is `./display.js`;
  - import statements spread over several lines are read whole.
- **CP: the wheel pin**, respelled to `opts.wheelKey === SOURCE`, with `SOURCE` held to be `STAGE_ROLES[0]`.

## The falsifier (composed, each file alone; quoted)

**The composed form, R1Q23 (a):**
- openxFactory `92010d3e` (`tests/composed_host_pin.yaml`), with `openDox`, `openXdox` and `openXwallet` initialized recursively, and its `scripts/` on `PYTHONPATH`;
- openDox installed from openDox-code `main` `84f8ed83` (force-reinstalled);
- the colour variables unset and `TERM=dumb`;
- per file: `python -m pytest -q -p no:cacheprovider -rfE --junitxml=<file>.xml tests/<file>.py < /dev/null`.

T022's `scripts/composed_placements.py` (taken from #44's tree) refuses to place in these checkouts: its `.gitignore` entry is not on `main` until #41 lands. None of these files reads the runbook it places. Every checkout read clean after every run.

**Measured over real `main`** (`e2340bb6`, carrying T095 #46 and T026 #43): the base is `main` itself, and the head is this PR's `c935f2a0` (this branch merged with `main`).

| run | test_canvas.py | test_explorer_viewer.py | test_doxbench_scope.py | named reds |
|---|---|---|---|---|
| base: `main` `e2340bb6` | 10 passed, 11 failed | 15 passed, 9 failed | 34 passed, 1 failed | **21** |
| head: `c935f2a0` | **21 passed, 0 failed** | **24 passed, 0 failed** | **37 passed, 0 failed** | **0** |

Before T095 landed, the same 21 were measured at a local integration that was never pushed (`main` `f70bc9c3` + #46 `c2192e3` + this branch): 21 red at its base and 0 at its head. At `main` `d45a0939`, before T095 existed, these files read 19, 9 and 1 red.

The 21 nodes that flipped:
- **canvas (11):** `test_member_pane_is_exactly_the_topics_document_edges`, `test_downstream_artifacts_are_in_the_lineage_strip_never_members`, `test_evidence_board_carries_section_ref_and_passage_hash`, `test_missing_cluster_degrades_to_null`, `test_gap_unclaimed_member`, `test_gap_unsupported_possible`, `test_gap_both_kinds_on_one_cluster`, `test_option_set_grouping`, `test_js_and_python_draft_constants_agree`, `test_esc_escapes_markup`, `test_model_passes_prose_through_as_data_not_html`;
- **explorer (9):** `test_explorer_js_exists_and_is_importable_standalone`, `test_staged_tile_lists_the_topic_folder_with_headers`, `test_aggregate_folder_entry_keeps_its_member_source_key`, `test_aggregate_folder_entry_disambiguates_same_repository_refs`, `test_aggregate_wheel_navigation_threads_the_owning_member_key`, `test_proposal_tile_groups_proposal_and_tasks`, `test_realized_tile_lists_the_archived_change_folder`, `test_unresolvable_tile_and_kind_degrade_to_null`, `test_change_file_classification`;
- **scope (1):** `test_server_and_actual_browser_projection_are_byte_shape_equivalent`.

Two nodes are new: `test_the_server_labels_follow_the_facet_a_host_declares` and `test_with_no_host_registered_the_labels_are_openDoxs_neutral_words`. No node regressed.

**The unchanged set** was compared node for node, base against head over `main`: **974 nodes, 10 red at both, 0 nodes changed outcome.**

| set | base | head |
|---|---|---|
| 12.5's 16 governed suites | 832 passed, 10 failed (pre-existing: `session_runbook` 9 and `session_notebook` 1, which read the runbook T022 places) | identical, file for file |
| F5.2's 5 suites | 123 passed | identical, file for file |
| `test_column_contributions_governed.py` | 9 passed | 9 passed |

## Mutants

Each mutant was applied at the integration (`main` `f70bc9c3` + T095 `c2192e3` + this branch), its file run alone composed, and the file restored byte for byte. **11 of 12 killed:**

| mutant | site | killed by |
|---|---|---|
| S1 | src: ignores the host's facet (always neutral) | `test_the_server_labels_follow_the_facet_a_host_declares` |
| S2 | src: no-host fallback dropped | `test_with_no_host_registered_the_labels_are_openDoxs_neutral_words` |
| S3 | src: members label hard-coded "cluster documents" | parity (served) + no-host |
| S4 | src: folder label one/many swapped | parity + declaring host + no-host |
| S5 | src: neighbourhood note drops a facet word | declaring host |
| H1 | scope harness: no `display.js` | all three browser cases |
| H2 | scope harness: browser ignores the display | declaring host |
| H3 | canvas: no `display.js` | the 11 canvas Node cases |
| H4 | canvas: neutral display instead of served | `test_js_and_python_draft_constants_agree` |
| H5 | explorer: no `display.js` | 7 explorer Node cases |
| H6 | explorer: neutral display instead of served | `test_proposal_tile_groups_proposal_and_tasks`, `test_change_file_classification` |
| H7 | scope harness: no `package.json` | **SURVIVES** (37 passed) |

**H7 is an equivalent mutant on the measured runtime.** Node v24 detects module syntax in a `.js` file with no `"type"` declared, so `package.json` changes nothing there. It is kept for runtimes without that detection, as #43's harness keeps it. T097 measured the same survivor.

## The lone CI form (`validate.yml`'s exact step, at `c935f2a0`)

`python -m pytest -q --junitxml=pytest-report.xml --deselect "$LEFT_OUT"`, run locally, read:

```
SELECTED=1165 PASSED=1161 SKIPPED=4 FAILURES=0 ERRORS=0   (floors MIN_SELECTED=1099 MIN_PASSED=1095 EXPECT_SKIPPED=4)
1161 passed, 4 skipped, 1 deselected in 169.43s
```

- `tests/test_declared_exclusion.py` run alone: rc 0. Each of the three files still fails lone on its declared reason (`doc_health`, at collection).
- The floors are untouched; only T029 raises them.

## CI and Copilot at `c935f2a0`

- **`validate`:** success, run `37548181742`. The triple is `selected=1165 passed=1161 skipped=4 failures=0 errors=0`, with pins `selected>=1099` (margin 66), `passed>=1095` (margin 66) and `skipped==4`.
- **SonarCloud Code Analysis:** success.
- **Copilot**, review `5435780547` at `c935f2a0`:
  - **"Needs a closer look", Findings: None.** Its stated reason is that "final composed validation after integration remains unverified". This PR's CI cannot run the composed form until `composed.yml` lands (T029 #44, then T073 #45). The composed validation over real `main` `e2340bb6` is the local run quoted above: 21 to 0 red, and the unchanged set identical node for node.
  - The earlier review at `588d4c15` (`5435186246`) gave the same verdict, with no findings, for the then-unmerged #46.
  - 0 review threads, 0 unresolved.

## Files changed

- `src/openxdox/doxbench_scope.py`
- `tests/test_canvas.py`
- `tests/test_explorer_viewer.py`
- `tests/test_doxbench_scope.py`

No protected suite is edited: none of the four is in 12.5's 16 or F5.2's 7. No allow-list entry is needed, and `tests/declared_exclusion.yaml` is untouched.

## Not done

- **Composed CI:** `composed.yml` is not on `main` yet (T029 #44, then T073 #45), so this PR's CI is `validate` and SonarCloud. The composed numbers above are local runs, quoted.

🤖 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>
brettheap and others added 3 commits October 7, 2026 00:25
Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1 and T098 landed)

Conflicts in src/openxdox/gate_console.py, tests/conftest.py,
tests/test_gate_console_schema_source.py and tests/test_host_plane.py,
all T020's and T021's files that this branch carried from their pull
requests before they landed: main's side taken for all four (main's
gate_console.py and the schema-source test equal #40's final head; main's
conftest.py and test_host_plane.py carry T026's later edits). No T029 file
conflicted.

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
brettheap added a commit that referenced this pull request Oct 7, 2026
…and the composed tree (plan 038, T094's R2-INV-R9) (#49)

Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Authored by lane openXfactory-3, delegated by lane openxfactory-4 (#656 6021862323)

Arc: neutral-product-standalone-operability

## T103: the T074 files (plan 038; T094's map R2-INV-R9, slice outline row T103)

There is no `tasks.md` entry yet: lane 4 records T094's slices after T002 (openxFactory#1251). Until then, the contract is the slice outline row plus the holder's rulings, openxFactory#656 `6021830531`.

- **Realizes:** the composed half of requirement 9 for these four declared files (T073's F9.1: each declared file runs green in the composed workflow). The means are PP, plus MN, HR and CS for `test_gate_console.py`.
- **Falsifier (the outline row):** run the four files composed, each alone. **28 reds go to 0**: completeness 2, packet 1, gate_console 1, round_trip 24. `test_gate_console.py` then reads 127 passed and 5 skipped. Its 5 skips are OQ-R9-3, which this slice respells so they run composed: it reads **132 passed, 0 skipped**.
- **Ruled:** openxFactory#656 `6021830531`:
  - OQ-R9-1 to OQ-R9-4 at their defaults;
  - W7: each of these four files' chain is **T103 → T074**, so this lands before T074.
  - The ruling it sits under is `6016982816` (ARC-Q2 (a)).
- **After:** T095 (#46, landed as `e2340bb6`), which this PR codes against: `openxfactory_root()`, `CONTRACTS_DIR` and `DECLARED_HOST_SUITES`. T021 (#40) landed as `f70bc9c3`. Both are met.

## What changed (4 test files, nothing else)

| file | nodes | means | change |
|---|---|---|---|
| `tests/test_completeness.py` | 2 | MN, PP | The purity check reads `completeness.py` at its carved home (`carved_module_path`), and `doc_health.lines` where this process imports it from. The gating bound scans this leg's import root `src/` and expects `openxdox/generator.py` and `openxdox/kickoff.py`. Its `doc_health` half scans the `doc_health` package this process imports. |
| `tests/test_round_trip.py` | 24 | PP | `_MOVER` reads openxFactory's `scripts/proposal-support.py` through `openxfactory_root()` (23 nodes). The shared-predicate pin reads `round_trip.py` at its carved home and `families.py` from `families.__file__` (1 node). |
| `tests/test_doxbench_packet.py` | 1, the rail node | PP | The status-read drift check reads openxFactory's corpus, `openxfactory_root() / "docs"`, instead of this checkout's `docs/`. |
| `tests/test_gate_console.py` | 1 collection error, and the 3 cases behind it | PP, MN, HR, CS | The module-level forward-gate load goes through `openxfactory_root()`. The two in-test `from ideation_dashboard import cli` imports become `from opendox import cli`. HR (the two cases that parse `gate`) is T095's `DECLARED_HOST_SUITES`, and CS (the receipt case) is T095's `CONTRACTS_DIR`; neither needs an edit in this file. |
| `tests/test_gate_console.py`, OQ-R9-3 | 5 skips | CS | `_contract_carries_the_new_verbs` reads each schema where the pinned validator reads it. `gate-action-record` comes from the installed distribution's packaged copy (`openxdox.contracts.verified_path`). `gate-intent` comes from the directory `CONTRACTS_DIR` names. A lone checkout has no `CONTRACTS_DIR`, so the group still skips there. Once `CONTRACTS_DIR` is supplied, a schema that cannot be read fails collection instead of reading as "not released" (Copilot r4199436303). |

Edits are kept local to the failing reads, so T074's later respelling of these files' `doc_health` imports stays clean. The two `doc_health` reads in completeness and round_trip resolve through the import system, so they follow whatever T074 makes those imports.

**Flagged, a widening beyond the failing assertion:** `test_the_scoring_modules_only_importers_are_the_generator_and_the_guard`'s second loop scanned `REPO_ROOT / "scripts" / "doc_health"`. This leg has no such directory, so since the carve the loop has scanned nothing and passed vacuously. It now scans the composed `doc_health` package, 38 modules, and passes. Mutant M2b below shows the check is live.

## The falsifier, measured

Measured in R1Q23 (a)'s composed form:
- openxFactory `92010d3e` (#44's `tests/composed_host_pin.yaml`), with `openDox`, `openXdox` and `openXwallet` initialized recursively, and its `scripts/` on `PYTHONPATH`;
- openDox installed from openDox-code `main` `84f8ed83`;
- colour variables unset, `TERM=dumb`;
- each file ALONE: `python -m pytest -q -p no:cacheprovider -rfE --junitxml=… <file>`.

The integration branches are LOCAL and never pushed:
- **base** = `main` `e2340bb6`, which carries T021 (#40) and T095 (#46), both landed;
- **head** = this PR at `683b9562`, which is `main` merged in and differs from it by exactly these four files.

Earlier runs over local integrations read the same, file for file: `main` `d45a0939` + #40 + #46 at `6aeb6797`, and `main` `f70bc9c3` + #46 `c2192e3e` at `57a970fc`.

The rail node needs T073's status-exemption rail. Without it, the node fails on `pk.STATUS_SCAN_LINES`, at the rail, before it reaches its corpus read. So the four files are also measured with #45 `26fe63e7` merged in, with T022's placement made first and removed after.

**Base + #45, 28 red:**
```
tests/test_completeness.py: exit 1; 29 passed, 2 failed, 0 errors, 0 skipped
tests/test_doxbench_packet.py: exit 1; 85 passed, 1 failed, 0 errors, 0 skipped
tests/test_gate_console.py: exit 2; 0 passed, 0 failed, 1 errors, 0 skipped
tests/test_round_trip.py: exit 1; 50 passed, 24 failed, 0 errors, 0 skipped
TOTAL over 4 files: 164 passed, 27 failed, 1 errors, 0 skipped; 28 red
```

**Head + #45, 0 red:**
```
tests/test_completeness.py: exit 0; 31 passed, 0 failed, 0 errors, 0 skipped
tests/test_doxbench_packet.py: exit 0; 86 passed, 0 failed, 0 errors, 0 skipped
tests/test_gate_console.py: exit 0; 132 passed, 0 failed, 0 errors, 0 skipped
tests/test_round_trip.py: exit 0; 74 passed, 0 failed, 0 errors, 0 skipped
TOTAL over 4 files: 323 passed, 0 failed, 0 errors, 0 skipped; 0 red
```

The 28 red at base, each with its first failure:
- `test_completeness.py`: `test_the_scoring_module_touches_nothing_outside_its_arguments` (FileNotFoundError, `scripts/ideation_dashboard/completeness.py`); `test_the_scoring_modules_only_importers_are_the_generator_and_the_guard` (`AssertionError: []`).
- `test_doxbench_packet.py::test_the_status_read_agrees_with_the_repositorys_own_corpus_reader`: "the drift check needs real documents to be a check".
- `test_gate_console.py`: a collection error (FileNotFoundError, `scripts/proposal-support.py`).
- `test_round_trip.py`:
  - `test_both_gates_agree_where_a_documents_status_header_is` ×12, `test_the_mover_agrees_with_the_other_fence_implementations` ×10 and `test_the_movers_line_split_is_the_three_real_endings_and_nothing_else`: FileNotFoundError, `scripts/proposal-support.py`;
  - `test_the_shared_predicate_is_spelled_the_same_in_all_three`: FileNotFoundError, `scripts/ideation_dashboard/round_trip.py`.

**Without #45**, base vs head reads completeness 2→0, round_trip 24→0 and gate_console 1→0 (132 passed). Packet reads 57 red at both: its 56 rail nodes and the rail node all fail at the rail, which is T073's.

The two "rejected" conformance cases are rejected by the schema (validator exit 1), not by a harness failure (exit 2). Run by hand with the farm:
- a valid `promote-to-staging` record: exit 0;
- one with no workflow job: exit 1, "2 error(s)";
- a `derive-possibles` record with no `cluster_id`: exit 1, "1 error(s)".

## Mutants of the respellings

This slice adds no guard and no fixture. Each mutant reverts or misdirects one respelled read; every one was run with that file alone, composed, over head + #45.

| id | mutation | result |
|---|---|---|
| M1 | purity check reads `generator.py`, not `completeness.py` | killed: 1 failed (`AssertionError` on the import set) |
| M2b | the `doc_health` loop scans `src/` instead | killed: 1 failed (the loop's assertion names `completeness.py`) |
| M3 | importer scan back to `REPO_ROOT / "scripts"` | killed: 1 failed (`AssertionError: []`) |
| M4 | shared-predicate pin reads `gate_console.py` | killed: 1 failed |
| M5 | rail node back to `REPO_ROOT / "docs"` | killed: 1 failed ("the drift check needs real documents to be a check") |
| M6 | conformance group: `CONTRACTS_DIR` test inverted | 127 passed, **5 skipped**: the group reverts to never running |
| M7 | conformance group reads a schema without the verbs | 127 passed, **5 skipped**: the skip condition reads the schema's content |
| M8 | **a broken farm**: a pytest plugin removes `gate-intent.schema.yaml` from the farm after the conftest builds it | at `6aeb6797`, before the fix: 127 passed, 5 skipped, so it **survived** (the masking Copilot r4199436303 named). At `57a970fc`: a collection error (`FileNotFoundError`), so it is **killed** |

## Unchanged

Base (`main` `e2340bb6`) vs head (`683b9562`), each file alone and composed: **974 nodes, 0 differ**, and no node goes from green to red anywhere.
- **12.5's 16 governed suites:** identical at both, node for node. Their remaining reds are T073's and the other slices', not this PR's.
- **F5.2's 5 protected files:** generator 45, session_snapshot 23, snapshot_determinism 6, snapshot_registry 40 and snapshot_validation_launch 9, all passed at both.
- **`test_column_contributions_governed.py`:** 9 passed at both.
- **`tests/test_declared_exclusion.py`, lone** (no `PYTHONPATH`): 207 passed at base and at head.
- None of the four files is in 12.5's computed set (16) or F5.2's (7), so no allow-list entry is needed.

## CI

T095 has landed, so this head carries `openxfactory_root` and `CONTRACTS_DIR` from `main`. Locally, in validate's lone form at `683b9562` (pinned openDox `dede32b4`, `--deselect` of LEFT_OUT): `selected=1165 passed=1161 skipped=4 failures=0 errors=0`, against the floors 1099 / 1095 / 4. `tests/test_declared_exclusion.py` run alone reads 207 passed at base and at head.

**CI at `683b9562`:**
- `validate` passed (run 37548600589): `selected=1165 passed=1161 skipped=4 failures=0 errors=0`, against the floors 1099 / 1095 / 4 (margin 66);
- SonarCloud Code Analysis passed;
- Copilot's review 5435805891 at `683b9562`: **Findings: None**. It rates the PR "Needs a closer look" only because the PR was a draft and the composed packet result depends on T073's rail, which lands separately.

**Threads:** 1 in all (r4199436303, fixed at `57a970fc`, answered and resolved), 0 unresolved.

## Not done / what remains

- Nothing is left on this PR's own After set: T021 (#40) and T095 (#46) have landed, and this branch merges `main` `e2340bb6`. If `main` moves again, the holder's loop updates the branch.
- The rail node's composed green also needs T073 (#45), which lands after this slice. Until then it is measured over a local integration with #45, as above.
- T074 later respells these files' `doc_health` imports; this PR leaves them alone.

🤖 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>
Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ilot r4201850292)

By default git hash-object applies the path's clean filters and EOL
normalization, so a filter in the spec checkout (.git/info/attributes plus a
filter.<name>.clean command, or the text attribute on a CRLF edit) turned
edited bytes back into the pinned blob. Against the script before this
commit, both cases PLACED the link. The check now runs hash-object
--no-filters, so it compares the raw bytes the placed link exposes.

tests/test_composed_placements.py adds
test_an_edit_a_clean_filter_would_hide_is_refused[clean-filter|eol-normalization].
Both cases fail before the fix and pass with it: 29 passed.
MW-hash-through-clean-filters is killed, and all 22 mutants are killed.

Refs opensoft/openxFactory#656 (CLAIMED 6013608959).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The placement guard accepts tracked symlinks whose target contents are not pinned.

Review effort: Balanced
Findings: 1 High severity

Open (1)

Comment thread scripts/composed_placements.py
brettheap and others added 2 commits October 7, 2026 09:45
…w-on landed)

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Source validation can still accept symlink-mediated content that is not securely bound to the pinned document.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (1)

Comment thread scripts/composed_placements.py Outdated
…nd T096's follow-on #56 landed)

Two files conflicted (add/add), both T022's, which this branch had carried
from #41's head 1f2d685: scripts/composed_placements.py and
tests/test_composed_placements.py. Main's side was taken for both, as it
was for .gitignore (auto-merged to main's). After this merge the tree
differs from main only in T029's two files.

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap added a commit that referenced this pull request Oct 8, 2026
Brings T101 (#48), T098 (#51), T103 (#49), T096 (#53), T100 (#54), T099
(#47), T021's follow-on (#55), T102 (#52), T022 (#41, landed as aefd109),
T097 (#50) and T096's follow-on (#56).

Conflicted: scripts/composed_placements.py and tests/test_composed_placements.py,
both T022's files, which this branch carried from #41 through #44. Main's
landed side is kept for both. tests/declared_exclusion.yaml merged clean
(T101's entry edits beside T073's header and open_until).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@brettheap
brettheap requested a balanced review from Copilot October 8, 2026 16:22
@sonarqubecloud

sonarqubecloud Bot commented Oct 8, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The draft’s composed suite remains red pending T022 and T028 landing and integration.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

@brettheap

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-08T19:42:52.673884Z 9a3b8ed Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 9a3b8ed113

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants