Skip to content

DRAFT: T073: the composed declarations (ARC-Q2 (a)): every declared entry runs composed, the rail files get the host's rail (plan 038) - #45

Draft
brettheap wants to merge 19 commits into
mainfrom
r2/t073-composed-declarations
Draft

brettheap wants to merge 19 commits into
mainfrom
r2/t073-composed-declarations

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 6016678145)

Arc: neutral-product-standalone-operability

T073 [US3] [oXc]: the composed declarations (ARC-Q2 (a))

This is plan 038's T073, in openxFactory's specs/038-opendox-document-tool-self-maintenance/tasks.md (opensoft/openxFactory#1245).

This PR is a DRAFT. Its After set, as of head 81796156:

The landed batch Q text of F9.1 is identical to the text this PR implements against (94d6e7c7), as diffed. Its points are met as follows:

  • every file of the declaration is a declared integration test;
  • each entry keeps its reason, and the declaration keeps its count;
  • the declaration names the commit by naming tests/composed_host_pin.yaml;
  • the workflow runs every entry (steps 5 and 5a);
  • the three checks stand;
  • the lone run reports under batch B's title line.

It is branched from #44, which carries T029 until it lands. #44's newer heads and main are merged in, never rebased. The latest merge brought main 9720925e in at 81796156. It conflicted only in T022's two files, scripts/composed_placements.py and tests/test_composed_placements.py, which this branch carried from #41 through #44, and it kept main's landed side for both. tests/declared_exclusion.yaml merged clean, with T101's entry edits beside T073's header and open_until. This PR's own commits touch only T073's five files:

What changes

  • tests/declared_exclusion.yaml (first in its chain, then T074):

    • A header section declares EVERY entry a declared integration test. .github/workflows/composed.yml runs it, composed, at the ONE openxFactory commit tests/composed_host_pin.yaml names: 12.5's governed suites in step 5, every other entry in step 5a, each file alone. By that declaration requirement 9 closes for openXdox-code (batch Q). A lone run still reports the declaration under batch B's title line.

    • The count (66), every entry, and each reason's id, reason and ruled are byte-identical, checked against DRAFT: T029, U-9: the composed run, permanent: composed.yml and its openxFactory pin (plan 038) #44's file.

    • open_until, as ruled:

      • doc_health keeps its words and gains "; run composed, as a declared integration test, by .github/workflows/composed.yml at the openxFactory commit tests/composed_host_pin.yaml names (ARC-Q2 (a), openxFactory#656 comment 6003918488)";
      • status-exemption-rail and openxfactory-contracts read "never in a lone checkout: a declared integration test, run composed by .github/workflows/composed.yml at the openxFactory commit tests/composed_host_pin.yaml names (ARC-Q2 (a), openxFactory#656 comment 6003918488)".

      Every lone run prints these.

    • No commit id is written outside the pin file, so a pin advance (T030, T064, T075) needs no edit here.

  • tests/conftest.py (third: T020 → T021 → T073 → T074). One self-contained block, appended after T095's composed harness, so this branch's diff against main is a pure append there. No line of T020, T021, T026 or T095 is touched, except the one fix below that the holder assigned to T073.

    • The fix for Copilot r4201411760 on T095: the composed harness for the declared integration tests (plan 038, T094's R2-INV-R9) #46 (holder r4201421102). T095's contracts farm (build_contracts_farm) skipped every opendox-snapshot-* example whichever owner kept it. It now skips only openDox-spec's (CONTRACT_EXAMPLE_LEFT_OUT_OWNER = "opendox_spec"), so a same-prefix example from openxFactory or openXdox-spec is linked like any other. As the holder measured, none exists today, so the composed farm is unchanged.
    • Where the run is composed, each test of a file the declaration names under status-exemption-rail runs with the rail openxFactory's host registers. That rail is the register_status_exemption row of opendox_host.seams(), the table the host's process start registers from, read off it rather than named.
    • It is registered for the test and taken back after it, as U-1 registers the governed host. Where the run is not composed, nothing is registered, so the declaration's lone evidence is unchanged.
  • .github/workflows/composed.yml (second: T029 → T073 → T074). Step 5a runs every declared entry outside 12.5's governed set, composed in step 5's form and each file alone, and tabulates the results as step 5 does. It refuses an entry that has a report from neither step ("dropped from both suites"). The header's list gains 5a. Nothing else of T029's is changed.

  • tests/test_host_plane.py (the fifth file, one case): test_the_farm_leaves_out_only_opendox_specs_snapshot_examples, beside T095's farm cases. A same-prefix example from openxFactory and one from openXdox-spec are linked, and openDox-spec's two are not.

  • tests/test_declared_rail_registration.py (new, the fourth file, holder ruling (a) on question 6020683941; fix-now for Copilot r4197793290). It holds 5 cases with stand-in rails, so they run in the lone required check:

    • an earlier host rail is dropped for the block and put back after;
    • no rail is left behind where none was registered;
    • openDox's own default is not put back;
    • the seam is put back when the block fails;
    • the rail suites are the declaration's rail entries.

    Each case leaves openDox's seam as it found it.

Where the two lists come from

  • R1Q24's 3 rail files are tests/test_doxbench_abstract_envelope.py, tests/test_doxbench_packet.py and tests/test_doxbench_turns.py. Sources:
    • plan 034's clarify-questions.md:920-926 (R1Q24 (a), ruled 5850003126);
    • the staged topic ideation/staging/doc-health-direction-arc/doc-health-direction-arc.md:180-187;
    • the declaration's own status-exemption-rail entries.
  • The 5 contracts files are tests/test_doxbench_blank_reason.py, tests/test_project_action_contracts.py, tests/test_project_schema_election.py, tests/test_validate_ideation_dashboard_contracts.py and tests/test_wheel_action_contracts.py. Sources:
    • plan 034's clarify-questions.md:929-944: R1Q24's four, and the eighth file T006 found;
    • the staged topic :188-196;
    • the declaration's openxfactory-contracts entries.

(openxFactory at 92010d3e.)

The falsifier, measured

Environment. openXdox-code at this branch, openDox-code dede32b4 installed by CI's command, openxFactory 92010d3e, which tests/composed_host_pin.yaml names, with openDox, openXdox and openXwallet initialized recursively. The colour variables were unset, TERM=dumb, and TMPDIR was short.

At head 81796156 (after merging main 9720925e):

  • lone CI form: selected 1201, passed 1197, skipped 4, 0 failed, both locally and in CI's validate (run 37808162995). That meets the pins (≥ 1099, ≥ 1095, == 4).
  • composed, in CI (runs 37808162815 and 37808186218, which agree):
    • step 5 reads 821 passed, 17 failed, 4 errors: 21 red, unchanged from cebf3452;
    • step 5a reads 1950 passed, 2 failed, 7 errors, 2 skipped: 9 red, down from 324;
    • no entry is NOT RUN by either step;
    • the oracle reads ok: 8 protected edit(s), each entered and holding, and the help tree passes.
  • composed, locally, each file alone:
    • the three rail files read 57, 86 and 183 passed, so the carve-residue node below is gone;
    • the four contracts files read 11, 13, 33 and 18 passed;
    • tests/test_declared_rail_registration.py reads 5 passed, and tests/test_host_plane.py 29;
    • the five files that are red in CI reproduce locally with the same counts.
  • The 30 reds, none in T073's files:
    • 26 are node-harness ERR_MODULE_NOT_FOUND: each test copies openDox's staging-workbench-model.js (or model.js) alone into a temporary directory, and the copy cannot find the ./display.js it imports;
    • 3 assert on the same workbench module or its view (test_staging_workbench_view_has_no_write_path, the pure model "must stay import-free", and test_only_the_plane_postures_still_stand_as_an_inline_note);
    • 1 is tests/test_renderer.py::test_every_constructed_doxbench_class_has_a_styles_rule, where doxchat-no-model and doxchat-untrusted have no styles.css rule.
  • Where the reds sit:
    • step 5's 21 are all tests/test_staging_workbench.py, which is T023 to T028's;
    • step 5a's 9 are tests/test_doxbench_save.py (3), tests/test_doxbench_transport.py (4), tests/test_renderer.py (1) and tests/test_session_document_ownership.py (1). They remain after T095 to T104 landed, so they are with the holder for routing (lane 3's log, 2026-10-08).

At head cebf3452 (after merging main e2340bb6):

  • lone CI form: selected 1185, passed 1181, skipped 4, 0 failed, both locally and in CI's validate. That meets the pins (≥ 1099, ≥ 1095, == 4).
  • composed, in CI: step 5 reads 821 passed, 17 failed, 4 errors: 21 red, and step 5a reads 1476 passed, 245 failed, 79 errors, 3 skipped: 324 red. The oracle reads ok: 8 protected edit(s), each entered and holding, and the help tree passes. T095 and the landed 12.5 repairs moved both counts down, and the rest belong to the open slices.
  • composed, locally: the three rail files read 325 passed and 1 failed (the mapped carve-residue node). tests/test_declared_rail_registration.py and tests/test_host_plane.py are green.

The measurements below were taken at the earlier heads they name.

F9.1 (lone), in validate's form, python -m pytest -q --deselect "$LEFT_OUT":

1121 passed, 4 skipped, 1 deselected in 80.43s (0:01:20)
triple: selected=1125 passed=1121 skipped=4 failures=0 errors=0
=================== open extraction: the declared exclusion ====================
declared exclusion: 66 files, listed in tests/declared_exclusion.yaml and left out of this run. It is an OPEN extraction: each file runs again once its reason is cleared.

The run meets validate's pins (selected ≥ 1099, passed ≥ 1095, skipped == 4). tests/test_declared_exclusion.py, inside that run, still holds each listed file to its reason, alone.

F9.1's own command block (a fresh mktemp -d venv, pip install ".[test]", batch J's --deselect, and the two validate.yml assertions), at bf8f5b74, whose tree b09c1384 equals byte for byte:

1121 passed, 4 skipped, 1 deselected in 75.24s (0:01:15)
F9.1 EXIT 0

The composed workflow, run locally step by step as the runner runs it, at the same openxFactory pin as #44, and at #44's head for comparison:

step #44 (local) this PR (local) this PR, CI at b09c1384
5, 12.5's 16 governed suites 760 passed, 78 failed, 4 errors: 82 red the same 82 red nodes, node for node (diffed) the same 82, node for node
5a, the declaration's other 51 entries (no such step) 1356 passed, 366 failed, 79 errors, 2 skipped: 445 red 1357 passed, 364 failed, 79 errors, 3 skipped: 443 red
6, the oracle ok: 3 protected edit(s), each entered and holding the same the same (10 arc landings since ARC_BASE)
7, the help tree 1 passed the same the same

CI's 443 are the local 445 less two nodes that depend on the environment:

  • tests/test_repo_root_guard.py::test_the_refusal_names_the_resolved_path_what_it_wanted_and_a_correct_shape trips locally on the local checkout's path;
  • tests/test_wheel_model.py::test_landed_parses_a_real_archived_change_delta is a node-harness failure locally, and CI reads one more skip.

CI is the authority for T094's map.

So the composed workflow is not yet green, as the holder's ruling foresaw. Step 5a's 445 are exactly the reds lane 3 mapped in question 6016925160:

class red
the rail files' one carve-residue node, tests/test_doxbench_packet.py::test_the_status_read_agrees_with_the_repositorys_own_corpus_reader (assert 2 > 10: it reads this checkout's docs/) 1
the 5 contracts files (they take Path(__file__).parents[2] for openxFactory's root, which no composed placement serves) 75
33 of the 43 other doc_health files: carve residue (node harness ERR_MODULE_NOT_FOUND 138, pre-carve paths, moved names, unregistered routes) 369

T094 (R2-INV-R9) maps them, and T095 onward repair them, both in lane 3. Step 5 is T023 to T028's.

The rail, red first and then green (the 3 rail files, each alone):

run result
composed, without this block (#44's head) 236 passed, 90 failed; all 90 are StatusExemptionNotRegistered or the AttributeError raised from it
composed, with it 325 passed, 1 failed; the one is the carve-residue node above, a doc_health case when the file runs alone
lone, with it 236 passed, 90 failed, every one the rail's refusal, unchanged

So the declaration's lone evidence still holds.

Mutants

Every mutant was run against the step or block it targets. Each one stays valid code, and each is killed.

Step 5a: 7 of 7 killed. The step's two Python blocks were extracted from this workflow and driven against crafted reports. As written, the split and 5 cases are right. The mutants:

  • S1, the split keeps the governed entries;
  • S2, the split keeps only the governed entries;
  • M1, no check that every entry was run, killed by "an entry with no report from either step" and "a governed entry whose step-5 report is missing";
  • M2, only step 5a's reports count as run;
  • M3, the step exits 0 whatever it read;
  • M4, a failed case is not red;
  • M5, a collection error is not red.

The tests/conftest.py rail block: 7 of 7 killed. These ran in a throwaway worktree. Two probe test files were used there only, never committed.

mutant killed by
C1, never registers the composed rail file
C2, the list is read from the wrong reason the composed rail file; tests/test_declared_rail_registration.py (lone)
C3, the wrong row of the host's seam table the composed rail file: 57 errors
C4, a rail registered in a lone checkout too tests/test_declared_exclusion.py's alone check for the rail entry
C5, the rail is not taken back after the test tests/test_declared_rail_registration.py (lone), committed at 26fe63e7
C6, a host's earlier rail is not put back tests/test_declared_rail_registration.py (lone), committed at 26fe63e7
C7, the earlier rail is not dropped before the test tests/test_declared_rail_registration.py (lone), committed at 26fe63e7

The farm fix (r4201411760): red first, then green, 3 of 3 mutants killed. In tests/test_host_plane.py -k farm:

  • main's owner-wide line (M0) turns the new case red;
  • scoping the exclusion to the wrong owner (M1) is killed by both farm cases;
  • dropping the exclusion (M2) is killed by both farm cases;
  • as written, 5 passed.

Re-run at 81796156: M0 turns 1 case red, M1 2 and M2 2. Restored, it reads 5 passed.

C1, re-run at 81796156 (the fixture's rail set to none), turns the three composed rail files red: tests/test_doxbench_packet.py 57 failed, tests/test_doxbench_turns.py 32 and tests/test_doxbench_abstract_envelope.py 1. As written, they are green.

So all 7 are killed by committed tests in CI: C1 and C3 by the composed run of the rail files, C4 by tests/test_declared_exclusion.py, and C2, C5, C6 and C7 by tests/test_declared_rail_registration.py in the lone required check. Measured in a throwaway worktree, the new file kills C2, C5, C6 and C7, and passes as written: 5 passed, both lone and composed.

CI and Copilot at the head

CI at 81796156:

  • validate: success (run 37808162995), triple: selected=1201 passed=1197 skipped=4 failures=0 errors=0;
  • SonarCloud Code Analysis: success;
  • copilot-pull-request-reviewer: success;
  • composed: failure (runs 37808162815 and 37808186218): step 5 21 red, step 5a 9 red, both as measured above. The oracle reads ok: 8 protected edit(s), and the help tree passes.

Copilot at 81796156: review 5459767763, COMMENTED, Needs a closer look, 0 open findings. Every earlier thread stays resolved:

3 threads, 0 unresolved.

What is NOT done

  • The composed workflow green. At 81796156 it waits for step 5's 21 (T023 to T028's, with T028 open), for step 5a's 9, which are with the holder for routing, and for T029 (DRAFT: T029, U-9: the composed run, permanent: composed.yml and its openxFactory pin (plan 038) #44). This PR stays a DRAFT until it is told otherwise, as ruled.
  • The re-runs after each tests/composed_host_pin.yaml advance (T030, T064 and T075) are later work.
  • tests/test_dependency_direction.py::test_first_attribute_access_refuses_and_names_the_layering, the known pre-existing composed red, is not a declared entry. So it is outside T073's declared set, and neither step runs it. It is T074's, in the direction arc.
  • .gitignore and T022's scripts/composed_placements.py are not T073's files, and nothing is placed for the contracts files. A scratch placement, removed afterwards, left 60 of their 75 red, so their repair is T094's to map.

🤖 Generated with Claude Code

brettheap and others added 14 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>
…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>
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>
… composed, and the rail files get the host's rail (plan 038)

Batch Q's amendment of #1144's F9.1 (openxFactory#1248; ARC-Q2 (a),
openxFactory#656 comment 6003918488) makes every file of the declared
exclusion a DECLARED INTEGRATION TEST that the composed workflow runs.

- tests/declared_exclusion.yaml: the header declares every entry a
  declared integration test, run by .github/workflows/composed.yml at the
  ONE openxFactory commit tests/composed_host_pin.yaml names. The count,
  every entry, and each reason's id, reason and ruled are unchanged. Each
  open_until gains one clause naming that composed home, so every lone run
  prints it. No commit id is written outside the pin file, so a pin
  advance needs no edit here.
- tests/conftest.py: one block after T021's. Where the run is composed,
  each test of a file the declaration names under status-exemption-rail
  (R1Q24 (a), comment 5850003126) runs with the rail openxFactory's host
  registers. That rail is read off the register_status_exemption row of
  opendox_host.seams(), registered for the test and taken back after it,
  as U-1 registers the governed host. A lone checkout registers nothing,
  so the declaration's lone evidence is unchanged. No T020, T021 or T026
  line is touched.
- .github/workflows/composed.yml: step 5a runs every entry outside 12.5's
  governed set, composed in step 5's form and each alone. It refuses an
  entry that has a report from neither step.

Refs opensoft/openxFactory#656 (CLAIMED 6016678145; QUESTION 6016925160).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…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>
…openxFactory#656 6016982816)

Under ARC-Q2 (a) the doc_health direction arc does not clear the
status-exemption-rail and openxfactory-contracts reasons, so their
open_until drops "the doc_health direction arc (plan 034 T008)" and
reads, as ruled: "never in a lone checkout: a declared integration test,
run composed by .github/workflows/composed.yml at the openxFactory
commit tests/composed_host_pin.yaml names (ARC-Q2 (a), openxFactory#656
comment 6003918488)". The doc_health reason keeps its words and the
clause. id, reason and ruled stay byte-identical, and no commit id is
written outside the pin file.

Refs opensoft/openxFactory#656 (QUESTION 6016925160, RULED 6016982816).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings T029's two commits since 570efda: an edited pull request body is
judged again (Copilot r4195546545), and 12.5's oracle call takes --chains,
as Brett ruled (openxFactory#656 6016648451).

Lane: openXfactory-3

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

sourcery-ai Bot commented Oct 6, 2026

Copy link
Copy Markdown

Reviewer's Guide

This draft establishes the composed execution infrastructure for T073: CI checks out a pinned openxFactory host, safely supplies files and host seams, runs every declared exclusion alongside the governed suites, and relocates contract-schema lookup to explicit package or host ownership while preserving lone-checkout behavior.

Sequence diagram for composed declared integration tests

sequenceDiagram
    participant CI as composed.yml
    participant Decl as declared_exclusion.yaml
    participant Host as openxFactory host
    participant Conf as tests/conftest.py
    participant Pytest as pytest

    CI->>Decl: Read declared entries
    CI->>Host: Checkout pinned commit and initialize submodules
    CI->>Conf: Run composed test session
    Conf->>Host: Read register_status_exemption from opendox_host.seams()
    Conf->>Conf: Register host rail for the test
    CI->>Pytest: Run each non-governed declared file alone
    Pytest-->>CI: Write per-file report
    Conf->>Conf: Unregister host rail after the test
    CI->>CI: Refuse entries without a report from either suite
Loading

Sequence diagram for host-owned contract schema validation

sequenceDiagram
    participant Host as Host process
    participant Gate as openxdox.gate_console
    participant Schema as Host contract schema directory
    participant Validator as JSON Schema validator

    Host->>Gate: register_contract_schema_source(source)
    Gate->>Schema: Check demotion-execution-receipt.schema.yaml
    Schema-->>Gate: Registered schema source
    Host->>Gate: validate_demotion_execution_receipt(receipt)
    Gate->>Schema: Read host schema text
    Schema-->>Gate: YAML schema text
    Gate->>Validator: _schema_errors(receipt, schema_text)
    Validator-->>Gate: Validation result
    Gate-->>Host: Success or GateRefused
Loading

File-Level Changes

Change Details Files
Adds a permanent composed CI workflow that pins and assembles an openxFactory host, runs governed suites plus every declared exclusion, and reports missing or failing entries.
  • Validate a strict host-pin manifest and initialize the required public submodules at that commit.
  • Place pinned openDox-spec files into the composed tree using guarded symlinks.
  • Run governed and declared suites independently with JUnit aggregation, then execute the oracle and help-tree checks.
.github/workflows/composed.yml
tests/composed_host_pin.yaml
scripts/composed_placements.py
tests/test_composed_placements.py
.gitignore
Introduces host-aware test fixtures so composed runs use openxFactory's registered host behavior while lone runs retain stand-ins and isolation.
  • Register and restore governed host profiles, extension columns, and process import paths around each test.
  • Derive and register the status-exemption rail only for declaration-listed suites during composed runs.
  • Register the host contract-schema source at startup and preserve packaged-schema validation for consumer-owned contracts.
tests/conftest.py
tests/test_host_plane.py
tests/test_gate_console_schema_source.py
Moves contract-schema validation from checkout-relative paths to explicit package-owned or host-registered schema sources.
  • Read gate-action schemas through verified packaged contract copies.
  • Require hosts to register a validated demotion-receipt schema directory and reject conflicting registrations.
  • Normalize malformed, invalid, or unresolvable schemas into GateRefused errors.
src/openxdox/gate_console.py
Declares all excluded entries as composed integration tests while preserving lone-checkout exclusion evidence and documenting the composed host pin.
  • Add composed execution and open-until wording to the declaration metadata.
  • Run the declaration's non-governed files individually in the composed workflow and reject entries absent from both suites.
  • Keep declaration counts and entry reasons intact.
tests/declared_exclusion.yaml

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 placement guard can incorrectly accept a global Git exclusion instead of the repository’s required .gitignore entry, and the draft composed workflow remains red.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds pinned, composed openxFactory test execution for declared integration suites and supporting host seams.

Changes:

  • Runs every declared exclusion in composed CI.
  • Registers governed host, schema, and status-exemption seams.
  • Adds pinned runbook placement and comprehensive tests.
File Description
.github/​workflows/​composed.yml Adds composed test workflow.
.gitignore Ignores the placed runbook link.
scripts/​composed_placements.py Safely places composed-tree files.
src/​openxdox/​gate_console.py Resolves schemas from their owners.
tests/​composed_host_pin.yaml Pins the openxFactory host.
tests/​conftest.py Registers composed host seams.
tests/​declared_exclusion.yaml Declares composed execution for exclusions.
tests/​test_composed_placements.py Tests placement safeguards.
tests/​test_gate_console_schema_source.py Tests schema-source behavior.
tests/​test_host_plane.py Tests governed-host registration.

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

Comment thread scripts/composed_placements.py Outdated
T020 landed by squash with the tree of c52900e, which this branch already
carries. tests/conftest.py conflicted only where T020's identical lines sit
beside T021's and T073's, so this branch's file is kept. Its T020 lines
equal main's byte for byte.

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

Rail cleanup lacks committed regression coverage, and the composed check remains intentionally red pending prerequisite repairs.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Comment thread tests/conftest.py
…_registration.py (Copilot r4197793290; holder ruling (a))

Copilot found that nothing committed exercised conftest.a_hosts_rail's
put-back: the mutants that drop the take-back, skip the restore of a
host's earlier rail, or skip the drop before the test (C5 to C7) died only
under uncommitted probes. Five cases with stand-in rails, run in the lone
required check:
- an earlier host rail is dropped for the block and put back after;
- no rail is left behind where none was registered;
- openDox's own default is not put back;
- the seam is put back when the block fails;
- the rail suites are the declaration's rail entries.

They kill C2, C5, C6 and C7. C1 and C3 die in the composed run and C4 in
tests/test_declared_exclusion.py, so all seven are killed by committed
tests. Each case leaves the seam as it found it. The holder added this file
to T073's Files (openxFactory#656, question 6020683941, option (a)).

Refs opensoft/openxFactory#656 (QUESTION 6020683941).

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

The draft’s composed check remains intentionally red across hundreds of nodes, and its description no longer matches the committed test scope.

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

Open (2)

Comment thread tests/test_declared_rail_registration.py
brettheap added a commit that referenced this pull request Oct 6, 2026
…nly by a local exclude (Copilot r4196244480; holder ruling 6026275158)

git check-ignore honours .git/info/exclude and core.excludesFile, so the
guard passed on a machine whose repository .gitignore no longer named the
placement, and the placed link could then be committed from another
checkout. The holder ruled the fix into T022, which owns the script
(opensoft/openxFactory#656, comment 6026275158, item 2); Copilot raised it
on #45, which stacks on this file.

The guard now reads git check-ignore -v, which prints the deciding pattern
as <source>:<line>:<pattern><TAB><path>, with the source relative to the
repository root. It requires that source to be .gitignore. It also refuses
a negated pattern: -v exits 0 for a "!" pattern although the path is not
ignored, which the old -q exit status had covered.

tests/test_composed_placements.py adds four cases:
  * a destination only .git/info/exclude names: refused;
  * a destination only core.excludesFile names: refused;
  * a local exclude beside the .gitignore line still places, because the
    root .gitignore outranks it and stays the source;
  * a .gitignore that negates the destination: refused.
Against the script before this commit, the two local-exclude cases fail
(2 failed, 16 passed). With it, 18 passed. All 17 mutants are killed, each
by its own case, including MP-any-ignore-source (the dropped source check)
and MQ-a-negation-counts-as-ignored.

Refs opensoft/openxFactory#656 (CLAIMED 6013608959).

Lane: openXfactory-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap and others added 2 commits October 6, 2026 23:36
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>
…hot-* examples (Copilot r4201411760 on #46; holder r4201421102)

T095's farm (build_contracts_farm) skipped every example named
opendox-snapshot-* whichever owner kept it, while the farm's own contract
leaves out only openDox's. The condition is now scoped to the opendox_spec
owner (CONTRACT_EXAMPLE_LEFT_OUT_OWNER), so a same-prefix example kept by
openxFactory or openXdox-spec is linked like any other. As the holder
measured, none exists today, so the composed farm is unchanged.

tests/test_host_plane.py gains one farm case beside T095's: a same-prefix
example from openxFactory and one from openXdox-spec are linked, and
openDox-spec's two are not. It is red on the owner-wide condition and green
on the scoped one. It also kills the mutants that scope the exclusion to
the wrong owner or drop it, the latter beside T095's existing farm case.

The fix rides T073 as the holder ruled (r4201421102, under
openxFactory#656 5988818366). T073's files gain tests/test_host_plane.py
for this one case.

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 stacked dependencies and an intentionally failing composed integration workflow.

Review effort: Balanced
Findings: None

Resolved since last review (2)

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 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>
brettheap added a commit that referenced this pull request Oct 7, 2026
…thing vendored (plan 038, P4F-3) (#41)

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

Arc: neutral-product-standalone-operability

## T022, U-3: the runbook placed from the composed tree (RP) (plan 038, feature `038-opendox-document-tool-self-maintenance`)

After set met: T004 #1245 → 260234b5; T020 #39 → c6d15b2; T021 #40 → f70bc9c.

**The branch.** The head is `57b4f80a`, GitHub's update-branch merge of `main` `e7815ac5`. T022's last commit before it is `1f592766`, the merge of `main` `da736090`, and T022's three files are byte-identical between the two. The diff against `main` is T022's three files.
- T022's own commits:
  - `c40af91b`: U-3;
  - `7a149b70`: the committed tests, under the holder's Files ruling;
  - `dc879beb`, `146ae923`, `74f3a4fb`, `1f2d6858` and `2c86dcca`: the review fixes below.
- Main came in by merges, never a rebase: `42d7ea97` (`f70bc9c3`), `c22b5df7` (`36bbef79`), `49c1026d` (`e2340bb6`), `214f3613` (`fc1115d6`), `60c56a47` (`6f259156`) and `1f592766` (`da736090`).
- At `42d7ea97` this branch still carried T021's older `ecf0ba9b` versions of `src/openxdox/gate_console.py` and `tests/test_gate_console_schema_source.py`. Both conflicts were resolved to main's side, since neither file is T022's.

### What changes

`tests/test_session_runbook.py` (its module constant `RUNBOOK`) and `tests/test_session_notebook.py` read `docs/ideation-dashboard-session-runbook.md` at the checkout root. The carve sent the runbook to openDox-spec, so this checkout has none, and both suites are protected.

- **New `scripts/composed_placements.py`** links the runbook, for a composed run, from the composed tree into `docs/`. The source is `openDox/spec/docs/ideation-dashboard-session-runbook.md` under the openxFactory checkout.

  ```
  python3 scripts/composed_placements.py --openxfactory "$OPENXFACTORY"            # place
  python3 scripts/composed_placements.py --openxfactory "$OPENXFACTORY" --remove   # take back
  ```

  Before it places anything, it refuses (exit 2), naming the remedy, unless all of these hold:
  - **Each leg is its own work tree.** The openDox root and the spec leg each have their own `.git`, and `git rev-parse --show-toplevel` resolves to the directory itself. So no enclosing repository answers for them, and no `core.worktree` points the checks elsewhere.
  - **The spec leg is at the pin.** Its HEAD is the commit the root's `spec` gitlink pins.
  - **The bytes are the pinned bytes.** The runbook is tracked, no component from the spec leg down to the file is a symbolic link, and its raw bytes are the pinned blob: `git hash-object --no-filters` equals `HEAD:<path>`. So no index flag, clean filter or EOL normalization can hide an edit.
  - **The destination is ignored by the repository-root `.gitignore` itself**, read through `git check-ignore -v`. A local exclude alone (`.git/info/exclude`, `core.excludesFile`) is refused, and so is a negated pattern.
  - **Nothing is overwritten.** Anything already at the destination must be the script's own link to that same source.

  Every git call drops the ambient repository variables (`GIT_DIR`, `GIT_WORK_TREE`, `GIT_INDEX_FILE` and kin, as `openxdox.domain_corpus_adapter` drops them), so a hook cannot redirect the checks. The script places all or nothing. `--remove` takes back its own links and nothing else. It needs only the standard library and `git`.
- **`.gitignore`** names `/docs/ideation-dashboard-session-runbook.md`, so the checkout stays clean and nothing placed can be committed.
- **New `tests/test_composed_placements.py`**, 30 cases, holds those guards in the suite CI runs. Each case runs the script in a child interpreter: a copy of this checkout's script, inside a scratch checkout that carries this checkout's own `.gitignore`. It runs against a scratch openxFactory/openDox/spec tree pinned to commit A, so no case writes into this checkout. No case skips, and the file carries none of 12.5's governed-suite markers.
- **Nothing is vendored.** `src/openxdox/contracts/copies.yaml` is untouched (P4F-3; ADV-10).

The composed workflow (T029) and the documented local composed run call the script. Neither is in this PR.

### The contract, from `tasks.md`

- **Realizes**: 12.5 (part).
- **Falsifier**: the 10 nodes, composed, quoted.
- **Ruled**: R2Q8. **Decisions**: P4F-3 ("The runbook is PLACED from the composed tree (`openDox/spec/docs/…` at the root's pinned spec commit) by T022's script, git-ignored; nothing vendored."), ruled with plan 038 by Brett Heap (#656 `6013547504`).
- **After**: T021, met as above.
- **Files**: new `scripts/composed_placements.py` and `.gitignore`.
  - **Amended** by the holder's ruling on opensoft/openxFactory#656, comment `6016356225`, option (a): add the new `tests/test_composed_placements.py`.
  - The `check-ignore` source fix (Copilot r4196244480, raised on #45) is ruled into this PR by #656 `6026275158`, item 2.
  - Nothing else is changed.

### The falsifier, composed

**Environment.**
- openxFactory `51456835`, with its submodules initialized recursively and `scripts/` on `PYTHONPATH` (R1Q23 (a)).
- The openDox root `e1e3a3c3` pins openDox-spec `f7ee3c76`, the spec checkout's HEAD.
- openDox-code `34c20641`, force-reinstalled with `--no-deps`.
- Python 3.12.3, pytest 8.4.2, `LANG=C.UTF-8`, no colour forcing.

At the head, the runbook is placed, then removed afterwards, which leaves the checkout clean:

```
python3 scripts/composed_placements.py --openxfactory "$OPENXFACTORY"
PYTHONPATH="$OPENXFACTORY/scripts" python -m pytest -q -p no:cacheprovider -rA <the 10 runbook nodes, one process per suite file>
```

| at | the 10 runbook nodes |
|---|---|
| main `56e1c238` (first round) | `9 failed` + `1 failed` |
| main `f70bc9c3`, `36bbef79`, `6f259156` | "PASSED nodes: 0 / FAILED-or-ERROR nodes: 10" |
| **head `1f592766`, placed** | **"PASSED nodes: 10 / FAILED-or-ERROR nodes: 0"** |

The cause at main is `FileNotFoundError: … '<checkout>/docs/ideation-dashboard-session-runbook.md'`.

The 10 nodes:
- `test_session_runbook.py`:
  - `test_the_runbook_start_command_parses_with_the_real_cli_parser`
  - `test_the_runbook_names_the_two_required_flags_and_what_they_are_for`
  - `test_the_runbook_claims_no_flag_generate_and_open_does_not_have`
  - `test_the_runbook_says_what_a_wrong_repo_root_does`
  - `test_the_runbook_does_not_call_the_unvalidated_snapshot_routine`
  - `test_the_runbook_lists_the_posture_readings_the_page_can_produce`
  - `test_the_derived_artifact_inventory_names_every_container_the_code_writes`
  - `test_the_runbook_names_the_pull_the_refusal_names`
  - `test_the_runbook_states_what_a_session_may_rewrite`
- `test_session_notebook.py`:
  - `test_the_runbook_names_the_bound_by_its_production_constants`

**The 16 suites, each alone (first round, at `c40af91b`/`7a149b70` against main `56e1c238`):**
- 82 red of R2-INV-P4F's 174;
- 0 regressions;
- node for node the same at `7a149b70`.

T022 changes no code under `src/`. The only change it makes to a composed run is the placement.

### The script's guards: 30 cases and 23 mutants

`tests/test_composed_placements.py` reads `30 passed` at `1f592766`. Each later finding's cases were red against the script before its fix; the review rounds below quote them.

All 23 mutants were applied to this checkout's script or `.gitignore`, which the cases copy, and every one is killed by the case that names its guard:

| mutant | killed by |
|---|---|
| MA no pin comparison | `test_a_spec_checkout_off_the_opendox_roots_pin_is_refused` |
| MB content not compared | `test_a_modified_runbook_is_refused` |
| MW hash through clean filters | `test_an_edit_a_clean_filter_would_hide_is_refused[clean-filter]` |
| MS no link check | `test_a_runbook_the_pinned_commit_tracks_as_a_link_is_refused` |
| MX only the last component | `test_a_runbook_reached_through_a_linked_directory_is_refused` |
| MU root may be any repository | `test_an_opendox_root_answered_by_an_enclosing_repository_is_refused` |
| MV toplevel not compared | `test_a_spec_leg_whose_work_tree_is_configured_elsewhere_is_refused` |
| MO no pinned-root check | `test_an_opendox_root_without_a_spec_gitlink_is_refused` |
| MI spec leg is any directory | `test_an_uninitialized_spec_leg_is_refused_as_not_checked_out` |
| MC no tracked check | `test_a_runbook_the_spec_does_not_track_is_refused` |
| MD no ignore check | `test_a_destination_the_gitignore_does_not_name_is_refused` |
| MP any ignore source | `test_a_destination_only_a_local_exclude_names_is_refused[info/exclude]` |
| MQ a negation counts as ignored | `test_a_gitignore_that_negates_the_destination_is_refused` |
| MJ `.gitignore` drops the runbook | `test_a_pinned_unmodified_runbook_is_linked_and_leaves_the_checkout_clean` |
| MR ambient git variables kept | `test_ambient_git_repository_variables_do_not_redirect_the_checks[clean]` |
| ME overwrite a foreign file | `test_a_file_at_the_destination_is_refused_and_kept[place]` |
| MF remove any file | `test_a_file_at_the_destination_is_refused_and_kept[remove]` |
| MK place accepts any link | `test_a_link_elsewhere_at_the_destination_is_refused_and_kept[place]` |
| ML remove takes any link | `test_a_link_elsewhere_at_the_destination_is_refused_and_kept[remove]` |
| MM remove unlinks nothing | `test_remove_takes_back_its_own_link_and_nothing_else` |
| MN no already-placed short circuit | `test_placing_again_is_a_no_op` |
| MG no git crashes | `test_without_git_on_path_it_refuses` |
| MH place before checking all | `test_a_modified_runbook_is_refused` |

```
mutants: 23/23 killed; files restored
```

### 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 `1f592766` with nothing placed: `selected=1195 passed=1191 skipped=4 failures=0 errors=0`. All three pins hold.
- **The governed set is unchanged at 16.**
- **No protected file is edited.** T059's oracle, as 12.5 calls it, over a simulated squash of `1f592766`'s tree onto `da736090`, which touches T022's three files: "ok: 0 protected edit(s), each entered and holding".

### Copilot, by round

- **At `c40af91b`:**
  - **r4194596249**, "Translate schema validation errors into GateRefused" (T021's code, carried then): fix-now, fixed in T021's `ecf0ba9b`, merged here at `8d02a1b3`.
  - **r4194596317**, "Add pytest coverage for filesystem-mutating script safeguards": first an accepted limit, put to the holder; the holder ruled (a) (#656 `6016356225`), making it fix-now. Fixed in `7a149b70` (the committed tests). That commit also fixed a misreport the new tests found: an uninitialized spec leg had been reported at the root's HEAD.
- **At `8d02a1b3`**: "Findings: None".
- **At `7a149b70`: r4195469835**, the PR description. Fixed in the body.
- **On #45 (`26fe63e7`), on this file: r4196244480**, `check-ignore` honours local excludes. The holder ruled it into this PR (#656 `6026275158`, item 2).
  - **Fixed in `dc879beb`**: the source must be the root `.gitignore`, and a negated pattern is refused.
  - Red before the fix: the `.git/info/exclude` and `core.excludesFile` cases (`2 failed, 16 passed`).
  - #45's thread is answered by citing `dc879beb` once #45 merges `main`.
- **At `dc879beb`: r4200975244**, ambient `GIT_DIR`/`GIT_WORK_TREE` redirect the checks. Fix-now, fixed in `146ae923`; red before: `2 failed, 18 passed`.
- **At `146ae923`/`c22b5df7`:**
  - **r4201032934**, `assume-unchanged`/`skip-worktree` hide an edit from `git diff`;
  - **r4201424060**, a symlinked runbook;
  - **r4201032886**, repository discovery inherited or redirected.

  All fix-now, fixed in `74f3a4fb`. Before it, four of the new cases PLACED the link: both index flags, the link tracked at the pinned commit, and the enclosing repository.
- **At `60c56a47`: r4201850292**, `hash-object` applies clean filters. Fix-now, fixed in `1f2d6858` (`--no-filters`). Before it, a hiding clean filter and a CRLF edit under `text` both PLACED the link.
- **At `1f2d6858`: r4201912214**, a symbolic link in a parent directory. Fix-now, fixed in `2c86dcca`. Before it, a linked `docs/` holding identical bytes PLACED the link.
- **r4200975330 and r4201032964**, the PR description: fixed by this body.
- **At the head `1f592766`**: review 5440606809, no new finding. Its overview reads "Needs a closer look" for one stated reason: "It remains a cross-repository draft stacked on prerequisites that have not landed". The After set above has landed, so this is not a finding.
- Each fix-now finding was a real bypass with a simple payload, under the convergence rule (#656 `5988818366`).
- **At `57b4f80a`**, GitHub's update-branch merge of `main` `e7815ac5` after the un-draft, with T022's three files byte-identical to `1f592766`: review 5440672686, two findings. Both are **ACCEPTED LIMITS under 5988818366, for the holder at READY**; the PR was un-drafted, so neither is pushed.
  - **r4205549424**, a symlinked destination parent. It is already refused. Measured: with `checkout/docs` a link outside, `git check-ignore` answers "pathspec ... is beyond a symbolic link" (exit 128), the ignore guard refuses (exit 2) before `mkdir` or `symlink_to`, and the outside directory stays empty. Only the message misnames the cause.
  - **r4205549516**, the pinned tree mode. The scenario needs a pin that tracks the runbook as a link, but openDox-spec tracks it as `100644 blob e88f06e6` at `f7ee3c76`. The one-line hardening is a `git ls-tree` mode check, as a follow-on.

**At the head `57b4f80a`** (T022's content as at `1f592766`):
- Checks: `validate` pass (run 37604221972), `SonarCloud Code Analysis` pass, Copilot success, Sourcery skipped. At `1f592766`: validate run 37603513031 passed.
- Review threads: 13, all answered and resolved. 0 unresolved.

### Not done here

- The permanent composed workflow that calls the script on every run is T029 (U-9).
- The "documented local composed run" is wherever T029 documents it. This PR adds the script's own usage docstring only.

🤖 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 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>
@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.

🔵 Needs a closer look

The composed gate remains red and the draft depends on unresolved repair slices.

0 open findings

🧠 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:43:15.065948Z 8179615 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. What shall we delve into next?

Reviewed commit: 817961568e

ℹ️ 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