From 3dbfa7bb14fb4cd6800e50779ba9fd01948fcc25 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Tue, 6 Oct 2026 12:39:30 +0000 Subject: [PATCH 1/7] T026, U-7: the oracle admits R-1 (a)'s named module-level span (plan 038) R-1 (a), ruled by Brett Heap with plan 038 (opensoft/openxFactory#656 comment 6013547504, "Admitted edit kind (Recommended)") and recorded at 12.5's falsifier by T005's batch Q: an admitted entry may name a SPAN, one module-level constant of a protected suite, in place of its test. scripts/protected_suites.py's _inside_the_test rule is amended to admit that kind, and nothing wider: * the entry's old text lies inside that one constant's statement before the landing, and its new text inside it at the landing; * a span is one module-level assignment to one bare name that nothing else in module scope binds, so a function, a class, an import or a second binding is never one, and no span is ever added; * only an admitted entry names a span, and an entry naming both a test and a span is refused. Refs opensoft/openxFactory#656 (CLAIMED 6015602254). Lane: openXfactory-3 Co-Authored-By: Claude Opus 5.5 (1M context) --- scripts/protected_suites.py | 118 ++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 6 deletions(-) diff --git a/scripts/protected_suites.py b/scripts/protected_suites.py index bf0b6af..bab9374 100644 --- a/scripts/protected_suites.py +++ b/scripts/protected_suites.py @@ -54,6 +54,18 @@ test and changes no line it does not add. `old` is then the text the test is added after, and may lie in a neighbouring test, which it leaves as it was. +* A NAMED MODULE-LEVEL SPAN (plan 038 T026; R-1 (a), RULED by Brett Heap at + openxFactory#656 comment 6013547504, "Admitted edit kind (Recommended)", and + recorded at 12.5's falsifier by T005's batch Q). An `admitted` entry may + name, in place of `test`, a `span`: one module-level constant of the suite, + such as a harness text several tests run, which lies inside no test. Its + `old` text must then lie inside that constant's statement in the before + text, and its `new` text inside it in the after text, so the entry admits + an edit to that one constant and to nothing else of the suite. A span is + one module-level assignment to that one name, and it must exist before the + landing and at it: no span is added, and a function or class is never one. + A respelling names its test, never a span. + A landing that touches a protected suite is admitted for that suite only if one entry holds at it, or, in F5.2's call, a CHAIN of entries does. Every other protected path it touches is refused. @@ -96,7 +108,8 @@ THE ALLOW-LIST'S OWN RULES are checked before anything is subtracted, and a file that breaks one refuses the whole check (exit 2) rather than subtracting less: `schema_version` 1, `kind` `protected-suite-respellings`, no key given -twice, and each entry carrying exactly its declared keys, with blobs that are +twice, and each entry carrying exactly its declared keys (an admitted entry +names a `test` or a `span`, never both), with blobs that are full object ids, `old` and `new` that are whole lines ending in a newline, and entries for one suite that chain (each `before_blob` is the previous entry's `after_blob`). @@ -130,6 +143,10 @@ "before_blob", "after_blob", "old", "new"}) EDIT_KEYS = {"respelling": frozenset({"respelled"}), "admitted": frozenset({"reason"})} +#: R-1 (a) (plan 038 T026): the key an `admitted` entry names IN PLACE OF +#: `test` when its edit lies in a module-level constant of the suite. +SPAN_KEY = "span" +SPAN_EDITS = frozenset({"admitted"}) #: A full object id: a blob in an entry, or a landing's commit in the input. _BLOB = re.compile(r"[0-9a-f]{40}") @@ -138,6 +155,8 @@ _SUITE = re.compile(r"tests/test_\w+\.py", re.ASCII) _LANDING = re.compile(r"opensoft/openXdox-code#[1-9]\d*", re.ASCII) _TEST = re.compile(r"test_\w+", re.ASCII) +#: A span is a module-level name, and never a test's. +_SPAN = re.compile(r"[A-Za-z_]\w*", re.ASCII) class AllowListInvalid(ValueError): @@ -212,6 +231,14 @@ def _check_keys(entry: Any, where: str) -> None: if not isinstance(edit, str) or edit not in EDIT_KEYS: raise AllowListInvalid(f"{where}: edit is {edit!r}, not one of {sorted(EDIT_KEYS)}") wanted = COMMON_KEYS | EDIT_KEYS[edit] + if SPAN_KEY in entry: + # R-1 (a): a span stands IN PLACE OF the test, and only an admitted + # entry names one. Naming both still fails the key check below. + if edit not in SPAN_EDITS: + raise AllowListInvalid( + f"{where}: names a span, and only an admitted entry may (R-1 (a)); " + f"a {edit} entry names its test") + wanted = (wanted - {"test"}) | {SPAN_KEY} if set(entry) != wanted: raise AllowListInvalid( f"{where}: carries {sorted(map(str, entry))}, and a {edit} entry " @@ -224,7 +251,12 @@ def _check_spellings(entry: dict, where: str) -> None: """The suite, test, landing and blobs are each spelled as the rules say.""" if not _SUITE.fullmatch(entry["suite"]): raise AllowListInvalid(f"{where}: suite {entry['suite']!r} is not tests/test_.py") - if not _TEST.fullmatch(entry["test"]): + if SPAN_KEY in entry: + span = entry[SPAN_KEY] + if not _SPAN.fullmatch(span) or _TEST.fullmatch(span): + raise AllowListInvalid( + f"{where}: span {span!r} is not a module-level name, or is a test's") + elif not _TEST.fullmatch(entry["test"]): raise AllowListInvalid(f"{where}: test {entry['test']!r} is not a test's name") if not _LANDING.fullmatch(entry["landing"]): raise AllowListInvalid( @@ -291,13 +323,76 @@ def _test_lines(text: str, test: str) -> tuple[int, int] | None: return min([node.lineno, *(d.lineno for d in node.decorator_list)]), node.end_lineno -def _inside_the_test(text: str, start: int, piece: str, test: str) -> bool: - span = _test_lines(text, test) - if span is None: +def _span_lines(text: str, span: str) -> tuple[int, int] | None: + """The 1-based line span of the module-level constant `span`, or None. + + R-1 (a) (plan 038 T026): a span is ONE module-level assignment whose one + target is the bare name `span`, and no other module-level statement binds + that name, so a function, a class, an import or a second assignment is + never a span.""" + try: + tree = ast.parse(text) + except SyntaxError: + return None + binders = [node for node in _module_scope(tree.body) if span in _module_names(node)] + if len(binders) != 1 or binders[0] not in tree.body: + return None + node = binders[0] + if isinstance(node, ast.Assign): + single = len(node.targets) == 1 and isinstance(node.targets[0], ast.Name) + else: + single = (isinstance(node, ast.AnnAssign) and node.value is not None + and isinstance(node.target, ast.Name)) + return (node.lineno, node.end_lineno) if single else None + + +def _module_scope(body: list[ast.stmt]): + """Every statement in module scope: the module's own, and those nested in + its compound statements (`if`, `try`, `with`, loops), but never those in a + function's or a class's body, which bind no module-level name.""" + for node in body: + yield node + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): + continue + for field in ("body", "orelse", "finalbody"): + yield from _module_scope(getattr(node, field, []) or []) + for handler in getattr(node, "handlers", []) or []: + yield from _module_scope(handler.body) + + +def _module_names(node: ast.stmt) -> set[str]: + """Every name a module-scope statement binds itself (a compound statement's + nested statements are walked on their own).""" + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): + return {node.name} + if isinstance(node, (ast.Import, ast.ImportFrom)): + return {(alias.asname or alias.name).split(".")[0] for alias in node.names} + targets = (node.targets if isinstance(node, ast.Assign) + else [node.target] if isinstance(node, (ast.AnnAssign, ast.AugAssign, + ast.For, ast.AsyncFor)) + else [item.optional_vars for item in node.items if item.optional_vars] + if isinstance(node, (ast.With, ast.AsyncWith)) + else []) + return {name.id for target in targets for name in ast.walk(target) + if isinstance(name, ast.Name)} + + +def _inside(lines: tuple[int, int] | None, text: str, start: int, piece: str) -> bool: + if lines is None: return False first_line = text.count("\n", 0, start) + 1 last_line = first_line + piece.count("\n") - 1 - return span[0] <= first_line and last_line <= span[1] + return lines[0] <= first_line and last_line <= lines[1] + + +def _inside_the_test(text: str, start: int, piece: str, test: str) -> bool: + return _inside(_test_lines(text, test), text, start, piece) + + +def _inside_the_span(text: str, start: int, piece: str, span: str) -> bool: + """`_inside_the_test`'s rule, amended by R-1 (a) to admit the new kind: + `piece` lies inside the one module-level constant `span` names.""" + return _inside(_span_lines(text, span), text, start, piece) def _only_the_added_test(after_text: str, at: int, old: str, new: str, test: str) -> str | None: @@ -411,6 +506,17 @@ def _edit_holds(before_text: str, after_text: str, entry: dict) -> str | None: return "the entry's old text does not start a line before the landing, so it is not whole lines" if before_text.replace(old, new, 1) != after_text: return "replacing the entry's old text with its new text does not give the suite at the landing" + if SPAN_KEY in entry: + # R-1 (a): the edit lies inside the one module-level constant the entry + # names, before the landing and at it. No span is ever added. + span = entry[SPAN_KEY] + if _span_lines(before_text, span) is None: + return f"{span} is not one module-level constant before the landing" + if not _inside_the_span(before_text, at, old, span): + return f"the entry's old text is not inside {span} before the landing" + if not _inside_the_span(after_text, at, new, span): + return f"the entry's new text is not inside {span} at the landing" + return None if _test_lines(before_text, entry["test"]) is None: # An ADDED test (plan 034 T061; T007 batch F admits one): it has no body # before the landing to lie inside, so the edit must add it and only it. From 6db5be77d5fc7626b7e8edf32a4b6fddc4629d4a Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Tue, 6 Oct 2026 12:43:38 +0000 Subject: [PATCH 2/7] T026, U-7: the allow-list's 17 H-2 entries and the 'cluster' entry, each with its respelling (plan 038) Eighteen edits to five of 12.5's governed suites, one per test, each entered in tests/protected_suite_respellings.yaml with its exact old and new text and chained by blob in the order it applies: * the 17 that CF-4 (H-2) admits, respelling names the split-opendox carve moved before the arc: cmd_gate_* 7 (test_session_gates.py 3, test_session_verbs.py 4: the verbs now read from openxdox.cli_gate), hosted_index 3 (test_session_confinement.py: serve_projection's), share paths 2 (test_doxbench_share.py: each swept module at its one home, the record schema at the packaged copy), Group W 1 (test_session_verbs.py: openxdox.generator) and Group S2 4 (test_staging_workbench.py: the S5 spellings of the create and session columns); * test_staged_scope_adds_cluster_neighbourhood_section, R-1 (a)'s in-test entry: the label is the neutral grouping word, since S7 spells labels in the registered domain's vocabulary and that probe sets none. No assertion is weakened: each entry's review says what stays. The file's header now names the CF-4 respellings and R-1 (a)'s span kind. Refs opensoft/openxFactory#656 (CLAIMED 6015602254; questions 6016415390). Lane: openXfactory-3 Co-Authored-By: Claude Opus 5.5 (1M context) --- tests/protected_suite_respellings.yaml | 729 ++++++++++++++++++++++++- tests/test_doxbench_share.py | 24 +- tests/test_session_confinement.py | 23 +- tests/test_session_gates.py | 26 +- tests/test_session_verbs.py | 37 +- tests/test_staging_workbench.py | 27 +- 6 files changed, 840 insertions(+), 26 deletions(-) diff --git a/tests/protected_suite_respellings.yaml b/tests/protected_suite_respellings.yaml index 6dbe9db..91f5e2d 100644 --- a/tests/protected_suite_respellings.yaml +++ b/tests/protected_suite_respellings.yaml @@ -19,7 +19,10 @@ # # WHAT A RULING ADMITS. Nothing else is entered. # * A RESPELLING of a reference to a seam the arc moved, with no assertion -# weakened (R1Q7 (a), batch C). +# weakened (R1Q7 (a), batch C). For 12.5's governed suites that includes +# the seventeen respellings of names the split-opendox CARVE moved before +# the arc (CF-4, H-2, confirmed with plan 038's ruling at `6013547504`; +# batch Q), and the 'cluster' node's in-test entry (R-1 (a)'s note). # * The two edits F7.1 requires (R1Q14 (a), `5850003126`, batch F), in # `tests/test_snapshot.py` and `tests/test_snapshot_validator_home.py`. # * The two edits 5.3a's `values` block requires (R1Q26 (a), `5851950767`, @@ -28,8 +31,11 @@ # K), nine in `tests/test_snapshot_validation_launch.py` and one in # `tests/test_snapshot.py`: each one's walk premise becomes 7.3's answer, # and its assertions stay. -# The last three kinds are each entered with a reason, and none is entered as -# a respelling. +# * An edit to a NAMED MODULE-LEVEL SPAN of one of 12.5's governed suites, +# such as a harness text several tests run, which lies inside no test +# (R-1 (a), RULED with plan 038 at `6013547504`; batch Q). +# The F7.1, 5.3a, batch K and span kinds are each entered with a reason, and +# none is entered as a respelling. # # WHO WRITES IT. The task that makes the first admitted edit creates it, in its # own pull request (plan 034 T019's rule). T067 amended that rule: T060 @@ -49,7 +55,9 @@ # `protected-suite-respellings`. No mapping gives a key twice. # * `entries` are in landing order, one per edit. Each entry names: # - `suite`, the protected file, written plainly (`tests/`); -# - `test`, the one test the edit changes; +# - `test`, the one test the edit changes, or, for an admitted edit to a +# module-level constant (R-1 (a)), `span`, that constant's name, in +# its place; # - `landing`, the pull request that lands the edit, # `opensoft/openXdox-code#`, because the landing's commit is not # known inside it (T019's rule); @@ -72,6 +80,12 @@ # fourth, from `test`: `old` lies inside the named test in the # `before_blob` text, and `new` inside it in the `after_blob` text, so an # entry cannot admit an edit to another test of the suite. +# * A SPAN (plan 038 T026; R-1 (a)). For an entry that names `span`, the +# fourth condition reads the span for the test: `old` lies inside that +# constant's statement in the `before_blob` text, and `new` inside it in +# the `after_blob` text. A span is one module-level assignment to one +# bare name that nothing else in module scope binds, it exists before the +# edit and after it, and only an admitted entry names one. # * AN ADDED TEST (plan 034 T061; batch F admits one). Where `test` does not # exist in the `before_blob` text, the fourth condition reads instead: `new` # is `old` followed by that test's whole definition and blank lines, and @@ -1083,3 +1097,710 @@ entries: httpd.shutdown() httpd.server_close() thread.join(timeout=2) + - suite: tests/test_session_gates.py + test: test_the_abandon_cli_verb_enforces_the_human_gate_itself + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The gate verb's home: `cli_mod.cmd_gate_abandon_session` to + `cli_gate.cmd_gate_abandon_session`, imported inside the test from + `openxdox.cli_gate`, where the split-opendox carve's section 2.4 moved + openDox's gate verbs (a commit with no `Arc:` trailer). `opendox.cli` no + longer carries the name, so the old spelling raised AttributeError. The + shared `_session_identity_gate` stays openDox's. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. Both assertions stay: the verb's own source still + calls the shared helper, and the helper still requires the human gate. + No assertion is weakened. + before_blob: d3f427ec36ba65f8035a532b46218abdefb16d1e + after_blob: a4e4864823e8d47ff846f3d81935eace2115c5fe + old: |2 + assert "_session_identity_gate" in inspect.getsource( + cli_mod.cmd_gate_abandon_session) + new: |2 + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + assert "_session_identity_gate" in inspect.getsource( + cli_gate.cmd_gate_abandon_session) + - suite: tests/test_session_gates.py + test: test_the_cli_offers_no_bypass_flag_for_the_endings + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The functions the two parsed verbs dispatch to: + `cli_mod.cmd_gate_abandon_session` and + `cli_mod.cmd_gate_cleanup_abandoned_branch` to their `cli_gate` + spellings, imported inside the test from `openxdox.cli_gate`, where the + split-opendox carve's section 2.4 moved openDox's gate verbs (a commit + with no `Arc:` trailer). `opendox.cli` no longer carries the names, so + the old spellings raised AttributeError. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The parser is still the real one, both verbs + still dispatch to exactly their functions (identity checks), and both + required flags still refuse. No assertion is weakened. + before_blob: a4e4864823e8d47ff846f3d81935eace2115c5fe + after_blob: f30ceeae6c123e183e19dbe684b4b103c43271bb + old: |2 + parser = cli_mod.build_parser() + args = parser.parse_args(["gate", "abandon-session", "--repo-root", ".", + "--actor", "brett", "--scope-kind", + bs.STAGED_TOPIC, "--scope-id", TOPIC, + "--reason", "why"]) + assert args.func is cli_mod.cmd_gate_abandon_session + assert args.records_dir == gc.DEFAULT_RECORDS_DIR # _add_gate_identity_args + with pytest.raises(SystemExit): # --reason is required + parser.parse_args(["gate", "abandon-session", "--repo-root", ".", + "--actor", "brett", "--scope-kind", bs.STAGED_TOPIC, + "--scope-id", TOPIC]) + cleanup = parser.parse_args(["gate", "cleanup-abandoned-branch", + "--repo-root", ".", "--actor", "brett", + "--scope-kind", bs.STAGED_TOPIC, + "--scope-id", TOPIC, "--ref", DRAFT]) + assert cleanup.func is cli_mod.cmd_gate_cleanup_abandoned_branch + new: |2 + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + parser = cli_mod.build_parser() + args = parser.parse_args(["gate", "abandon-session", "--repo-root", ".", + "--actor", "brett", "--scope-kind", + bs.STAGED_TOPIC, "--scope-id", TOPIC, + "--reason", "why"]) + assert args.func is cli_gate.cmd_gate_abandon_session + assert args.records_dir == gc.DEFAULT_RECORDS_DIR # _add_gate_identity_args + with pytest.raises(SystemExit): # --reason is required + parser.parse_args(["gate", "abandon-session", "--repo-root", ".", + "--actor", "brett", "--scope-kind", bs.STAGED_TOPIC, + "--scope-id", TOPIC]) + cleanup = parser.parse_args(["gate", "cleanup-abandoned-branch", + "--repo-root", ".", "--actor", "brett", + "--scope-kind", bs.STAGED_TOPIC, + "--scope-id", TOPIC, "--ref", DRAFT]) + assert cleanup.func is cli_gate.cmd_gate_cleanup_abandoned_branch + - suite: tests/test_session_gates.py + test: test_the_cleanup_cli_verb_enforces_the_human_gate_and_deletes_the_branch + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The gate verb's home: `cli_mod.cmd_gate_cleanup_abandoned_branch` to + `cli_gate.cmd_gate_cleanup_abandoned_branch`, imported inside the test + from `openxdox.cli_gate`, where the split-opendox carve's section 2.4 + moved openDox's gate verbs (a commit with no `Arc:` trailer). + `opendox.cli` no longer carries the name, so the old spelling raised + AttributeError. The shared `_session_identity_gate` stays openDox's. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. Both assertions stay, and every refusal and the + deletion the test drives through the CLI stay. No assertion is weakened. + before_blob: f30ceeae6c123e183e19dbe684b4b103c43271bb + after_blob: 034dcea538f6044658237fe81de39f55fc46367e + old: |2 + assert "_session_identity_gate" in inspect.getsource( + cli_mod.cmd_gate_cleanup_abandoned_branch) + new: |2 + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + assert "_session_identity_gate" in inspect.getsource( + cli_gate.cmd_gate_cleanup_abandoned_branch) + - suite: tests/test_session_verbs.py + test: test_the_cli_verb_enforces_the_human_gate_itself + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The gate verb's home: `cli_mod.cmd_gate_edit_document` to + `cli_gate.cmd_gate_edit_document`, imported inside the test from + `openxdox.cli_gate`, where the split-opendox carve's section 2.4 moved + openDox's gate verbs (a commit with no `Arc:` trailer). `opendox.cli` no + longer carries the name, so the old spelling raised AttributeError. The + shared `_session_identity_gate` stays openDox's. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The verb's own source still calls the shared + helper, which still requires the human gate and console presence. No + assertion is weakened. + before_blob: 33f3626ca6973a8ea69e925c8baaf02256fd4b68 + after_blob: 7d946d390f5caec4985930d3046de3546561ed08 + old: |2 + source = inspect.getsource(cli_mod.cmd_gate_edit_document) + new: |2 + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + source = inspect.getsource(cli_gate.cmd_gate_edit_document) + - suite: tests/test_session_verbs.py + test: test_the_cli_offers_no_inline_content_and_no_bypass_flag + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The function the parsed verb dispatches to: + `cli_mod.cmd_gate_edit_document` to `cli_gate.cmd_gate_edit_document`, + imported inside the test from `openxdox.cli_gate`, where the + split-opendox carve's section 2.4 moved openDox's gate verbs (a commit + with no `Arc:` trailer). `opendox.cli` no longer carries the name, so + the old spelling raised AttributeError. The shared + `_session_identity_gate` stays openDox's. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The flag sweep, the identity check on the + dispatched function and both required-flag refusals stay. No assertion + is weakened. + before_blob: 7d946d390f5caec4985930d3046de3546561ed08 + after_blob: 0190d38dcd57f3a5fe0ffd210bb7123f286cdf14 + old: |2 + assert args.func is cli_mod.cmd_gate_edit_document + new: |2 + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + assert args.func is cli_gate.cmd_gate_edit_document + - suite: tests/test_session_verbs.py + test: test_the_save_cli_verb_enforces_the_human_gate_itself + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The gate verb's home: `cli_mod.cmd_gate_open_pr` to + `cli_gate.cmd_gate_open_pr`, imported inside the test from + `openxdox.cli_gate`, where the split-opendox carve's section 2.4 moved + openDox's gate verbs (a commit with no `Arc:` trailer). `opendox.cli` no + longer carries the name, so the old spelling raised AttributeError. The + shared `_session_identity_gate` stays openDox's. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. Both assertions stay, and every refusal the test + drives through the CLI stays. No assertion is weakened. + before_blob: 0190d38dcd57f3a5fe0ffd210bb7123f286cdf14 + after_blob: e03b4aab422f429ec9c2565207ee900c929235db + old: |2 + assert "_session_identity_gate" in inspect.getsource(cli_mod.cmd_gate_open_pr) + new: |2 + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + assert "_session_identity_gate" in inspect.getsource(cli_gate.cmd_gate_open_pr) + - suite: tests/test_session_verbs.py + test: test_the_save_cli_offers_no_token_and_no_bypass_flag + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The function the parsed verb dispatches to: `cli_mod.cmd_gate_open_pr` + to `cli_gate.cmd_gate_open_pr`, imported inside the test from + `openxdox.cli_gate`, where the split-opendox carve's section 2.4 moved + openDox's gate verbs (a commit with no `Arc:` trailer). `opendox.cli` no + longer carries the name, so the old spelling raised AttributeError. The + shared `_session_identity_gate` stays openDox's. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The identity check on the dispatched function, + the optional title and body, the token and merge-flag sweep and the + required-flag refusal stay. No assertion is weakened. + before_blob: e03b4aab422f429ec9c2565207ee900c929235db + after_blob: 311df394b623d1f9698d938c3c60cadc356a9f5e + old: |2 + assert args.func is cli_mod.cmd_gate_open_pr + new: |2 + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + assert args.func is cli_gate.cmd_gate_open_pr + - suite: tests/test_session_verbs.py + test: test_open_pr_proceeds_on_an_unready_topic_without_reaching_require_ready + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The patched function's dotted home: + `ideation_dashboard.generator.live_topic_health` to + `openxdox.generator.live_topic_health`. The carve manifest moved + `generator` to openXdox-code's `src/openxdox/generator.py` with a + declared edit, and this suite already imports `live_topic_health` from + there. Composed, `ideation_dashboard` is openxFactory's lane remainder, + which has no `generator`, so monkeypatch raised ImportError resolving + the old target. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The spy still raises if the save consults live + topic health, now on the module the save would actually reach, and every + assertion on the saved pull request stays. No assertion is weakened. + before_blob: 311df394b623d1f9698d938c3c60cadc356a9f5e + after_blob: 00b5b96c3f20171385d37bca490d2f3861db058f + old: |2 + monkeypatch.setattr( + "ideation_dashboard.generator.live_topic_health", + new: |2 + monkeypatch.setattr( + # THE MODULE'S OWN HOME (plan 038 T026, under batch Q's CF-4): the carve + # moved `generator` to `openxdox.generator`, where this suite already + # imports `live_topic_health` from. + "openxdox.generator.live_topic_health", + - suite: tests/test_session_confinement.py + test: test_a_hosted_response_never_names_a_session_ref_in_its_headers + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The projection's home: `serve_mod.hosted_index` to + `serve_projection.hosted_index`, imported inside the test from + `openxdox.serve_projection`, where it lives since the split-opendox + carve's section 2.4. openDox-code#10 (BUILD slice 2b, `0f1f2e5`, no + `Arc:` trailer) dropped openDox's re-export, so the old spelling raised + AttributeError. Both of the test's calls. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. Both calls are respelled and nothing else: the + hosted entries are still main-only with no `active`, and the local + projection still keeps its document. No assertion is weakened. + before_blob: d135117fb4e54cfadb22e9f288ba7e5049b72168 + after_blob: 9ae7cc227958812bb4a220fb1a690ccda8cb3f9b + old: |2 + projected = serve_mod.hosted_index(document) + + assert [e["ref"] for e in projected["entries"]] == [reg.DEFAULT_REF] + assert "active" not in projected + # the LOCAL plane's projection is the document itself, untouched + assert serve_mod.hosted_index({"entries": [{"repository": REPO, + new: |2 + # THE PROJECTION'S OWN HOME (plan 038 T026, under batch Q's CF-4): + # `hosted_index` is openXdox's projection column's, and openDox no longer + # re-exports it (BUILD slice 2b). + from openxdox import serve_projection + + projected = serve_projection.hosted_index(document) + + assert [e["ref"] for e in projected["entries"]] == [reg.DEFAULT_REF] + assert "active" not in projected + # the LOCAL plane's projection is the document itself, untouched + assert serve_projection.hosted_index({"entries": [{"repository": REPO, + - suite: tests/test_session_confinement.py + test: test_the_hosted_index_never_names_a_session_ref_as_an_aggregate_member + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The projection's home: `serve_mod.hosted_index` to + `serve_projection.hosted_index`, imported inside the test from + `openxdox.serve_projection`, where it lives since the split-opendox + carve's section 2.4. openDox-code#10 (BUILD slice 2b, `0f1f2e5`, no + `Arc:` trailer) dropped openDox's re-export, so the old spelling raised + AttributeError. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. Every assertion on the projected refs and the + narrowed aggregate stays. No assertion is weakened. + before_blob: 9ae7cc227958812bb4a220fb1a690ccda8cb3f9b + after_blob: 46b238e7702adb0ead34d7b199935a14c5e0178f + old: |2 + assert DRAFT in json.dumps(document) # the LOCAL document names it + projected = serve_mod.hosted_index(document) + new: |2 + assert DRAFT in json.dumps(document) # the LOCAL document names it + # THE PROJECTION'S OWN HOME (plan 038 T026, under batch Q's CF-4): + # `hosted_index` is openXdox's projection column's, and openDox no longer + # re-exports it (BUILD slice 2b). + from openxdox import serve_projection + + projected = serve_projection.hosted_index(document) + - suite: tests/test_session_confinement.py + test: test_an_aggregate_of_only_session_refs_is_dropped_from_a_hosted_index + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The projection's home: `serve_mod.hosted_index` to + `serve_projection.hosted_index`, imported inside the test from + `openxdox.serve_projection`, where it lives since the split-opendox + carve's section 2.4. openDox-code#10 (BUILD slice 2b, `0f1f2e5`, no + `Arc:` trailer) dropped openDox's re-export, so the old spelling raised + AttributeError. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. Both assertions stay: the aggregate is dropped + whole and only main remains. No assertion is weakened. + before_blob: 46b238e7702adb0ead34d7b199935a14c5e0178f + after_blob: 986482e36e35331a4ad1282dcdc5eeeb09a4c4a9 + old: |2 + "members": [{"repository": REPO, "ref": DRAFT}]}]} + + projected = serve_mod.hosted_index(document) + new: |2 + "members": [{"repository": REPO, "ref": DRAFT}]}]} + + # THE PROJECTION'S OWN HOME (plan 038 T026, under batch Q's CF-4): + # `hosted_index` is openXdox's projection column's, and openDox no longer + # re-exports it (BUILD slice 2b). + from openxdox import serve_projection + + projected = serve_projection.hosted_index(document) + - suite: tests/test_doxbench_share.py + test: test_no_implicit_path_reaches_a_push + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + Where each swept module is read: `REPO_ROOT / "scripts" / + "ideation_dashboard" / module`, the pre-carve home above the checkout, + to the one home each module has now. The carve split + `scripts/ideation_dashboard/` three ways: openDox's modules to the + pinned `opendox` package, `serve_gate.py` and `serve_projection.py` to + this leg's `openxdox`, and openxFactory's own lane modules + (`nightly_lane.py`, `doxbench_status_exemption.py`, + `serve_openxfactory_lanes.py`) stayed in its + `scripts/ideation_dashboard/`, which the composed run puts on the path + (R1Q23 (a)). The old path raised FileNotFoundError. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The sweep still reads every module + `NO_IMPLICIT_PUSH_MODULES` names, each whole, against every forbidden + token, and a module found in none of the three homes, or in more than + one, now FAILS rather than shrinking the sweep. No assertion is + weakened. + before_blob: 10fe26d6ad5b2527930325775417234fb2eb15e0 + after_blob: e8f98bc82c931c1fc407445a43c55de9d6d9d46f + old: |2 + for module in NO_IMPLICIT_PUSH_MODULES: + source = (REPO_ROOT / "scripts" / "ideation_dashboard" + / module).read_text(encoding="utf-8") + new: |2 + import importlib.util + + for module in NO_IMPLICIT_PUSH_MODULES: + # WHERE EACH MODULE LIVES NOW (plan 038 T026, under batch Q's CF-4). The + # carve split `scripts/ideation_dashboard/` three ways: openDox's modules + # to the pinned `opendox` package, `serve_gate.py` and `serve_projection.py` + # to this leg's `openxdox`, and openxFactory's own lane modules stayed in + # its `scripts/ideation_dashboard/`, which the composed run puts on the + # path (R1Q23 (a)). Each is read at the one home it has: a module found in + # none of the three, or in more than one, fails rather than shrinking the + # sweep. Locating a module imports only its package, never the module. + homes = [spec.origin for spec in ( + importlib.util.find_spec(f"{package}.{module.removesuffix('.py')}") + for package in ("opendox", "openxdox", "ideation_dashboard")) + if spec is not None] + assert len(homes) == 1, (module, homes) + source = Path(homes[0]).read_text(encoding="utf-8") + - suite: tests/test_doxbench_share.py + test: test_the_action_is_in_the_pinned_schema_enum + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + Where the pinned record schema is read: `REPO_ROOT / "contracts" / + "schemas"`, above the checkout, where nothing is since the carve, to + openXdox-code's own packaged copy beside `gate_console` + (`src/openxdox/contracts/schemas/gate-action-record.schema.yaml`), which + the copy record `src/openxdox/contracts/copies.yaml` holds to + openXdox-spec at its pinned commit. The old path raised + FileNotFoundError. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The assertion is unchanged: the share action is + in the pinned schema's action enum, read from the schema itself. No + assertion is weakened. + before_blob: e8f98bc82c931c1fc407445a43c55de9d6d9d46f + after_blob: 68ad73431785b849da162efbd7adc3fa780ec42e + old: |2 + schema = yaml.safe_load( + (REPO_ROOT / "contracts" / "schemas" + / "gate-action-record.schema.yaml").read_text(encoding="utf-8")) + new: |2 + # THE SCHEMA'S PACKAGED HOME (plan 038 T026, under batch Q's CF-4): no + # `contracts/` sits above this checkout since the carve. openXdox-code + # carries the record schema in its own package, held to openXdox-spec at its + # pinned commit by `src/openxdox/contracts/copies.yaml`, so it is read there. + schema = yaml.safe_load( + (Path(gc.__file__).resolve().parent / "contracts" / "schemas" + / "gate-action-record.schema.yaml").read_text(encoding="utf-8")) + - suite: tests/test_staging_workbench.py + test: test_staged_scope_adds_cluster_neighbourhood_section + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The neighbourhood section's label word: 'cluster neighbourhood' to + 'group neighbourhood', the neutral grouping word. Since section 3.4 + slice S7 (openDox-code `1e46971`) the model spells its labels in the + registered domain's vocabulary, set at mount, and this probe sets none, + so it reads openDox's own words. + ruled: >- + R-1 (a), RULED by Brett Heap with plan 038 at opensoft/openxFactory#656 + comment 6013547504 ("Admitted edit kind (Recommended)"), whose note at + #1144's 12.5 falsifier (plan 038's T005, batch Q) gives this node an + ordinary in-test entry, H-2's form, on R1Q7 (a), comment 5817152735. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The section is still the third and last, still + flagged inherited, still labelled by its stage's word with ' + neighbourhood', and its documents and their order are unchanged. No + assertion is weakened. + before_blob: 655afc8b57224bbf4f515c50243b3ae4d5407ebc + after_blob: 201b5b1aafb70035147410e57eebb98fa5d0af60 + old: |2 + assert neighbourhood["label"] == "cluster neighbourhood" + new: |2 + # THE NEUTRAL GROUPING WORD (plan 038 T026, R-1 (a)'s in-test entry): since + # section 3.4 slice S7 the model spells its labels in the registered domain's + # vocabulary, set at mount, and this probe sets none. + assert neighbourhood["label"] == "group neighbourhood" + - suite: tests/test_staging_workbench.py + test: test_create_transport_uses_the_injected_fetcher_spelling + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + How the create transport reaches openDox's model: the import `from + "./staging-workbench-model.js"` to the shell-handed `ctx.model` (`MODEL + = (ctx && ctx.model) || null;`), under RULED counterpart Q6 + (opensoft/openxFactory#656 comment 5649094228): a contributed binding + imports nothing of openDox's bundle but `helpers.js`. Its route is still + the one `CREATE_ROUTE` constant, which RULED Q3 moved into the module. + Carved at openXdox-code#18 (`c1ad341`) and openDox-code#20 (`8efb3cf`), + section 3.4 slice S5, with no `Arc:` trailer. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The transport still takes the one model, never a + copy, still names its route through the one constant, and the fetch, + XHR, single-POST and innerHTML pins stay. No assertion is weakened. + before_blob: 201b5b1aafb70035147410e57eebb98fa5d0af60 + after_blob: cc46b03553e3c222d59222daefc9032a70e0d61c + old: |2 + # the route the browser calls is the model's single constant, not a literal + assert 'from "./staging-workbench-model.js"' in body + new: |2 + # the route the browser calls is one constant, not a literal (swb-create.js's + # own since RULED Q3), and the model is openDox's own, reached through the + # shell's `ctx.model` since section 3.4 slice S5 (RULED counterpart Q6) rather + # than imported (plan 038 T026, under batch Q's CF-4) + assert "MODEL = (ctx && ctx.model) || null;" in body + - suite: tests/test_staging_workbench.py + test: test_no_session_write_is_reachable_from_a_gate_off_page + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The early-return's argument name: `renderSessionDescriptors(host, ctx, + o)` to `renderSessionDescriptors(host, session, o)`. RULED Q3's + `mount(host, snapshot, ctx)` took the name `ctx`, so the session the + descriptors render is named `session`. Carved at openXdox-code#18 + (`c1ad341`) and openDox-code#20 (`8efb3cf`), section 3.4 slice S5, with + no `Arc:` trailer. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The gate-off early return still comes before any + live control, the capability is still asked once, and the descriptor + renderer still cannot reach the transport. No assertion is weakened. + before_blob: cc46b03553e3c222d59222daefc9032a70e0d61c + after_blob: 2c297f6c727d79a96b2d708bff1d9730ddbc0144 + old: |2 + assert ("if (!sessionActionsLive(o.caps)) " + "return renderSessionDescriptors(host, ctx, o);") in body + new: |2 + # `ctx` is the mount's own argument since RULED Q3's `mount(host, snapshot, + # ctx)`, so the session the descriptors render is named `session` (section + # 3.4 slice S5; plan 038 T026, under batch Q's CF-4) + assert ("if (!sessionActionsLive(o.caps)) " + "return renderSessionDescriptors(host, session, o);") in body + - suite: tests/test_staging_workbench.py + test: test_the_create_outcome_tells_the_shell_a_session_opened + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The two overlays' spelling at the view's draw call: `opened: + openedSessions(), ended: endedSessions()` to `opened: + sessionColumn.openedSessions(),` and `ended: + sessionColumn.endedSessions()`, adjacent across a line break. The view + reaches the session column through `sessionColumn` since slice S5. + Carved at openXdox-code#18 (`c1ad341`) and openDox-code#20 (`8efb3cf`), + section 3.4 slice S5, with no `Arc:` trailer. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The pair is still asserted together and in order, + and the ordered wiring and the four session-module exports stay. No + assertion is weakened. + before_blob: 2c297f6c727d79a96b2d708bff1d9730ddbc0144 + after_blob: 3822ac818505660f9b834778f94c6133b1689748 + old: |2 + assert "opened: openedSessions(), ended: endedSessions()" in view + new: |2 + # the overlays are the session column's since section 3.4 slice S5 + # (`sessionColumn`), and the pair now spans a line (plan 038 T026, under + # batch Q's CF-4) + assert re.search(r"opened: sessionColumn\.openedSessions\(\),\s*" + r"ended: sessionColumn\.endedSessions\(\)", view) + - suite: tests/test_staging_workbench.py + test: test_the_posture_pill_tooltips_name_the_product + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The pill's split anchor: `const gateOn = createGateLive(caps);` to + `const gateOn = createColumn.createGateLive(caps);`. The view reaches + the create column's predicate through `createColumn` since slice S5, so + the old anchor was absent and the split raised IndexError. Carved at + openXdox-code#18 (`c1ad341`) and openDox-code#20 (`8efb3cf`), section + 3.4 slice S5, with no `Arc:` trailer. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The pill is still the text between the same two + statements, and both assertions on it stay: it names the product and not + the predecessor. No assertion is weakened. + before_blob: 3822ac818505660f9b834778f94c6133b1689748 + after_blob: d4b46a8270306a3aa40335f15cbf590144b03ebd + old: |2 + pill = view.split("const gateOn = createGateLive(caps);", 1)[1].split( + new: |2 + # the create column's own predicate since section 3.4 slice S5 + # (`createColumn`; plan 038 T026, under batch Q's CF-4) + pill = view.split("const gateOn = createColumn.createGateLive(caps);", 1)[1].split( diff --git a/tests/test_doxbench_share.py b/tests/test_doxbench_share.py index 10fe26d..68ad734 100644 --- a/tests/test_doxbench_share.py +++ b/tests/test_doxbench_share.py @@ -270,9 +270,23 @@ def test_no_implicit_path_reaches_a_push(tmp_path): assert {"serve.py", "doxbench_threads.py", "doxbench_bridge.py", "doxbench_mcp.py"} <= set(NO_IMPLICIT_PUSH_MODULES), ( "§12 may widen §11's sweep, never narrow it") + import importlib.util + for module in NO_IMPLICIT_PUSH_MODULES: - source = (REPO_ROOT / "scripts" / "ideation_dashboard" - / module).read_text(encoding="utf-8") + # WHERE EACH MODULE LIVES NOW (plan 038 T026, under batch Q's CF-4). The + # carve split `scripts/ideation_dashboard/` three ways: openDox's modules + # to the pinned `opendox` package, `serve_gate.py` and `serve_projection.py` + # to this leg's `openxdox`, and openxFactory's own lane modules stayed in + # its `scripts/ideation_dashboard/`, which the composed run puts on the + # path (R1Q23 (a)). Each is read at the one home it has: a module found in + # none of the three, or in more than one, fails rather than shrinking the + # sweep. Locating a module imports only its package, never the module. + homes = [spec.origin for spec in ( + importlib.util.find_spec(f"{package}.{module.removesuffix('.py')}") + for package in ("opendox", "openxdox", "ideation_dashboard")) + if spec is not None] + assert len(homes) == 1, (module, homes) + source = Path(homes[0]).read_text(encoding="utf-8") for forbidden in FORBIDDEN_PUSH_TOKENS: assert forbidden not in source, (module, forbidden) @@ -743,8 +757,12 @@ def test_the_record_names_the_branch_and_the_pushed_ref(tmp_path): def test_the_action_is_in_the_pinned_schema_enum(): """The contract-v1.36 growth this verb needed. Read from the schema itself, so a revert of that cut fails HERE rather than at record-write time.""" + # THE SCHEMA'S PACKAGED HOME (plan 038 T026, under batch Q's CF-4): no + # `contracts/` sits above this checkout since the carve. openXdox-code + # carries the record schema in its own package, held to openXdox-spec at its + # pinned commit by `src/openxdox/contracts/copies.yaml`, so it is read there. schema = yaml.safe_load( - (REPO_ROOT / "contracts" / "schemas" + (Path(gc.__file__).resolve().parent / "contracts" / "schemas" / "gate-action-record.schema.yaml").read_text(encoding="utf-8")) assert gc.ACTION_SHARE_SESSION in schema["properties"]["action"]["enum"] diff --git a/tests/test_session_confinement.py b/tests/test_session_confinement.py index d135117..986482e 100644 --- a/tests/test_session_confinement.py +++ b/tests/test_session_confinement.py @@ -1567,12 +1567,17 @@ def test_a_hosted_response_never_names_a_session_ref_in_its_headers(scratch_repo {"repository": REPO, "ref": "cluster/cl-x"}], "active": {"repository": REPO, "ref": DRAFT}} - projected = serve_mod.hosted_index(document) + # THE PROJECTION'S OWN HOME (plan 038 T026, under batch Q's CF-4): + # `hosted_index` is openXdox's projection column's, and openDox no longer + # re-exports it (BUILD slice 2b). + from openxdox import serve_projection + + projected = serve_projection.hosted_index(document) assert [e["ref"] for e in projected["entries"]] == [reg.DEFAULT_REF] assert "active" not in projected # the LOCAL plane's projection is the document itself, untouched - assert serve_mod.hosted_index({"entries": [{"repository": REPO, + assert serve_projection.hosted_index({"entries": [{"repository": REPO, "ref": reg.DEFAULT_REF}], "active": {"repository": REPO, "ref": reg.DEFAULT_REF}})["active"] @@ -1631,7 +1636,12 @@ def test_the_hosted_index_never_names_a_session_ref_as_an_aggregate_member( document = registry.index_document() assert DRAFT in json.dumps(document) # the LOCAL document names it - projected = serve_mod.hosted_index(document) + # THE PROJECTION'S OWN HOME (plan 038 T026, under batch Q's CF-4): + # `hosted_index` is openXdox's projection column's, and openDox no longer + # re-exports it (BUILD slice 2b). + from openxdox import serve_projection + + projected = serve_projection.hosted_index(document) assert set(_every_ref_in(projected)) == {reg.DEFAULT_REF} assert DRAFT not in json.dumps(projected) @@ -1652,7 +1662,12 @@ def test_an_aggregate_of_only_session_refs_is_dropped_from_a_hosted_index(): "aggregates": [{"id": "drafts", "members": [{"repository": REPO, "ref": DRAFT}]}]} - projected = serve_mod.hosted_index(document) + # THE PROJECTION'S OWN HOME (plan 038 T026, under batch Q's CF-4): + # `hosted_index` is openXdox's projection column's, and openDox no longer + # re-exports it (BUILD slice 2b). + from openxdox import serve_projection + + projected = serve_projection.hosted_index(document) assert "aggregates" not in projected assert _every_ref_in(projected) == (reg.DEFAULT_REF,) diff --git a/tests/test_session_gates.py b/tests/test_session_gates.py index d3f427e..034dcea 100644 --- a/tests/test_session_gates.py +++ b/tests/test_session_gates.py @@ -585,8 +585,14 @@ def test_the_abandon_cli_verb_enforces_the_human_gate_itself(scratch_repo, registry, created, worktree = _session(scratch_repo, tmp_path) # the obligation is PER VERB, and it is discharged through one shared helper # the verb calls FIRST — before the session is resolved and before any write + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + assert "_session_identity_gate" in inspect.getsource( - cli_mod.cmd_gate_abandon_session) + cli_gate.cmd_gate_abandon_session) assert "require_human_gate" in inspect.getsource( cli_mod._session_identity_gate) @@ -646,12 +652,18 @@ def test_the_abandon_cli_verb_tears_the_session_down_and_reports_it(scratch_repo def test_the_cli_offers_no_bypass_flag_for_the_endings(): + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + parser = cli_mod.build_parser() args = parser.parse_args(["gate", "abandon-session", "--repo-root", ".", "--actor", "brett", "--scope-kind", bs.STAGED_TOPIC, "--scope-id", TOPIC, "--reason", "why"]) - assert args.func is cli_mod.cmd_gate_abandon_session + assert args.func is cli_gate.cmd_gate_abandon_session assert args.records_dir == gc.DEFAULT_RECORDS_DIR # _add_gate_identity_args with pytest.raises(SystemExit): # --reason is required parser.parse_args(["gate", "abandon-session", "--repo-root", ".", @@ -661,7 +673,7 @@ def test_the_cli_offers_no_bypass_flag_for_the_endings(): "--repo-root", ".", "--actor", "brett", "--scope-kind", bs.STAGED_TOPIC, "--scope-id", TOPIC, "--ref", DRAFT]) - assert cleanup.func is cli_mod.cmd_gate_cleanup_abandoned_branch + assert cleanup.func is cli_gate.cmd_gate_cleanup_abandoned_branch with pytest.raises(SystemExit): # --ref is required parser.parse_args(["gate", "cleanup-abandoned-branch", "--repo-root", ".", "--actor", "brett", "--scope-kind", bs.STAGED_TOPIC, @@ -2135,8 +2147,14 @@ def test_the_cleanup_cli_verb_enforces_the_human_gate_and_deletes_the_branch( _abandon(scratch_repo, registry, reason="parked") _land_proposal(scratch_repo) git = sg.SessionGit(scratch_repo.root) + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + assert "_session_identity_gate" in inspect.getsource( - cli_mod.cmd_gate_cleanup_abandoned_branch) + cli_gate.cmd_gate_cleanup_abandoned_branch) assert "require_human_gate" in inspect.getsource( cli_mod._session_identity_gate) diff --git a/tests/test_session_verbs.py b/tests/test_session_verbs.py index 33f3626..00b5b96 100644 --- a/tests/test_session_verbs.py +++ b/tests/test_session_verbs.py @@ -688,7 +688,13 @@ def test_the_cli_verb_enforces_the_human_gate_itself(scratch_repo, tmp_path, # FR-019's agent/automation clause when that clause arrived). The pin follows # the obligation to where it is discharged, and is STRICTER for it: it now # asserts the shared implementation, so the four siblings are covered too. - source = inspect.getsource(cli_mod.cmd_gate_edit_document) + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + source = inspect.getsource(cli_gate.cmd_gate_edit_document) assert "_session_identity_gate(" in source shared = inspect.getsource(cli_mod._session_identity_gate) assert "require_human_gate" in shared @@ -783,7 +789,13 @@ def test_the_cli_offers_no_inline_content_and_no_bypass_flag(): "--actor", "brett", "--scope-kind", bs.STAGED_TOPIC, "--scope-id", TOPIC, "--document", "d.md", "--content-file", "c.md"]) - assert args.func is cli_mod.cmd_gate_edit_document + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + assert args.func is cli_gate.cmd_gate_edit_document assert args.records_dir == gc.DEFAULT_RECORDS_DIR # _add_gate_identity_args with pytest.raises(SystemExit): # --document is required parser.parse_args(["gate", "edit-document", "--repo-root", ".", @@ -1173,7 +1185,13 @@ def test_the_save_cli_verb_enforces_the_human_gate_itself(scratch_repo, tmp_path FR-019 is a PER-VERB obligation here (contracts/cli.md) — discharged through the ONE shared helper, called FIRST, before any read and any resolution.""" registry, created, worktree = _session(scratch_repo, tmp_path) - assert "_session_identity_gate" in inspect.getsource(cli_mod.cmd_gate_open_pr) + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + assert "_session_identity_gate" in inspect.getsource(cli_gate.cmd_gate_open_pr) assert "require_human_gate" in inspect.getsource(cli_mod._session_identity_gate) with pytest.raises(SystemExit) as exited: # missing --actor @@ -1251,7 +1269,13 @@ def test_the_save_cli_offers_no_token_and_no_bypass_flag(): args = parser.parse_args(["gate", "open-pr", "--repo-root", ".", "--actor", "brett", "--scope-kind", bs.STAGED_TOPIC, "--scope-id", TOPIC]) - assert args.func is cli_mod.cmd_gate_open_pr + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the shared helper + # stays openDox's `cli`. + from openxdox import cli_gate + + assert args.func is cli_gate.cmd_gate_open_pr assert args.records_dir == gc.DEFAULT_RECORDS_DIR # _add_gate_identity_args assert args.title is None and args.body_file is None # both OPTIONAL source = Path(cli_mod.__file__).read_text(encoding="utf-8") @@ -1361,7 +1385,10 @@ def _spy(*args, **kwargs): monkeypatch.setattr(kickoff_mod, "_require_ready", _spy) monkeypatch.setattr( - "ideation_dashboard.generator.live_topic_health", + # THE MODULE'S OWN HOME (plan 038 T026, under batch Q's CF-4): the carve + # moved `generator` to `openxdox.generator`, where this suite already + # imports `live_topic_health` from. + "openxdox.generator.live_topic_health", lambda *a, **k: (_ for _ in ()).throw( AssertionError("open-pr consulted live topic health (FR-031)"))) diff --git a/tests/test_staging_workbench.py b/tests/test_staging_workbench.py index 655afc8..d4b46a8 100644 --- a/tests/test_staging_workbench.py +++ b/tests/test_staging_workbench.py @@ -441,7 +441,10 @@ def test_staged_scope_adds_cluster_neighbourhood_section(tmp_path): assert [s["key"] for s in r["sections"]] == ["folder", "declaring", "neighbourhood"] folder, declaring, neighbourhood = r["sections"] assert neighbourhood["inherited"] is True - assert neighbourhood["label"] == "cluster neighbourhood" + # THE NEUTRAL GROUPING WORD (plan 038 T026, R-1 (a)'s in-test entry): since + # section 3.4 slice S7 the model spells its labels in the registered domain's + # vocabulary, set at mount, and this probe sets none. + assert neighbourhood["label"] == "group neighbourhood" # folder = the one own doc; declaring = the one inbound doc (own deduped out) assert [d["id"] for d in folder["documents"]] == \ ["ideation/staging/github-administration-plane/github-administration-plane.md"] @@ -817,8 +820,11 @@ def test_create_transport_uses_the_injected_fetcher_spelling(): assert "fetch(" not in body # the case-sensitive bundle pin assert "XMLHttpRequest" not in body assert body.count("method: \"POST\"") == 1 # exactly ONE write, not a family - # the route the browser calls is the model's single constant, not a literal - assert 'from "./staging-workbench-model.js"' in body + # the route the browser calls is one constant, not a literal (swb-create.js's + # own since RULED Q3), and the model is openDox's own, reached through the + # shell's `ctx.model` since section 3.4 slice S5 (RULED counterpart Q6) rather + # than imported (plan 038 T026, under batch Q's CF-4) + assert "MODEL = (ctx && ctx.model) || null;" in body assert "CREATE_ROUTE" in body # every dynamic value binds through helpers.el's textContent; innerHTML is # only ever cleared @@ -1715,8 +1721,11 @@ def test_no_session_write_is_reachable_from_a_gate_off_page(): and the capability is asked exactly once, through the pure model's one derivation, so no branch of this file can reach a different verdict.""" body = SESSION_JS.read_text(encoding="utf-8") + # `ctx` is the mount's own argument since RULED Q3's `mount(host, snapshot, + # ctx)`, so the session the descriptors render is named `session` (section + # 3.4 slice S5; plan 038 T026, under batch Q's CF-4) assert ("if (!sessionActionsLive(o.caps)) " - "return renderSessionDescriptors(host, ctx, o);") in body + "return renderSessionDescriptors(host, session, o);") in body # ONE capability question, asked through the model — never re-derived here assert body.count("sessionActionsLive(") == 1 assert "caps.actions" not in body @@ -1765,7 +1774,11 @@ def test_the_create_outcome_tells_the_shell_a_session_opened(): # the jump is keyed to the SESSION ref and `main` is never widened — the # main-keyed 404 on a draft document is correct session isolation assert "onSessionRekey" in view - assert "opened: openedSessions(), ended: endedSessions()" in view + # the overlays are the session column's since section 3.4 slice S5 + # (`sessionColumn`), and the pair now spans a line (plan 038 T026, under + # batch Q's CF-4) + assert re.search(r"opened: sessionColumn\.openedSessions\(\),\s*" + r"ended: sessionColumn\.endedSessions\(\)", view) # both overlays live in the session module, beside the one that consumes them assert "export function sessionOpened(" in session assert "export function openedSessions(" in session @@ -2229,7 +2242,9 @@ def test_the_posture_pill_tooltips_name_the_product(): """The pill's `title` is the sentence that states what authority the plane has (design D9). It is human-visible text and it named the predecessor.""" view = VIEW_JS.read_text(encoding="utf-8") - pill = view.split("const gateOn = createGateLive(caps);", 1)[1].split( + # the create column's own predicate since section 3.4 slice S5 + # (`createColumn`; plan 038 T026, under batch Q's CF-4) + pill = view.split("const gateOn = createColumn.createGateLive(caps);", 1)[1].split( "const closeBtn", 1)[0] assert DOXBENCH_NAME in pill assert "workbench" not in pill.replace("createGateLive", "") From 5fb475fac74e2f2c3f906766af86614d17a5d4c6 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Tue, 6 Oct 2026 12:49:01 +0000 Subject: [PATCH 3/7] T026, U-7: R-1 (a)'s two in-test relocations, :1291 and :1333 (plan 038) R-1 (a), ruled with plan 038 (opensoft/openxFactory#656 comment 6013547504), names the two route claims of tests/test_staging_workbench.py among its admitted edits. RULED Q3 (comment 5642758731) moved the session routes from the pure model into the session column, swb-session.js, the binding that POSTs them, so each claim is relocated with them and entered as an admitted edit with its reason: * test_the_session_surface_added_no_transport_to_the_view_or_the_model: each route is one constant of the session column, and neither the view nor the model names it; the view reaches the column the gate column contributes (the S5 respelling beside it); * test_session_transport_uses_the_injected_fetcher_spelling: every route literal in the transport is its one export const declaration, each route declared once; the model arrives as ctx.model (the S5 respelling beside it). The staging suite's entries chain again by blob, now seven. Refs opensoft/openxFactory#656 (CLAIMED 6015602254; questions 6016415390). Lane: openXfactory-3 Co-Authored-By: Claude Opus 5.5 (1M context) --- tests/protected_suite_respellings.yaml | 133 +++++++++++++++++++++++-- tests/test_staging_workbench.py | 35 +++++-- 2 files changed, 154 insertions(+), 14 deletions(-) diff --git a/tests/protected_suite_respellings.yaml b/tests/protected_suite_respellings.yaml index 91f5e2d..27d991d 100644 --- a/tests/protected_suite_respellings.yaml +++ b/tests/protected_suite_respellings.yaml @@ -1702,6 +1702,127 @@ entries: # shell's `ctx.model` since section 3.4 slice S5 (RULED counterpart Q6) rather # than imported (plan 038 T026, under batch Q's CF-4) assert "MODEL = (ctx && ctx.model) || null;" in body + - suite: tests/test_staging_workbench.py + test: test_the_session_surface_added_no_transport_to_the_view_or_the_model + landing: opensoft/openXdox-code#43 + edit: admitted + reason: >- + A claim about where the session routes are defined, relocated as RULED + Q3 (opensoft/openxFactory#656 comment 5642758731) moved them: from the + pure model to the session column, `swb-session.js`, the binding that + POSTs them (section 3.4 slice S4). The claim is still that each route is + ONE constant and that the view names none; it now also holds that the + model names none. Beside it, the view's reach is respelled: it takes the + session column the gate column contributes (`const sessionColumn = + gate?.session || NO_SESSION_COLUMN;`) rather than importing + `./swb-session.js` (section 3.4 slice S5, openXdox-code#18 `c1ad341`). + ruled: >- + R-1 (a), RULED by Brett Heap with plan 038 at opensoft/openxFactory#656 + comment 6013547504 ("Admitted edit kind (Recommended)"), which names + this claim's relocation (`:1291`, `:1333` at openXdox-code 56e1c238) + among the admitted edits; recorded at #1144's 12.5 falsifier by plan + 038's T005 (batch Q). The respelling beside it is CF-4's (H-2), on R1Q7 + (a), comment 5817152735. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The import-free, transport-free and + dynamic-import pins on the view and the model are untouched, and so is + the self-pin on the older test. Each route is still asserted to be one + constant and absent from the view, now in the module that owns it, and + it is newly asserted absent from the model. No assertion is weakened. + before_blob: cc46b03553e3c222d59222daefc9032a70e0d61c + after_blob: 208617ff09fcfa96d28cacf503abbb00198b65f2 + old: |2 + # the three session routes are MODEL constants; the view names none of them + for route in ("/actions/gate/edit-document", "/actions/gate/open-pr", + "/actions/gate/abandon-session"): + assert route in model, f"{route} is not a model constant" + assert route not in view, f"the view names the route {route}" + assert "/actions/" not in view + # the view reaches the transport ONLY through the sibling module + assert 'from "./swb-session.js"' in view + new: |2 + # THE ROUTES TRAVEL WITH THEIR BINDING (plan 038 T026; R-1 (a), RULED with + # plan 038 at openxFactory#656 comment 6013547504). RULED Q3 (comment + # 5642758731) moved the three session routes from the model to the session + # column, `swb-session.js`, the binding that POSTs them: each is still ONE + # constant there, and neither the view nor the model names any of them. + session = SESSION_JS.read_text(encoding="utf-8") + for route in ("/actions/gate/edit-document", "/actions/gate/open-pr", + "/actions/gate/abandon-session"): + assert len(re.findall(rf'^export const [A-Z_]+_ROUTE = "{re.escape(route)}";$', + session, re.MULTILINE)) == 1, \ + f"{route} is not one session-column constant" + assert route not in model, f"the model names the route {route}" + assert route not in view, f"the view names the route {route}" + assert "/actions/" not in view + # the view reaches the transport ONLY through the session column the gate + # column contributes (section 3.4 slice S5) + assert "const sessionColumn = gate?.session || NO_SESSION_COLUMN;" in view + - suite: tests/test_staging_workbench.py + test: test_session_transport_uses_the_injected_fetcher_spelling + landing: opensoft/openXdox-code#43 + edit: admitted + reason: >- + A claim about route literals in the transport, relocated as RULED Q3 + (opensoft/openxFactory#656 comment 5642758731) moved the routes into + this module: "a route constant travels with the binding that calls it". + `"/actions/gate/" not in body` no longer holds, because the module now + declares its routes, so the claim becomes what it guarded: every route + literal in the module is its one `export const ..._ROUTE` declaration, + each route declared once, and nothing else in the module spells one. + Beside it, the model's reach is respelled: the payload builders arrive + as the shell's `ctx.model` (`MODEL = (ctx && ctx.model) || null;`) + rather than by importing `./staging-workbench-model.js` (section 3.4 + slice S5, RULED counterpart Q6, comment 5649094228). + ruled: >- + R-1 (a), RULED by Brett Heap with plan 038 at opensoft/openxFactory#656 + comment 6013547504 ("Admitted edit kind (Recommended)"), which names + this claim's relocation (`:1291`, `:1333` at openXdox-code 56e1c238) + among the admitted edits; recorded at #1144's 12.5 falsifier by plan + 038's T005 (batch Q). The respelling beside it is CF-4's (H-2), on R1Q7 + (a), comment 5817152735. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The fetch, XHR, dynamic-import, single-POST, + single-helper, dispose-panel and innerHTML pins are untouched, and the + four model symbols are still consulted. A route literal anywhere but its + one declaration still fails, and so does a route declared twice. No + assertion is weakened. + before_blob: 208617ff09fcfa96d28cacf503abbb00198b65f2 + after_blob: d2ef93e4ab7696be1e621967c15ff132d4ffb817 + old: |2 + # routes and payloads come from the pure model, never from a literal here + assert 'from "./staging-workbench-model.js"' in body + for symbol in ("sessionRoute", "sessionRequest", "sessionCommand", + "sessionActionsLive"): + assert symbol in body, f"{symbol} is not consulted by the transport" + assert "/actions/gate/" not in body, \ + "a route literal in the transport would be a second definition" + new: |2 + # THE ROUTES TRAVEL WITH THEIR BINDING (plan 038 T026; R-1 (a), RULED with + # plan 038 at openxFactory#656 comment 6013547504). Payloads still come from + # the pure model, now the shell's `ctx.model` (section 3.4 slice S5, RULED + # counterpart Q6, comment 5649094228) rather than an import; the routes are + # this module's own constants since RULED Q3 (comment 5642758731), each + # declared ONCE, and no other line of the transport spells one. + assert "MODEL = (ctx && ctx.model) || null;" in body + for symbol in ("sessionRoute", "sessionRequest", "sessionCommand", + "sessionActionsLive"): + assert symbol in body, f"{symbol} is not consulted by the transport" + literals = [line for line in body.splitlines() if "/actions/gate/" in line] + assert literals and all( + re.fullmatch(r'export const [A-Z_]+_ROUTE = "/actions/gate/[a-z-]+";', line) + for line in literals), \ + "a route literal outside its one constant would be a second definition" + routes = [line.split('"')[1] for line in literals] + assert len(routes) == len(set(routes)), "a route is declared twice" - suite: tests/test_staging_workbench.py test: test_no_session_write_is_reachable_from_a_gate_off_page landing: opensoft/openXdox-code#43 @@ -1726,8 +1847,8 @@ entries: inside the test named. The gate-off early return still comes before any live control, the capability is still asked once, and the descriptor renderer still cannot reach the transport. No assertion is weakened. - before_blob: cc46b03553e3c222d59222daefc9032a70e0d61c - after_blob: 2c297f6c727d79a96b2d708bff1d9730ddbc0144 + before_blob: d2ef93e4ab7696be1e621967c15ff132d4ffb817 + after_blob: a2bb0d82d0ea0153380fedcaa5881c869f80a049 old: |2 assert ("if (!sessionActionsLive(o.caps)) " "return renderSessionDescriptors(host, ctx, o);") in body @@ -1762,8 +1883,8 @@ entries: inside the test named. The pair is still asserted together and in order, and the ordered wiring and the four session-module exports stay. No assertion is weakened. - before_blob: 2c297f6c727d79a96b2d708bff1d9730ddbc0144 - after_blob: 3822ac818505660f9b834778f94c6133b1689748 + before_blob: a2bb0d82d0ea0153380fedcaa5881c869f80a049 + after_blob: 623d6fe253b36814eb3a52c009528456ee68185c old: |2 assert "opened: openedSessions(), ended: endedSessions()" in view new: |2 @@ -1796,8 +1917,8 @@ entries: inside the test named. The pill is still the text between the same two statements, and both assertions on it stay: it names the product and not the predecessor. No assertion is weakened. - before_blob: 3822ac818505660f9b834778f94c6133b1689748 - after_blob: d4b46a8270306a3aa40335f15cbf590144b03ebd + before_blob: 623d6fe253b36814eb3a52c009528456ee68185c + after_blob: 4bf069cdf4177e9dc65fe4051be015d9e3ed06fa old: |2 pill = view.split("const gateOn = createGateLive(caps);", 1)[1].split( new: |2 diff --git a/tests/test_staging_workbench.py b/tests/test_staging_workbench.py index d4b46a8..4bf069c 100644 --- a/tests/test_staging_workbench.py +++ b/tests/test_staging_workbench.py @@ -1291,14 +1291,23 @@ def test_the_session_surface_added_no_transport_to_the_view_or_the_model(): assert "import(" not in body, f"{name} carries a dynamic import" assert not re.findall(r"^\s*import\s", model, re.MULTILINE), \ "the pure model module must stay import-free (node-harness standalone)" - # the three session routes are MODEL constants; the view names none of them + # THE ROUTES TRAVEL WITH THEIR BINDING (plan 038 T026; R-1 (a), RULED with + # plan 038 at openxFactory#656 comment 6013547504). RULED Q3 (comment + # 5642758731) moved the three session routes from the model to the session + # column, `swb-session.js`, the binding that POSTs them: each is still ONE + # constant there, and neither the view nor the model names any of them. + session = SESSION_JS.read_text(encoding="utf-8") for route in ("/actions/gate/edit-document", "/actions/gate/open-pr", "/actions/gate/abandon-session"): - assert route in model, f"{route} is not a model constant" + assert len(re.findall(rf'^export const [A-Z_]+_ROUTE = "{re.escape(route)}";$', + session, re.MULTILINE)) == 1, \ + f"{route} is not one session-column constant" + assert route not in model, f"the model names the route {route}" assert route not in view, f"the view names the route {route}" assert "/actions/" not in view - # the view reaches the transport ONLY through the sibling module - assert 'from "./swb-session.js"' in view + # the view reaches the transport ONLY through the session column the gate + # column contributes (section 3.4 slice S5) + assert "const sessionColumn = gate?.session || NO_SESSION_COLUMN;" in view # and the pre-existing pin is still THE pin: a clobber cannot pass by # deleting the assertion it was supposed to satisfy own = Path(__file__).read_text(encoding="utf-8") @@ -1331,13 +1340,23 @@ def test_session_transport_uses_the_injected_fetcher_spelling(): # session-bar affordances and the doxBench Save transport (T080, 2026-07-30); # both ride the same single write-method literal above assert body.count("submitSession(") == 3 - # routes and payloads come from the pure model, never from a literal here - assert 'from "./staging-workbench-model.js"' in body + # THE ROUTES TRAVEL WITH THEIR BINDING (plan 038 T026; R-1 (a), RULED with + # plan 038 at openxFactory#656 comment 6013547504). Payloads still come from + # the pure model, now the shell's `ctx.model` (section 3.4 slice S5, RULED + # counterpart Q6, comment 5649094228) rather than an import; the routes are + # this module's own constants since RULED Q3 (comment 5642758731), each + # declared ONCE, and no other line of the transport spells one. + assert "MODEL = (ctx && ctx.model) || null;" in body for symbol in ("sessionRoute", "sessionRequest", "sessionCommand", "sessionActionsLive"): assert symbol in body, f"{symbol} is not consulted by the transport" - assert "/actions/gate/" not in body, \ - "a route literal in the transport would be a second definition" + literals = [line for line in body.splitlines() if "/actions/gate/" in line] + assert literals and all( + re.fullmatch(r'export const [A-Z_]+_ROUTE = "/actions/gate/[a-z-]+";', line) + for line in literals), \ + "a route literal outside its one constant would be a second definition" + routes = [line.split('"')[1] for line in literals] + assert len(routes) == len(set(routes)), "a route is declared twice" # session refusals render in dispose.js's panel, identically to every other # refusal on the page (research R6) assert 'from "./dispose.js"' in body From 856047ae83d5ce46a8d231597872bce3d0577ff4 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Tue, 6 Oct 2026 13:11:42 +0000 Subject: [PATCH 4/7] T026, U-7: R-1 (a)'s six spans, the served display, and the span kind's cases (plan 038) Brett Heap's ruling at opensoft/openxFactory#656 comment 6016648451 ("Widen the spans, served display (Recommended)") widens R-1 (a)'s named spans to six: _CREATE_HARNESS, _SESSION_HARNESS and _HOSTILE_HARNESS, and the helpers _run_create, _run_session and _hostile_descriptors. The helpers copy the model's siblings and hand each harness the SERVED governed display. * scripts/protected_suites.py: a span may now be one module-level helper function as well as one module-level constant. A function's span starts at its first decorator, as a test's does. A class, an import, a test or a name bound twice is still never a span, and no span is added. * tests/test_staging_workbench.py, ten entries chained after the seven: - the three helpers copy display.js and the siblings the harness imports (swb-create.js or swb-session.js, with the helpers.js and dispose.js they import), with a package.json that makes node read them as modules, and hand the harness /capabilities["display"], by the statement serve.build_server() makes under the registered host; - the three harnesses install it (setDisplay(readDisplay(...))) before anything derives, as the shell does, and import CREATE_ROUTE and sessionRoute from the columns RULED Q3 moved them to; - three in-test respellings of the CF-4 kind, which T026's measurement found outside the seventeen (comment 6016415390 point 2): the gate verbs read from openxdox.cli_gate in two tests, and the sync script read at its one home on the import path. * tests/test_protected_suite_check.py: the span kind's cases (T026's Files gain it, the holder's answer on the same ruling). * tests/protected_suite_respellings.yaml: the header names the six spans, the helper-function form, and batch Q's --chains for 12.5's call. T020's registration of tests/test_staging_workbench.py (its tests/conftest.py and the scan in tests/test_host_plane.py) is not in this commit: it is made after opensoft/openXdox-code#39 lands. Refs opensoft/openxFactory#656 (CLAIMED 6015602254; questions 6016415390; ruling 6016648451). Lane: openXfactory-3 Co-Authored-By: Claude Opus 5.5 --- scripts/protected_suites.py | 47 +- tests/protected_suite_respellings.yaml | 582 ++++++++++++++++++++++++- tests/test_protected_suite_check.py | 171 ++++++++ tests/test_staging_workbench.py | 120 ++++- 4 files changed, 871 insertions(+), 49 deletions(-) diff --git a/scripts/protected_suites.py b/scripts/protected_suites.py index bab9374..68815fc 100644 --- a/scripts/protected_suites.py +++ b/scripts/protected_suites.py @@ -57,14 +57,18 @@ * A NAMED MODULE-LEVEL SPAN (plan 038 T026; R-1 (a), RULED by Brett Heap at openxFactory#656 comment 6013547504, "Admitted edit kind (Recommended)", and recorded at 12.5's falsifier by T005's batch Q). An `admitted` entry may - name, in place of `test`, a `span`: one module-level constant of the suite, - such as a harness text several tests run, which lies inside no test. Its - `old` text must then lie inside that constant's statement in the before - text, and its `new` text inside it in the after text, so the entry admits - an edit to that one constant and to nothing else of the suite. A span is - one module-level assignment to that one name, and it must exist before the - landing and at it: no span is added, and a function or class is never one. - A respelling names its test, never a span. + name, in place of `test`, a `span`: one module-level constant or helper + function of the suite, such as a harness text several tests run or the + helper that runs it, which lies inside no test. Comment 6016648451 ("Widen + the spans, served display (Recommended)") names three helpers among the six + spans, so a function may be one. Its `old` text must then lie inside that + constant's or function's statement in the before text, and its `new` text + inside it in the after text, so the entry admits an edit to that one + statement and to nothing else of the suite. A span is one module-level + assignment to that one name, or one module-level function of that name, + and it must exist before the landing and at it: no span is added, and a + class, an import or a second binding of the name is never one. A + respelling names its test, never a span, and a test is never a span. A landing that touches a protected suite is admitted for that suite only if one entry holds at it, or, in F5.2's call, a CHAIN of entries does. Every other @@ -144,7 +148,8 @@ EDIT_KEYS = {"respelling": frozenset({"respelled"}), "admitted": frozenset({"reason"})} #: R-1 (a) (plan 038 T026): the key an `admitted` entry names IN PLACE OF -#: `test` when its edit lies in a module-level constant of the suite. +#: `test` when its edit lies in a module-level constant or helper function of +#: the suite. SPAN_KEY = "span" SPAN_EDITS = frozenset({"admitted"}) @@ -324,12 +329,15 @@ def _test_lines(text: str, test: str) -> tuple[int, int] | None: def _span_lines(text: str, span: str) -> tuple[int, int] | None: - """The 1-based line span of the module-level constant `span`, or None. + """The 1-based line span of the module-level constant or function `span`, + or None. R-1 (a) (plan 038 T026): a span is ONE module-level assignment whose one - target is the bare name `span`, and no other module-level statement binds - that name, so a function, a class, an import or a second assignment is - never a span.""" + target is the bare name `span`, or ONE module-level function named `span` + (comment 6016648451 names three helpers among the spans), and no other + module-level statement binds that name. So a class, an import or a second + binding is never a span. A function's span starts at its first decorator, + as a test's does.""" try: tree = ast.parse(text) except SyntaxError: @@ -338,6 +346,8 @@ def _span_lines(text: str, span: str) -> tuple[int, int] | None: if len(binders) != 1 or binders[0] not in tree.body: return None node = binders[0] + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + return min([node.lineno, *(d.lineno for d in node.decorator_list)]), node.end_lineno if isinstance(node, ast.Assign): single = len(node.targets) == 1 and isinstance(node.targets[0], ast.Name) else: @@ -391,7 +401,8 @@ def _inside_the_test(text: str, start: int, piece: str, test: str) -> bool: def _inside_the_span(text: str, start: int, piece: str, span: str) -> bool: """`_inside_the_test`'s rule, amended by R-1 (a) to admit the new kind: - `piece` lies inside the one module-level constant `span` names.""" + `piece` lies inside the one module-level constant or function `span` + names.""" return _inside(_span_lines(text, span), text, start, piece) @@ -507,11 +518,13 @@ def _edit_holds(before_text: str, after_text: str, entry: dict) -> str | None: if before_text.replace(old, new, 1) != after_text: return "replacing the entry's old text with its new text does not give the suite at the landing" if SPAN_KEY in entry: - # R-1 (a): the edit lies inside the one module-level constant the entry - # names, before the landing and at it. No span is ever added. + # R-1 (a): the edit lies inside the one module-level constant or + # function the entry names, before the landing and at it. No span is + # ever added. span = entry[SPAN_KEY] if _span_lines(before_text, span) is None: - return f"{span} is not one module-level constant before the landing" + return (f"{span} is not one module-level constant or function " + "before the landing") if not _inside_the_span(before_text, at, old, span): return f"the entry's old text is not inside {span} before the landing" if not _inside_the_span(after_text, at, new, span): diff --git a/tests/protected_suite_respellings.yaml b/tests/protected_suite_respellings.yaml index 27d991d..c42bcd8 100644 --- a/tests/protected_suite_respellings.yaml +++ b/tests/protected_suite_respellings.yaml @@ -22,7 +22,9 @@ # weakened (R1Q7 (a), batch C). For 12.5's governed suites that includes # the seventeen respellings of names the split-opendox CARVE moved before # the arc (CF-4, H-2, confirmed with plan 038's ruling at `6013547504`; -# batch Q), and the 'cluster' node's in-test entry (R-1 (a)'s note). +# batch Q), three more of that kind in `tests/test_staging_workbench.py` +# that T026's measurement found (`6016415390` point 2, ruled at +# `6016648451`), and the 'cluster' node's in-test entry (R-1 (a)'s note). # * The two edits F7.1 requires (R1Q14 (a), `5850003126`, batch F), in # `tests/test_snapshot.py` and `tests/test_snapshot_validator_home.py`. # * The two edits 5.3a's `values` block requires (R1Q26 (a), `5851950767`, @@ -32,8 +34,12 @@ # `tests/test_snapshot.py`: each one's walk premise becomes 7.3's answer, # and its assertions stay. # * An edit to a NAMED MODULE-LEVEL SPAN of one of 12.5's governed suites, -# such as a harness text several tests run, which lies inside no test -# (R-1 (a), RULED with plan 038 at `6013547504`; batch Q). +# such as a harness text several tests run or the helper that runs it, +# which lies inside no test (R-1 (a), RULED with plan 038 at +# `6013547504`; batch Q). The named spans are six, as `6016648451` +# widened them: `_CREATE_HARNESS`, `_SESSION_HARNESS` and +# `_HOSTILE_HARNESS`, and the helpers `_run_create`, `_run_session` and +# `_hostile_descriptors`, all in `tests/test_staging_workbench.py`. # The F7.1, 5.3a, batch K and span kinds are each entered with a reason, and # none is entered as a respelling. # @@ -56,8 +62,8 @@ # * `entries` are in landing order, one per edit. Each entry names: # - `suite`, the protected file, written plainly (`tests/`); # - `test`, the one test the edit changes, or, for an admitted edit to a -# module-level constant (R-1 (a)), `span`, that constant's name, in -# its place; +# module-level constant or helper function (R-1 (a)), `span`, that +# name, in its place; # - `landing`, the pull request that lands the edit, # `opensoft/openXdox-code#`, because the landing's commit is not # known inside it (T019's rule); @@ -82,10 +88,12 @@ # entry cannot admit an edit to another test of the suite. # * A SPAN (plan 038 T026; R-1 (a)). For an entry that names `span`, the # fourth condition reads the span for the test: `old` lies inside that -# constant's statement in the `before_blob` text, and `new` inside it in -# the `after_blob` text. A span is one module-level assignment to one -# bare name that nothing else in module scope binds, it exists before the -# edit and after it, and only an admitted entry names one. +# constant's or function's statement in the `before_blob` text, and `new` +# inside it in the `after_blob` text. A span is one module-level +# assignment to one bare name, or one module-level function of that name, +# that nothing else in module scope binds; it is never a test or a class, +# it exists before the edit and after it, and only an admitted entry names +# one. # * AN ADDED TEST (plan 034 T061; batch F admits one). Where `test` does not # exist in the `before_blob` text, the fourth condition reads instead: `new` # is `old` followed by that test's whole definition and blank lines, and @@ -93,16 +101,17 @@ # * Entries for one suite CHAIN: each one's `before_blob` is the previous # one's `after_blob`, because each edit is made to the suite the last one # left. -# * SEVERAL EDITS IN ONE LANDING (plan 034 T061; batch K), in F5.2's call -# alone (`--chains`; 12.5's keeps one entry per suite per landing). A -# landing that edits one suite in several tests enters each edit as its -# own entry, in the order they apply, all naming that landing. The first -# one's `before_blob` is the suite before the landing and the last one's -# `after_blob` the suite at it. Each one between records, as its -# `after_blob`, the blob id of the text its edit leaves (`git -# hash-object`; no commit need hold it). Each holds on its own texts by the -# conditions above, so the landing's diff for the suite is exactly their -# texts, each inside its own test. +# * SEVERAL EDITS IN ONE LANDING (plan 034 T061; batch K), in a call that +# passes `--chains`: F5.2's since batch K, and 12.5's from batch Q (RULED +# at `6016648451`; a call without it keeps one entry per suite per +# landing). A landing that edits one suite in several tests, or spans, +# enters each edit as its own entry, in the order they apply, all naming +# that landing. The first one's `before_blob` is the suite before the +# landing and the last one's `after_blob` the suite at it. Each one between +# records, as its `after_blob`, the blob id of the text its edit leaves +# (`git hash-object`; no commit need hold it). Each holds on its own texts +# by the conditions above, so the landing's diff for the suite is exactly +# their texts, each inside its own test or span. # * No entry weakens an assertion. The pull request that adds an entry is # reviewed on exactly that basis (batch C). @@ -1925,3 +1934,538 @@ entries: # the create column's own predicate since section 3.4 slice S5 # (`createColumn`; plan 038 T026, under batch Q's CF-4) pill = view.split("const gateOn = createColumn.createGateLive(caps);", 1)[1].split( + - suite: tests/test_staging_workbench.py + span: _CREATE_HARNESS + landing: opensoft/openXdox-code#43 + edit: admitted + reason: >- + The create harness's imports, as the product now places them: + `CREATE_ROUTE` from the create column, `swb-create.js`, where RULED Q3 + (opensoft/openxFactory#656 comment 5642758731) moved it, and the display + reader from `display.js`; the model's `BRAINSTORM_AREA` and + `STAGING_AREA` are its `capturedArea` and `organizedArea` readers since + slice S7. The harness installs the display the helper hands it + (`setDisplay(readDisplay({ display: cases.display }))`) before anything + derives, as the shell does. Since section 3.4 slice S7 (openDox-code + `1e46971`) the model spells its areas and statuses in the vocabulary the + shell installs at mount (`setDisplay`), so a harness that installs none + reads openDox's neutral words (area `''`, status `source`), which the + gate route and the real CLI parser refuse. + ruled: >- + R-1 (a), RULED by Brett Heap with plan 038 at opensoft/openxFactory#656 + comment 6013547504 ("Admitted edit kind (Recommended)") and recorded at + #1144's 12.5 falsifier by plan 038's T005 (batch Q). Its named spans are + widened to six by comment 6016648451 ("Widen the spans, served display + (Recommended)"): `_CREATE_HARNESS`, `_SESSION_HARNESS`, `_run_create`, + `_run_session`, `_hostile_descriptors` and `_HOSTILE_HARNESS`, the + helpers copying the model's siblings and handing each harness the SERVED + governed display. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test or span, and this step's is `old` to + `new`, inside the span named. Every derivation the harness runs, and + every value it reports, is unchanged; it now runs in the vocabulary the + served page runs in. No test's assertion is touched, and none is + weakened. + before_blob: 4bf069cdf4177e9dc65fe4051be015d9e3ed06fa + after_blob: 0c523554ce35d4438633504c53e171ed4f56ec76 + old: |2 + createSource, createDocumentCommand, CREATE_ROUTE, CREATE_TABS, + BRAINSTORM_AREA, STAGING_AREA, + } from './staging-workbench-model.mjs'; + import { readFileSync } from 'node:fs'; + const cases = JSON.parse(readFileSync(process.argv[2], 'utf8')); + new: |2 + createSource, createDocumentCommand, CREATE_TABS, + capturedArea, organizedArea, setDisplay, + } from './staging-workbench-model.mjs'; + // the create route is the create column's own since RULED Q3, and the display + // reader is display.js's (plan 038 T026, R-1 (a)) + import { CREATE_ROUTE } from './swb-create.js'; + import { readDisplay } from './display.js'; + import { readFileSync } from 'node:fs'; + const cases = JSON.parse(readFileSync(process.argv[2], 'utf8')); + // THE SERVED DISPLAY, installed before anything derives, as the shell installs + // it at mount (section 3.4 slice S7): the areas and statuses below are the + // registered host's words, as the page reads them + setDisplay(readDisplay({ display: cases.display })); + - suite: tests/test_staging_workbench.py + span: _CREATE_HARNESS + landing: opensoft/openXdox-code#43 + edit: admitted + reason: >- + The two area constants the harness reports, read through the model's + slice S7 readers: `BRAINSTORM_AREA` and `STAGING_AREA` to + `capturedArea()` and `organizedArea()`, which answer the installed + display's captured and organized areas. + ruled: >- + R-1 (a), RULED by Brett Heap with plan 038 at opensoft/openxFactory#656 + comment 6013547504 ("Admitted edit kind (Recommended)") and recorded at + #1144's 12.5 falsifier by plan 038's T005 (batch Q). Its named spans are + widened to six by comment 6016648451 ("Widen the spans, served display + (Recommended)"): `_CREATE_HARNESS`, `_SESSION_HARNESS`, `_run_create`, + `_run_session`, `_hostile_descriptors` and `_HOSTILE_HARNESS`, the + helpers copying the model's siblings and handing each harness the SERVED + governed display. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test or span, and this step's is `old` to + `new`, inside the span named. The reported keys are unchanged, and under + the served display the values are the same two areas. No test's + assertion is touched, and none is weakened. + before_blob: 0c523554ce35d4438633504c53e171ed4f56ec76 + after_blob: 2d2cde03dc86c53d7cf17ee935fef4b638fb4540 + old: |2 + areas: { brainstorm: BRAINSTORM_AREA, staging: STAGING_AREA }, + new: |2 + areas: { brainstorm: capturedArea(), staging: organizedArea() }, + - suite: tests/test_staging_workbench.py + span: _run_create + landing: opensoft/openXdox-code#43 + edit: admitted + reason: >- + The create helper copies the model's siblings the harness now imports + (`display.js`; `swb-create.js`, which carries `CREATE_ROUTE` since RULED + Q3, and the `helpers.js` and `dispose.js` it imports), with a + `package.json` that makes node read them as modules, and hands the + harness the SERVED display: `/capabilities["display"]`, by the statement + `serve.build_server()` makes under the registered host. Since section + 3.4 slice S7 (openDox-code `1e46971`) the model spells its areas and + statuses in the vocabulary the shell installs at mount (`setDisplay`), + so a harness that installs none reads openDox's neutral words (area + `''`, status `source`), which the gate route and the real CLI parser + refuse. + ruled: >- + R-1 (a), RULED by Brett Heap with plan 038 at opensoft/openxFactory#656 + comment 6013547504 ("Admitted edit kind (Recommended)") and recorded at + #1144's 12.5 falsifier by plan 038's T005 (batch Q). Its named spans are + widened to six by comment 6016648451 ("Widen the spans, served display + (Recommended)"): `_CREATE_HARNESS`, `_SESSION_HARNESS`, `_run_create`, + `_run_session`, `_hostile_descriptors` and `_HOSTILE_HARNESS`, the + helpers copying the model's siblings and handing each harness the SERVED + governed display. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test or span, and this step's is `old` to + `new`, inside the span named. The helper still copies the one model and + still runs the harness on the same cases; it adds the siblings and the + display beside them. No test's assertion is touched, and none is + weakened. + before_blob: 2d2cde03dc86c53d7cf17ee935fef4b638fb4540 + after_blob: 432b867049e8b51598faa3b2f75f7b419d2a8c23 + old: |2 + shutil.copy(MODEL_JS, tmp_path / "staging-workbench-model.mjs") + (tmp_path / "harness.mjs").write_text(_CREATE_HARNESS, encoding="utf-8") + cases_path = tmp_path / "create-cases.json" + cases_path.write_text(json.dumps({"scopes": cases, "areas": _STATUS_AREAS, + "toggles": _TOGGLES, "sources": _SOURCES}), + encoding="utf-8") + new: |2 + shutil.copy(MODEL_JS, tmp_path / "staging-workbench-model.mjs") + # THE MODEL'S SIBLINGS AND THE SERVED DISPLAY (plan 038 T026; R-1 (a), its + # spans widened at openxFactory#656 comment 6016648451). RULED Q3 moved + # `CREATE_ROUTE` into the create column, `swb-create.js`, and since section + # 3.4 slice S7 the model spells its areas and statuses in the vocabulary the + # shell installs (`setDisplay`). So the harness imports the route from + # `swb-create.js` and the display reader from `display.js`, copied beside + # the model under their own names with what they import, and + # `package.json` makes node read `.js` as a module, as + # `test_session_confinement.py`'s harness does. It is handed the display + # `/capabilities` serves: the statement `serve.build_server()` makes, under + # the host this run registers, replayed as `test_gate_loop_views.py`'s + # `_served_display` replays it. + for sibling in ("display.js", "helpers.js", "dispose.js", "swb-create.js"): + shutil.copy(WEB / "views" / sibling, tmp_path / sibling) + (tmp_path / "package.json").write_text('{"type": "module"}', encoding="utf-8") + from opendox import display_profile, view_extension + from opendox.profile_proxy import profile_openxfactory + + display = display_profile.display_manifest( + display_profile.host_display(profile_openxfactory), + host_profile=view_extension.host_profile_name(profile_openxfactory)) + (tmp_path / "harness.mjs").write_text(_CREATE_HARNESS, encoding="utf-8") + cases_path = tmp_path / "create-cases.json" + cases_path.write_text(json.dumps({"scopes": cases, "areas": _STATUS_AREAS, + "toggles": _TOGGLES, "sources": _SOURCES, + "display": display}), + encoding="utf-8") + - suite: tests/test_staging_workbench.py + test: test_gate_off_descriptor_is_the_real_cli_invocation + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The gate verb's home: `cli.cmd_gate_create_document` to + `cli_gate.cmd_gate_create_document`, imported inside the test from + `openxdox.cli_gate`, where the split-opendox carve's section 2.4 moved + openDox's gate verbs (a commit with no `Arc:` trailer). `opendox.cli` no + longer carries the name, so the old spelling raised AttributeError. The + parser stays openDox's `cli.build_parser()`. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). The + residue sits in this suite, outside the seventeen plan 038 names; T026's + measurement reported it at comment 6016415390 (point 2), and comment + 6016648451 ruled that point. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. The descriptor is still parsed by the real + parser, and it must still dispatch to the create-document verb, with + every argument assertion unchanged. No assertion is weakened. + before_blob: 432b867049e8b51598faa3b2f75f7b419d2a8c23 + after_blob: 23eb19a86cb5cdbcf534060040b5ebec7a9b90f1 + old: |2 + assert args.func is cli.cmd_gate_create_document + new: |2 + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the parser stays + # openDox's `cli`, carrying them under the registered host. + from openxdox import cli_gate + + assert args.func is cli_gate.cmd_gate_create_document + - suite: tests/test_staging_workbench.py + span: _SESSION_HARNESS + landing: opensoft/openXdox-code#43 + edit: admitted + reason: >- + The session harness's imports, as the product now places them: + `sessionRoute` from the session column, `swb-session.js`, where RULED Q3 + (opensoft/openxFactory#656 comment 5642758731) moved the session routes, + and the display reader from `display.js`, with the model's `setDisplay`. + The harness installs the display the helper hands it before anything + derives, as the shell does. Since section 3.4 slice S7 (openDox-code + `1e46971`) the model spells its areas and statuses in the vocabulary the + shell installs at mount (`setDisplay`), so a harness that installs none + reads openDox's neutral words (area `''`, status `source`), which the + gate route and the real CLI parser refuse. + ruled: >- + R-1 (a), RULED by Brett Heap with plan 038 at opensoft/openxFactory#656 + comment 6013547504 ("Admitted edit kind (Recommended)") and recorded at + #1144's 12.5 falsifier by plan 038's T005 (batch Q). Its named spans are + widened to six by comment 6016648451 ("Widen the spans, served display + (Recommended)"): `_CREATE_HARNESS`, `_SESSION_HARNESS`, `_run_create`, + `_run_session`, `_hostile_descriptors` and `_HOSTILE_HARNESS`, the + helpers copying the model's siblings and handing each harness the SERVED + governed display. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test or span, and this step's is `old` to + `new`, inside the span named. Every derivation the harness runs, and + every value it reports, is unchanged; it now runs in the vocabulary the + served page runs in. No test's assertion is touched, and none is + weakened. + before_blob: 23eb19a86cb5cdbcf534060040b5ebec7a9b90f1 + after_blob: 42e5694556172eb21d559bad79b2715a3128984e + old: |2 + sessionActionsLive, sessionSurfaceHidden, sessionRoute, createSeed, createRequest, + createDocumentCommand, normalizeContinuation, + advertisedTiles, otherTileBranches, + SESSION_AFFORDANCES, LIVE_SESSION_AFFORDANCES, DESCRIPTOR_ONLY_AFFORDANCES, + SESSION_VERBS, SESSION_LABELS, CONTINUATIONS, MAIN_REF, + } from './staging-workbench-model.mjs'; + import { readFileSync } from 'node:fs'; + const cases = JSON.parse(readFileSync(process.argv[2], 'utf8')); + new: |2 + sessionActionsLive, sessionSurfaceHidden, createSeed, createRequest, + createDocumentCommand, normalizeContinuation, + advertisedTiles, otherTileBranches, + SESSION_AFFORDANCES, LIVE_SESSION_AFFORDANCES, DESCRIPTOR_ONLY_AFFORDANCES, + SESSION_VERBS, SESSION_LABELS, CONTINUATIONS, MAIN_REF, setDisplay, + } from './staging-workbench-model.mjs'; + // the session routes are the session column's own since RULED Q3, and the + // display reader is display.js's (plan 038 T026, R-1 (a)) + import { sessionRoute } from './swb-session.js'; + import { readDisplay } from './display.js'; + import { readFileSync } from 'node:fs'; + const cases = JSON.parse(readFileSync(process.argv[2], 'utf8')); + // THE SERVED DISPLAY, installed before anything derives, as the shell installs + // it at mount (section 3.4 slice S7) + setDisplay(readDisplay({ display: cases.display })); + - suite: tests/test_staging_workbench.py + span: _run_session + landing: opensoft/openXdox-code#43 + edit: admitted + reason: >- + The session helper copies the model's siblings the harness now imports + (`display.js`; `swb-session.js`, which carries the session routes since + RULED Q3, and the `helpers.js` and `dispose.js` it imports), with a + `package.json` that makes node read them as modules, and hands the + harness the SERVED display, as `_run_create` does. Since section 3.4 + slice S7 (openDox-code `1e46971`) the model spells its areas and + statuses in the vocabulary the shell installs at mount (`setDisplay`), + so a harness that installs none reads openDox's neutral words (area + `''`, status `source`), which the gate route and the real CLI parser + refuse. + ruled: >- + R-1 (a), RULED by Brett Heap with plan 038 at opensoft/openxFactory#656 + comment 6013547504 ("Admitted edit kind (Recommended)") and recorded at + #1144's 12.5 falsifier by plan 038's T005 (batch Q). Its named spans are + widened to six by comment 6016648451 ("Widen the spans, served display + (Recommended)"): `_CREATE_HARNESS`, `_SESSION_HARNESS`, `_run_create`, + `_run_session`, `_hostile_descriptors` and `_HOSTILE_HARNESS`, the + helpers copying the model's siblings and handing each harness the SERVED + governed display. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test or span, and this step's is `old` to + `new`, inside the span named. The helper still copies the one model and + still runs the harness on the same cases; it adds the siblings and the + display beside them. No test's assertion is touched, and none is + weakened. + before_blob: 42e5694556172eb21d559bad79b2715a3128984e + after_blob: 4eef7231cb5535ce67eb3621d6dbfb491bad873a + old: |2 + shutil.copy(MODEL_JS, tmp_path / "staging-workbench-model.mjs") + (tmp_path / "harness.mjs").write_text(_SESSION_HARNESS, encoding="utf-8") + cases_path = tmp_path / "session-cases.json" + cases_path.write_text(json.dumps({ + "snapshot": SNAP, "siblingSnapshot": SIBLING_SNAP, + "postures": _POSTURE_CASES, "branches": _BRANCH_CASES, + "ordinals": _ORDINAL_CASES, "caps": _CAP_CASES, "refs": _REF_CASES, + "family": _FAMILY_CASES, "values": _SESSION_VALUES, + "continuations": _CONTINUATION_CASES, + "inventories": ["main", "sibling"], + "others": _OTHER_TILE_CASES, + }), encoding="utf-8") + new: |2 + shutil.copy(MODEL_JS, tmp_path / "staging-workbench-model.mjs") + # THE MODEL'S SIBLINGS AND THE SERVED DISPLAY, as `_run_create` takes them + # (plan 038 T026; R-1 (a), its spans widened at openxFactory#656 comment + # 6016648451): RULED Q3 moved the session routes into the session column, + # `swb-session.js`, so the harness imports `sessionRoute` from there, and it + # installs the display `/capabilities` serves. + for sibling in ("display.js", "helpers.js", "dispose.js", "swb-session.js"): + shutil.copy(WEB / "views" / sibling, tmp_path / sibling) + (tmp_path / "package.json").write_text('{"type": "module"}', encoding="utf-8") + from opendox import display_profile, view_extension + from opendox.profile_proxy import profile_openxfactory + + display = display_profile.display_manifest( + display_profile.host_display(profile_openxfactory), + host_profile=view_extension.host_profile_name(profile_openxfactory)) + (tmp_path / "harness.mjs").write_text(_SESSION_HARNESS, encoding="utf-8") + cases_path = tmp_path / "session-cases.json" + cases_path.write_text(json.dumps({ + "snapshot": SNAP, "siblingSnapshot": SIBLING_SNAP, + "postures": _POSTURE_CASES, "branches": _BRANCH_CASES, + "ordinals": _ORDINAL_CASES, "caps": _CAP_CASES, "refs": _REF_CASES, + "family": _FAMILY_CASES, "values": _SESSION_VALUES, + "continuations": _CONTINUATION_CASES, + "inventories": ["main", "sibling"], + "others": _OTHER_TILE_CASES, + "display": display, + }), encoding="utf-8") + - suite: tests/test_staging_workbench.py + test: test_gate_off_session_affordances_are_the_real_cli_invocations + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + The gate verbs' home: `cli.cmd_gate_edit_document`, + `cli.cmd_gate_open_pr` and `cli.cmd_gate_abandon_session` to + `cli_gate.…`, imported inside the test from `openxdox.cli_gate`, where + the split-opendox carve's section 2.4 moved openDox's gate verbs (a + commit with no `Arc:` trailer). `opendox.cli` no longer carries the + names, so the old spelling raised AttributeError. The parser stays + openDox's `cli.build_parser()`. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). The + residue sits in this suite, outside the seventeen plan 038 names; T026's + measurement reported it at comment 6016415390 (point 2), and comment + 6016648451 ruled that point. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. Each descriptor is still parsed by the real + parser and must still dispatch to its own verb, with every argument + assertion unchanged. No assertion is weakened. + before_blob: 4eef7231cb5535ce67eb3621d6dbfb491bad873a + after_blob: 63ae2d45d3287eec07bf8f1d20a5a5585502f3dd + old: |2 + expected = {"edit": cli.cmd_gate_edit_document, + "save": cli.cmd_gate_open_pr, + "abandon": cli.cmd_gate_abandon_session} + new: |2 + # THE VERB'S OWN HOME (plan 038 T026, under batch Q's CF-4). The + # split-opendox carve (section 2.4) moved the gate verbs to + # `openxdox.cli_gate`, so the verb is read there; the parser stays + # openDox's `cli`, carrying them under the registered host. + from openxdox import cli_gate + + expected = {"edit": cli_gate.cmd_gate_edit_document, + "save": cli_gate.cmd_gate_open_pr, + "abandon": cli_gate.cmd_gate_abandon_session} + - suite: tests/test_staging_workbench.py + test: test_the_notebook_refresh_is_a_descriptor_in_BOTH_gate_postures + landing: opensoft/openXdox-code#43 + edit: respelling + respelled: >- + Where the sync script is read: `REPO_ROOT / "scripts" / + "sync-notebooklm-books.py"`, the pre-carve home, to the one directory on + the import path that holds it. The carve left the script openxFactory's + (`scripts/`, not carved), and the composed run puts openxFactory's + `scripts/` on the path (R1Q23 (a)); this leg's `scripts/` has no such + file, so the old path raised FileNotFoundError. + ruled: >- + R1Q7 (a), opensoft/openxFactory#656 comment 5817152735 (batch C), for + pre-arc carve residue as CF-4 (H-2) confirms it with plan 038's ruling + at comment 6013547504, on the precedent of comment 5962785556 item 1. + Recorded at #1144's 12.5 falsifier by plan 038's T005 (batch Q). The + residue sits in this suite, outside the seventeen plan 038 names; T026's + measurement reported it at comment 6016415390 (point 2), and comment + 6016648451 ruled that point. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test, and this step's is `old` to `new`, + inside the test named. Both flags are still asserted to be the script's + own, read from the script itself, and a run that finds the script in + none of the path's directories, or in more than one, now FAILS. No + assertion is weakened. + before_blob: 63ae2d45d3287eec07bf8f1d20a5a5585502f3dd + after_blob: 8f4807562a114d9dbc97fba68fbc947e961db31a + old: |2 + sync = (REPO_ROOT / "scripts" / "sync-notebooklm-books.py").read_text(encoding="utf-8") + new: |2 + # THE SCRIPT'S OWN HOME (plan 038 T026, under batch Q's CF-4). The carve + # left the sync script openxFactory's, in its `scripts/`, which the composed + # run puts on the import path (R1Q23 (a)); this leg's `scripts/` has none. + # It is read where it is found, and a run that finds it in none of the + # path's directories, or in more than one, fails. + import sys + + homes = {path.resolve() for path in (Path(entry) / "sync-notebooklm-books.py" + for entry in sys.path if entry) + if path.is_file()} + assert len(homes) == 1, homes + sync = homes.pop().read_text(encoding="utf-8") + - suite: tests/test_staging_workbench.py + span: _HOSTILE_HARNESS + landing: opensoft/openXdox-code#43 + edit: admitted + reason: >- + The hostile harness installs the display the helper hands it + (`setDisplay(readDisplay({ display: cases.display }))`) before anything + derives, as the shell does, importing the reader from `display.js` and + the model's `setDisplay`. Without it the create descriptor carried + `--status source`, the neutral word, and the real parser exited 2 + (`invalid choice: 'source'`) before the quoting the test is about was + reached. Since section 3.4 slice S7 (openDox-code `1e46971`) the model + spells its areas and statuses in the vocabulary the shell installs at + mount (`setDisplay`), so a harness that installs none reads openDox's + neutral words (area `''`, status `source`), which the gate route and the + real CLI parser refuse. + ruled: >- + R-1 (a), RULED by Brett Heap with plan 038 at opensoft/openxFactory#656 + comment 6013547504 ("Admitted edit kind (Recommended)") and recorded at + #1144's 12.5 falsifier by plan 038's T005 (batch Q). Its named spans are + widened to six by comment 6016648451 ("Widen the spans, served display + (Recommended)"): `_CREATE_HARNESS`, `_SESSION_HARNESS`, `_run_create`, + `_run_session`, `_hostile_descriptors` and `_HOSTILE_HARNESS`, the + helpers copying the model's siblings and handing each harness the SERVED + governed display. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test or span, and this step's is `old` to + `new`, inside the span named. Every descriptor is still built through + the real seeding path with the same hostile values, and every quoting + and parsing assertion on it is unchanged. No test's assertion is + touched, and none is weakened. + before_blob: 8f4807562a114d9dbc97fba68fbc947e961db31a + after_blob: 75a082f13c4056c7484dba9c9efb10cd10f27ea6 + old: |2 + notebookRefreshCommand, + } from './staging-workbench-model.mjs'; + import { readFileSync } from 'node:fs'; + const cases = JSON.parse(readFileSync(process.argv[2], 'utf8')); + new: |2 + notebookRefreshCommand, setDisplay, + } from './staging-workbench-model.mjs'; + // the display reader is display.js's (plan 038 T026, R-1 (a)) + import { readDisplay } from './display.js'; + import { readFileSync } from 'node:fs'; + const cases = JSON.parse(readFileSync(process.argv[2], 'utf8')); + // THE SERVED DISPLAY, installed before anything derives, as the shell installs + // it at mount (section 3.4 slice S7) + setDisplay(readDisplay({ display: cases.display })); + - suite: tests/test_staging_workbench.py + span: _hostile_descriptors + landing: opensoft/openXdox-code#43 + edit: admitted + reason: >- + The hostile helper copies `display.js`, the sibling the harness now + imports, with a `package.json` that makes node read it as a module, and + hands the harness the SERVED display, as `_run_create` does. Since + section 3.4 slice S7 (openDox-code `1e46971`) the model spells its areas + and statuses in the vocabulary the shell installs at mount + (`setDisplay`), so a harness that installs none reads openDox's neutral + words (area `''`, status `source`), which the gate route and the real + CLI parser refuse. + ruled: >- + R-1 (a), RULED by Brett Heap with plan 038 at opensoft/openxFactory#656 + comment 6013547504 ("Admitted edit kind (Recommended)") and recorded at + #1144's 12.5 falsifier by plan 038's T005 (batch Q). Its named spans are + widened to six by comment 6016648451 ("Widen the spans, served display + (Recommended)"): `_CREATE_HARNESS`, `_SESSION_HARNESS`, `_run_create`, + `_run_session`, `_hostile_descriptors` and `_HOSTILE_HARNESS`, the + helpers copying the model's siblings and handing each harness the SERVED + governed display. + review: >- + Reviewed in the landing pull request on batch C's basis. It is one step + of the chain this landing's entries for the suite make, in order: the + landing's diff for this suite is exactly the chain's recorded texts, + each inside its own named test or span, and this step's is `old` to + `new`, inside the span named. The helper still copies the one model and + still runs the harness on the same snapshot and hostile values; it adds + the sibling and the display beside them. No test's assertion is touched, + and none is weakened. + before_blob: 75a082f13c4056c7484dba9c9efb10cd10f27ea6 + after_blob: 5a32a265ffac872c886a34a4d7a29e42c48f8bc9 + old: |2 + shutil.copy(MODEL_JS, tmp_path / "staging-workbench-model.mjs") + (tmp_path / "hostile.mjs").write_text(_HOSTILE_HARNESS, encoding="utf-8") + cases = tmp_path / "hostile-cases.json" + cases.write_text(json.dumps({"snapshot": SNAP, "values": _HOSTILE_VALUES}), + encoding="utf-8") + new: |2 + shutil.copy(MODEL_JS, tmp_path / "staging-workbench-model.mjs") + # THE SERVED DISPLAY, as `_run_create` takes it (plan 038 T026; R-1 (a), its + # spans widened at openxFactory#656 comment 6016648451): the create + # descriptor carries an area and a status, which the model spells in the + # vocabulary the shell installs since section 3.4 slice S7, so the harness + # installs the display `/capabilities` serves, through `display.js`. + shutil.copy(WEB / "views" / "display.js", tmp_path / "display.js") + (tmp_path / "package.json").write_text('{"type": "module"}', encoding="utf-8") + from opendox import display_profile, view_extension + from opendox.profile_proxy import profile_openxfactory + + display = display_profile.display_manifest( + display_profile.host_display(profile_openxfactory), + host_profile=view_extension.host_profile_name(profile_openxfactory)) + (tmp_path / "hostile.mjs").write_text(_HOSTILE_HARNESS, encoding="utf-8") + cases = tmp_path / "hostile-cases.json" + cases.write_text(json.dumps({"snapshot": SNAP, "values": _HOSTILE_VALUES, + "display": display}), + encoding="utf-8") diff --git a/tests/test_protected_suite_check.py b/tests/test_protected_suite_check.py index 7190098..0e4b4e8 100644 --- a/tests/test_protected_suite_check.py +++ b/tests/test_protected_suite_check.py @@ -22,6 +22,11 @@ (`--chains`), and only where every step holds on its own texts, the steps in between record the blob of the text they leave, and the chain's texts are the landing's diff and nothing else. 12.5's call refuses a chain. +* an edit to a NAMED MODULE-LEVEL SPAN (plan 038 T026; R-1 (a), its spans + widened at openxFactory#656 comment 6016648451) is admitted only where it + lies inside that one module-level constant or helper function, before the + landing and at it, and only an admitted entry names one. No span is added, + and a class, an import, a test or a name bound twice is never one. The last cases hold this repository's own allow-list to those rules. Which landing each entry holds at is the falsifier's to show, at the head it runs @@ -525,3 +530,169 @@ def test_a_respelling_names_what_it_respelled(tmp_path) -> None: def test_this_repositorys_allow_list_keeps_its_rules() -> None: assert ps.load_allow_list(REPO_ROOT / ps.ALLOW_LIST), "the allow-list enters no edit" + + +# -------------------------------------------------------------------------- +# R-1 (a)'s span kind (plan 038 T026) +# -------------------------------------------------------------------------- + +SPAN_BEFORE = textwrap.dedent('''\ + import functools + import json + + HARNESS = """ + import { a } from './m.mjs'; + console.log(a); + """ + + TWICE = "one" + TWICE = "two" + + if True: + NESTED = "n" + + SHADOWED = "s" + if json: + SHADOWED = "t" + + if json: + GUARDED = "g" + else: + GUARDED = "h" + + + class Harness: + TEXT = "c" + + + @functools.cache + def helper(): + return 1 + + + def test_first() -> None: + assert HARNESS and helper() +''') +WHOLE_HARNESS = '''HARNESS = """ +import { a } from './m.mjs'; +console.log(a); +""" +''' + + +def _span_entry(repo: Repo, old: str, new: str, *, span: str = "HARNESS") -> tuple[dict, str]: + """An admitted entry naming `span` for one edit of SPAN_BEFORE, and the + suite's text after it. The base is committed first, as no landing.""" + repo.commit({SUITE: SPAN_BEFORE}, "the span fixture, no landing") + after = SPAN_BEFORE.replace(old, new, 1) + entry = _entry(repo, before=SPAN_BEFORE, after=after, old=old, new=new) + del entry["test"] + entry["span"] = span + return entry, after + + +def _span_check(repo: Repo, old: str, new: str, *, span: str = "HARNESS") -> ps.Finding: + entry, after = _span_entry(repo, old, new, span=span) + repo.commit({SUITE: after}, f"edit\n\n{ARC}") + [finding] = _check(repo, [entry]) + return finding + + +def test_an_edit_spanning_a_whole_named_constant_is_admitted(repo) -> None: + whole_new = WHOLE_HARNESS.replace("{ a }", "{ b }").replace("log(a)", "log(b)") + finding = _span_check(repo, WHOLE_HARNESS, whole_new) + assert finding.admitted_by == 1, finding.why + + +def test_an_edit_inside_a_named_constant_is_admitted(repo) -> None: + finding = _span_check(repo, "console.log(a);\n", "console.log(a, a);\n") + assert finding.admitted_by == 1, finding.why + + +def test_an_edit_inside_a_named_helper_function_is_admitted(repo) -> None: + """Comment 6016648451 names three helpers among the six spans.""" + finding = _span_check(repo, " return 1\n", " return 2\n", span="helper") + assert finding.admitted_by == 1, finding.why + + +def test_a_helpers_decorator_is_inside_its_span(repo) -> None: + finding = _span_check(repo, "@functools.cache\n", "@functools.lru_cache\n", + span="helper") + assert finding.admitted_by == 1, finding.why + + +def test_an_old_text_outside_the_span_is_refused(repo) -> None: + finding = _span_check(repo, " assert HARNESS and helper()\n", + " assert HARNESS or helper()\n") + assert finding.admitted_by is None + assert "old text is not inside HARNESS before the landing" in finding.why + + +def test_a_new_text_that_leaves_the_span_is_refused(repo) -> None: + finding = _span_check(repo, 'console.log(a);\n"""\n', 'console.log(a);\n"""\nX = 1\n') + assert finding.admitted_by is None + assert "new text is not inside HARNESS at the landing" in finding.why + + +def test_a_new_text_that_leaves_a_helper_is_refused(repo) -> None: + finding = _span_check(repo, " return 1\n", " return 1\n\n\nX = 1\n", span="helper") + assert finding.admitted_by is None + assert "new text is not inside helper at the landing" in finding.why + + +def test_a_helper_renamed_at_the_landing_is_refused(repo) -> None: + finding = _span_check(repo, "def helper():\n", "def helper_two():\n", span="helper") + assert finding.admitted_by is None + assert "new text is not inside helper at the landing" in finding.why + + +@pytest.mark.parametrize("span, old, new", [ + ("Harness", ' TEXT = "c"\n', ' TEXT = "d"\n'), # a class + ("json", "import json\n", "import json # the module\n"), # an import + ("TWICE", 'TWICE = "one"\n', 'TWICE = "uno"\n'), # bound twice + ("GUARDED", ' GUARDED = "g"\n', ' GUARDED = "G"\n'), # twice, in blocks + ("NESTED", ' NESTED = "n"\n', ' NESTED = "m"\n'), # only in a block + ("SHADOWED", 'SHADOWED = "s"\n', 'SHADOWED = "S"\n'), # and in a block + ("ADDED", "import json\n", 'import json\n\nADDED = "x"\n'), # added +], ids=["class", "import", "twice", "twice-in-blocks", "block-only", "shadowed", "added"]) +def test_what_is_never_a_span_is_refused(repo, span, old, new) -> None: + finding = _span_check(repo, old, new, span=span) + assert finding.admitted_by is None + assert f"{span} is not one module-level constant or function before" in finding.why + + +def test_a_chain_of_a_test_entry_and_a_span_entry_is_admitted(repo) -> None: + """One landing, an edit in a test and one in a span: a chain, F5.2's call.""" + span_step, middle = _span_entry(repo, "console.log(a);\n", "console.log(a, a);\n") + both = middle.replace("assert HARNESS and helper()", "assert HARNESS or helper()") + test_step = _entry(repo, before=middle, after=both, test="test_first", + old=" assert HARNESS and helper()\n", + new=" assert HARNESS or helper()\n") + repo.commit({SUITE: both}, f"two edits\n\n{ARC}") + [finding] = _check(repo, [span_step, test_step], chains=True) + assert finding.admitted_by == 1, finding.why + assert finding.chain == (1, 2) + + +def test_a_span_entry_with_its_keys_loads(tmp_path) -> None: + entry = _valid_entry(span="_CREATE_HARNESS") + del entry["test"] + assert ps.load_allow_list(_write(tmp_path, { + "schema_version": 1, "kind": ps.KIND, "entries": [entry]})) == [entry] + + +@pytest.mark.parametrize("changes, why", [ + ({"edit": "respelling", "respelled": "a -> b"}, "only an admitted entry may"), + ({"test": "test_first"}, "carries"), + ({"span": "test_first"}, "not a module-level name, or is a test's"), + ({"span": "a.b"}, "not a module-level name, or is a test's"), +], ids=["respelling", "test-and-span", "a-test", "not-a-name"]) +def test_a_span_entry_that_breaks_the_rules_subtracts_nothing(tmp_path, changes, why) -> None: + entry = _valid_entry(span="_CREATE_HARNESS") + del entry["test"] + entry.update(changes) + if entry["edit"] == "respelling": + del entry["reason"] + with pytest.raises(ps.AllowListInvalid, match=why): + ps.load_allow_list(_write(tmp_path, { + "schema_version": 1, "kind": ps.KIND, "entries": [entry]})) diff --git a/tests/test_staging_workbench.py b/tests/test_staging_workbench.py index 4bf069c..5a32a26 100644 --- a/tests/test_staging_workbench.py +++ b/tests/test_staging_workbench.py @@ -496,11 +496,19 @@ def test_staged_scope_has_no_neighbourhood_when_no_linked_clusters(tmp_path): import { workbenchScope, lensScopeSnapshot, lensSessionSeed, scopeKey, toggleKeyword, createArea, createSeed, createOffered, createRequest, - createSource, createDocumentCommand, CREATE_ROUTE, CREATE_TABS, - BRAINSTORM_AREA, STAGING_AREA, + createSource, createDocumentCommand, CREATE_TABS, + capturedArea, organizedArea, setDisplay, } from './staging-workbench-model.mjs'; +// the create route is the create column's own since RULED Q3, and the display +// reader is display.js's (plan 038 T026, R-1 (a)) +import { CREATE_ROUTE } from './swb-create.js'; +import { readDisplay } from './display.js'; import { readFileSync } from 'node:fs'; const cases = JSON.parse(readFileSync(process.argv[2], 'utf8')); +// THE SERVED DISPLAY, installed before anything derives, as the shell installs +// it at mount (section 3.4 slice S7): the areas and statuses below are the +// registered host's words, as the page reads them +setDisplay(readDisplay({ display: cases.display })); const scopes = {}; for (const [id, snapshot, kind, tileId] of cases.scopes) { @@ -543,7 +551,7 @@ def test_staged_scope_has_no_neighbourhood_when_no_linked_clusters(tmp_path): } console.log(JSON.stringify({ scopes, route: CREATE_ROUTE, tabs: CREATE_TABS, - areas: { brainstorm: BRAINSTORM_AREA, staging: STAGING_AREA }, + areas: { brainstorm: capturedArea(), staging: organizedArea() }, // the `Status:` a create into each of these areas seeds — `brainstorm` // everywhere, per the ruling; the area is never consulted statuses: cases.areas.map((a) => [a, @@ -566,10 +574,32 @@ def _run_create(cases, tmp_path): if NODE is None: pytest.skip("node not available for the JS derivation probe") shutil.copy(MODEL_JS, tmp_path / "staging-workbench-model.mjs") + # THE MODEL'S SIBLINGS AND THE SERVED DISPLAY (plan 038 T026; R-1 (a), its + # spans widened at openxFactory#656 comment 6016648451). RULED Q3 moved + # `CREATE_ROUTE` into the create column, `swb-create.js`, and since section + # 3.4 slice S7 the model spells its areas and statuses in the vocabulary the + # shell installs (`setDisplay`). So the harness imports the route from + # `swb-create.js` and the display reader from `display.js`, copied beside + # the model under their own names with what they import, and + # `package.json` makes node read `.js` as a module, as + # `test_session_confinement.py`'s harness does. It is handed the display + # `/capabilities` serves: the statement `serve.build_server()` makes, under + # the host this run registers, replayed as `test_gate_loop_views.py`'s + # `_served_display` replays it. + for sibling in ("display.js", "helpers.js", "dispose.js", "swb-create.js"): + shutil.copy(WEB / "views" / sibling, tmp_path / sibling) + (tmp_path / "package.json").write_text('{"type": "module"}', encoding="utf-8") + from opendox import display_profile, view_extension + from opendox.profile_proxy import profile_openxfactory + + display = display_profile.display_manifest( + display_profile.host_display(profile_openxfactory), + host_profile=view_extension.host_profile_name(profile_openxfactory)) (tmp_path / "harness.mjs").write_text(_CREATE_HARNESS, encoding="utf-8") cases_path = tmp_path / "create-cases.json" cases_path.write_text(json.dumps({"scopes": cases, "areas": _STATUS_AREAS, - "toggles": _TOGGLES, "sources": _SOURCES}), + "toggles": _TOGGLES, "sources": _SOURCES, + "display": display}), encoding="utf-8") proc = subprocess.run([NODE, str(tmp_path / "harness.mjs"), str(cases_path)], capture_output=True, text=True, timeout=60) @@ -775,7 +805,13 @@ def test_gate_off_descriptor_is_the_real_cli_invocation(tmp_path): "gate create-document ") argv = shlex.split(command)[2:] # drop `python3