diff --git a/scripts/protected_suites.py b/scripts/protected_suites.py index bf0b6af..65adda5 100644 --- a/scripts/protected_suites.py +++ b/scripts/protected_suites.py @@ -21,10 +21,11 @@ python3 scripts/protected_suites.py --chains --landings="$(cat "$W/x-arc.txt")" \ --suites="$(cat "$W/gen-suites.txt")" # F5.2 - python3 scripts/protected_suites.py --landings="$(cat "$W/x-arc.txt")" \ + python3 scripts/protected_suites.py --chains --landings="$(cat "$W/x-arc.txt")" \ --suites="$(cat "$W/governed.txt")" # 12.5 -(`--chains` is F5.2's alone, since T061; see SEVERAL EDITS IN ONE LANDING.) +(`--chains` is F5.2's since T061, and 12.5's from T005's batch Q, RULED by Brett +Heap at openxFactory#656 comment 6016648451; see SEVERAL EDITS IN ONE LANDING.) Each option carries its LIST, one item per line, and never a path to one: the landings, each a full commit id as `git log --format=%H` prints it, and the @@ -54,9 +55,27 @@ 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 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, + that nothing else in the module binds, in any scope and in any binding + form, and that shares no line with another statement. 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 -protected path it touches is refused. +entry holds at it, or, in a call that passes `--chains`, a CHAIN of entries +does. Every other protected path it touches is refused. SEVERAL EDITS IN ONE LANDING (plan 034 T061; T007's batch K, on Brett's ruling at openxFactory#656 comment 5916000030). A landing may edit one suite in more @@ -80,11 +99,13 @@ each inside its own test, and nothing else. Every entry of the chain is spent by that landing. -F5.2'S CALL ALONE. Brett's ruling admits T061's ten under F5.2, whose protected -set they are in, and 12.5's governed set holds neither of their suites. So a -chain is admitted only when the call passes `--chains`, which F5.2's does and -12.5's does not. Without it, a landing's edits to one suite are admitted by one -entry or not at all, as T059 wired the check, and a chain is refused. +ONLY IN A CALL THAT PASSES `--chains`. F5.2's has since T061: Brett's ruling +admits T061's ten under F5.2, whose protected set they are in. 12.5's passes it +from T005's batch Q, RULED by Brett Heap at openxFactory#656 comment 6016648451, +because plan 038's T026 lands in one pull request several edits to each of five +of 12.5's governed suites. Without `--chains`, a landing's edits to one suite +are admitted by one entry or not at all, as T059 wired the check, and a chain is +refused. AN ENTRY ADMITS ONE LANDING (Copilot on openXdox-code#35). The landings are taken oldest first, and an entry that has admitted one is spent: a later @@ -96,7 +117,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 +152,11 @@ "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 or helper function 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 +165,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 +241,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 +261,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 +333,104 @@ 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 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`, or ONE module-level function named `span` + (comment 6016648451 names three helpers among the spans), and nothing else + in the module binds that name (`_bindings`). 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. + + A SPAN HOLDS ITS LINES ALONE (Copilot on openXdox-code#43). An entry's text + is whole lines, so a statement that shares a line with the span + (`HARNESS = "..."; OTHER = 1`) would let an edit inside the span's lines + change it too. Where another module-level statement starts or ends on the + span's lines, the name is not a span.""" + try: + tree = ast.parse(text) + except SyntaxError: + return None + if _bindings(tree, span) != 1: + return None + found = [node for node in tree.body if _binds_only(node, span)] + if len(found) != 1: + return None + node = found[0] + first = min([node.lineno, *(d.lineno for d in getattr(node, "decorator_list", []))]) + if any(other is not node and other.lineno <= node.end_lineno and first <= other.end_lineno + for other in tree.body): + return None + return first, node.end_lineno + + +def _binds_only(node: ast.stmt, name: str) -> bool: + """`node` is a module-level function named `name`, or an assignment whose + one target is the bare name `name`.""" + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + return node.name == name + if isinstance(node, ast.Assign): + return (len(node.targets) == 1 and isinstance(node.targets[0], ast.Name) + and node.targets[0].id == name) + return (isinstance(node, ast.AnnAssign) and node.value is not None + and isinstance(node.target, ast.Name) and node.target.id == name) + + +#: The binding forms that carry their name as a string field, where the +#: running Python has them (`match` captures; PEP 695 type parameters). +_NAMED_BINDERS = tuple(getattr(ast, kind) for kind in ( + "MatchAs", "MatchStar", "TypeVar", "ParamSpec", "TypeVarTuple") if hasattr(ast, kind)) + + +def _bindings(tree: ast.Module, name: str) -> int: + """How many times the module binds `name`, counted FAIL-CLOSED (Copilot on + openXdox-code#43): every binding form, in every scope of the module. An + assignment, augmented or annotated assignment, `for` or `with` target, a + walrus, or a `del` (each a stored or deleted `Name`); a function or class; + an import, and a star import, which may bind any name; an `except ... as` + alias; a `match` capture; a type parameter; and a parameter. Every scope + counts, so a function that rebinds the module's name through `global` + counts by the binding it makes, and a function's own local of the same + name counts too: a name bound twice anywhere is never taken for one.""" + count = 0 + for node in ast.walk(tree): + if isinstance(node, ast.Name): + count += isinstance(node.ctx, (ast.Store, ast.Del)) and node.id == name + elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): + count += node.name == name + elif isinstance(node, (ast.Import, ast.ImportFrom)): + count += sum(alias.name == "*" or (alias.asname or alias.name).split(".")[0] == name + for alias in node.names) + elif isinstance(node, ast.ExceptHandler): + count += node.name == name + elif isinstance(node, _NAMED_BINDERS): + count += node.name == name + elif isinstance(node, ast.MatchMapping): + count += node.rest == name + elif isinstance(node, ast.arg): + count += node.arg == name + return count + + +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 or function `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 +544,19 @@ 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 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 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): + 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. @@ -465,7 +611,8 @@ def _admitting(repo: Path, landing: str, path: str, entries: list[dict], def check(repo: Path, landings: list[str], protected: set[str], entries: list[dict], *, chains: bool = False) -> list[Finding]: """One finding per protected path each landing touched: admitted by the - entry (1-based) that holds there, or, with `chains` (F5.2's call), by the + entry (1-based) that holds there, or, with `chains` (F5.2's call, and + 12.5's from batch Q), by the chain of entries that does, or refused with every candidate's reason. The landings are taken oldest first, whatever order they come in, and each entry admits one of them at most.""" @@ -513,7 +660,8 @@ def main(argv: list[str] | None = None) -> int: help="the protected suites, one tests/test_.py per line") parser.add_argument("--chains", action="store_true", help="admit a chain of entries for one landing's several edits " - "to a suite (F5.2's call alone; plan 034 T061, batch K)") + "to a suite (F5.2's call since plan 034 T061, batch K; 12.5's " + "from batch Q, RULED at openxFactory#656 comment 6016648451)") try: args = parser.parse_args(sys.argv[1:] if argv is None else argv) except SystemExit as exc: diff --git a/tests/conftest.py b/tests/conftest.py index 5c0ad6a..51b83b4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -638,11 +638,19 @@ def _a_hosts_plane_for_the_token_reading_suites(request): # facets are read once with `sys.path` saved, `sys.path` is put back, and an # `openxdox` module imported from anywhere else refuses the case, by name. # +# AND THE STAGING WORKBENCH'S SUITE (plan 038 T026; R-1 (a), its spans widened +# by Brett Heap at openxFactory#656 comment 6016648451). `test_staging_workbench.py` +# parses its gate-off CLI descriptors (`gate create-document …`) with the real +# parser, and its node harnesses run in the display `/capabilities` serves, +# which openDox reads off the registered host. With no host registered, the +# descriptors read `invalid choice: 'gate'` and every harness helper refuses +# `ProfileNotRegistered`, so it runs under the governed host too. +# # THE LIST IS THE SCAN'S, as HOST_PLANE_SUITES is. `tests/test_host_plane.py` # holds it equal to the governed suites, computed as 12.5 computes them # (`governed_suites()` below), that name a `gate` verb of openXdox's command -# line. A module on both lists runs under the governed host, which is a host's -# plane too (`plane_host_for`). +# line, as an argument vector or as a command line. A module on both lists runs +# under the governed host, which is a host's plane too (`plane_host_for`). # --------------------------------------------------------------------------- GOVERNED_HOST_SUITES = frozenset({ @@ -654,6 +662,7 @@ def _a_hosts_plane_for_the_token_reading_suites(request): "test_session_notebook.py", "test_session_transaction.py", "test_session_verbs.py", + "test_staging_workbench.py", }) #: 12.5's governed set is every `tests/test_*.py` that names one of these diff --git a/tests/protected_suite_respellings.yaml b/tests/protected_suite_respellings.yaml index 6dbe9db..63eb6be 100644 --- a/tests/protected_suite_respellings.yaml +++ b/tests/protected_suite_respellings.yaml @@ -19,7 +19,12 @@ # # 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), 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`, @@ -28,8 +33,15 @@ # 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 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. # # 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 +61,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 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); @@ -72,6 +86,15 @@ # 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 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 the module binds, in any scope or binding form, and +# that shares no line with another statement; 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 @@ -79,16 +102,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). @@ -1083,3 +1107,1445 @@ 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 + (`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, read through + `openxdox.contracts.verified_bytes`, which refuses a copy whose sha256 + is not the record's before it is parsed (Copilot on openXdox-code#43). + 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, and the + copy read is now held to its pinned digest first. No assertion is + weakened. + before_blob: e8f98bc82c931c1fc407445a43c55de9d6d9d46f + after_blob: 17a356f6a2835fc3739d65897b808a0e9b4919fb + 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, + # through `verified_bytes`, which refuses a copy whose sha256 is not the + # record's before a byte of it is parsed. + from openxdox import contracts + + schema = yaml.safe_load(contracts.verified_bytes("gate-action-record")) + - 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_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 + 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: d2ef93e4ab7696be1e621967c15ff132d4ffb817 + after_blob: a2bb0d82d0ea0153380fedcaa5881c869f80a049 + 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: a2bb0d82d0ea0153380fedcaa5881c869f80a049 + after_blob: 623d6fe253b36814eb3a52c009528456ee68185c + 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: 623d6fe253b36814eb3a52c009528456ee68185c + after_blob: 4bf069cdf4177e9dc65fe4051be015d9e3ed06fa + 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( + - 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") + - 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: >- + The docstring's statement of where the session routes live, relocated + with the claim it describes (this test's other entry): 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. Copilot's finding on openXdox-code#43 (review comment 4195507364) + is that the docstring still said the opposite. + 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. Documentation only: the docstring now states the + invariant the test's assertions hold. No assertion is touched, and none + is weakened. + before_blob: 5a32a265ffac872c886a34a4d7a29e42c48f8bc9 + after_blob: 54852fa5250b2c4d0630088154aa4bac6517bffe + old: |2 + UNMODIFIED): the write method is banned from the MODEL too, and `import(` + from both. The session ROUTES live in the pure model as constants — one + definition the node harness and the Python route tests both read — while the + only module that calls one is the sibling transport. + new: |2 + UNMODIFIED): the write method is banned from the MODEL too, and `import(` + from both. The session ROUTES are constants of the session column, + `swb-session.js`, the binding that POSTs them (RULED Q3; plan 038 T026, + R-1 (a)) — one definition the node harness reads — and neither the view + nor the model names one. + - suite: tests/test_staging_workbench.py + test: test_session_transport_uses_the_injected_fetcher_spelling + landing: opensoft/openXdox-code#43 + edit: admitted + reason: >- + The docstring's statement of where the transport selects its route from, + relocated with the claim it describes (this test's other entry): since + RULED Q3 (opensoft/openxFactory#656 comment 5642758731) the routes are + this module's own constants, each declared once. Copilot's finding on + openXdox-code#43 (review comment 4195507428) is that the docstring still + named the pure model's constants. + 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. Documentation only: the docstring now states the + invariant the test's assertions hold. No assertion is touched, and none + is weakened. + before_blob: 54852fa5250b2c4d0630088154aa4bac6517bffe + after_blob: 66d16b72080ccbaece5be1aef81446aaa01500bc + old: |2 + per transport file, so the three session verbs share ONE request helper and + select their route from the pure model's constants. That is the pin's design, + new: |2 + per transport file, so the three session verbs share ONE request helper and + select their route from this module's own constants, each declared once + (RULED Q3; plan 038 T026, R-1 (a)). That is the pin's design, diff --git a/tests/test_doxbench_share.py b/tests/test_doxbench_share.py index 10fe26d..17a356f 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,9 +757,15 @@ 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.""" - schema = yaml.safe_load( - (REPO_ROOT / "contracts" / "schemas" - / "gate-action-record.schema.yaml").read_text(encoding="utf-8")) + # 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, + # through `verified_bytes`, which refuses a copy whose sha256 is not the + # record's before a byte of it is parsed. + from openxdox import contracts + + schema = yaml.safe_load(contracts.verified_bytes("gate-action-record")) assert gc.ACTION_SHARE_SESSION in schema["properties"]["action"]["enum"] diff --git a/tests/test_host_plane.py b/tests/test_host_plane.py index 9520b83..f254922 100644 --- a/tests/test_host_plane.py +++ b/tests/test_host_plane.py @@ -104,6 +104,11 @@ def test_on_the_stand_in_hosts_plane_the_token_stays_on_capabilities() -> None: #: A `gate` verb, as a test module spells it: the string `gate` and, after the #: comma, the verb, the way an argument vector names a subcommand. _GATE_VERB = re.compile(r"""["']gate["']\s*,\s*["']([a-z][a-z-]*)["']""") +#: Or as a command line spells it: a string that opens with `gate` and the verb, +#: the way a CLI descriptor does that a test parses with the real parser +#: (`test_staging_workbench.py`; plan 038 T026, RULED at openxFactory#656 +#: comment 6016648451). +_GATE_COMMAND = re.compile(r"""["']gate ([a-z][a-z-]*)""") def _gate_verbs() -> frozenset[str]: @@ -121,8 +126,10 @@ def _gate_verbs() -> frozenset[str]: def _drives_a_gate_verb(path: Path, verbs: frozenset[str]) -> bool: + text = path.read_text(encoding="utf-8") return any(verb in verbs - for verb in _GATE_VERB.findall(path.read_text(encoding="utf-8"))) + for spelling in (_GATE_VERB, _GATE_COMMAND) + for verb in spelling.findall(text)) def test_the_governed_list_is_every_governed_suite_that_drives_a_gate_verb() -> None: diff --git a/tests/test_protected_suite_check.py b/tests/test_protected_suite_check.py index 7190098..85ecb1c 100644 --- a/tests/test_protected_suite_check.py +++ b/tests/test_protected_suite_check.py @@ -18,10 +18,19 @@ * an ADDED test (plan 034 T061) is admitted only where the edit adds it and nothing else; * SEVERAL EDITS IN ONE LANDING (plan 034 T061; T007's batch K) are admitted by - a chain of entries, one per edit, applied in order: only in F5.2's call - (`--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. + a chain of entries, one per edit, applied in order: only in a call that + passes `--chains` (F5.2's, and 12.5's from batch Q, RULED at openxFactory#656 + comment 6016648451), 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. A call without it 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, a name bound twice (in any binding form or + scope, counted fail-closed) or a statement that shares a line with another + 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 @@ -342,11 +351,12 @@ def test_one_landing_with_two_edits_is_admitted_by_a_chain_of_two_entries(repo) assert finding.chain == (1, 2) -def test_a_chain_is_refused_in_12_5s_call(repo, monkeypatch) -> None: - """Chains are F5.2's alone (Brett's ruling admits T061's ten under F5.2; - batch K). 12.5's call passes no `--chains`, so its rule stays one entry per - suite per landing: each entry of the chain is tried alone, and neither - turns the suite before the landing into the suite at it.""" +def test_a_chain_is_refused_in_a_call_without_chains(repo, monkeypatch) -> None: + """Chains are admitted only in a call that passes `--chains`: F5.2's since + batch K (Brett's ruling admits T061's ten under F5.2), and 12.5's from batch + Q (RULED at openxFactory#656 comment 6016648451). Without it the rule stays + one entry per suite per landing: each entry of the chain is tried alone, + and neither turns the suite before the landing into the suite at it.""" repo.commit({SUITE: BOTH}, f"two edits\n\n{ARC}") chain = _two_step_chain(repo) [finding] = _check(repo, chain) @@ -359,8 +369,8 @@ def test_a_chain_is_refused_in_12_5s_call(repo, monkeypatch) -> None: allow.parent.mkdir(parents=True, exist_ok=True) allow.write_text(yaml.safe_dump({"schema_version": 1, "kind": ps.KIND, "entries": chain}), encoding="utf-8") - assert ps.main(_command(repo)) == 1 # 12.5's call - assert ps.main(["--chains", *_command(repo)]) == 0 # F5.2's call + assert ps.main(_command(repo)) == 1 # a call without --chains + assert ps.main(["--chains", *_command(repo)]) == 0 # F5.2's, and 12.5's from batch Q def test_a_chain_is_spent_whole_by_its_landing(repo) -> None: @@ -525,3 +535,217 @@ 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" + + CHAINED = ALSO = "c" + DECLARED: str + + + 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", + before: str = SPAN_BEFORE) -> tuple[dict, str]: + """An admitted entry naming `span` for one edit of `before`, and the + suite's text after it. The base is committed first, as no landing.""" + repo.commit({SUITE: before}, "the span fixture, no landing") + after = before.replace(old, new, 1) + entry = _entry(repo, before=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", + before: str = SPAN_BEFORE) -> ps.Finding: + entry, after = _span_entry(repo, old, new, span=span, before=before) + 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 + ("CHAINED", 'CHAINED = ALSO = "c"\n', 'CHAINED = ALSO = "d"\n'), # two targets + ("DECLARED", "DECLARED: str\n", "DECLARED: bytes\n"), # no value +], ids=["class", "import", "twice", "twice-in-blocks", "block-only", "shadowed", "added", + "two-targets", "annotation-only"]) +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 + + +@pytest.mark.parametrize("rebinding", [ + 'print((REBOUND := "s"))\n', + 'try:\n pass\nexcept ValueError as REBOUND:\n pass\n', + 'match 1:\n case REBOUND:\n pass\n', + '\n\ndef rebind():\n global REBOUND\n REBOUND = "s"\n', + "from json import *\n", + "del REBOUND\n", + "for REBOUND in ():\n pass\n", + "\n\ndef shadow(REBOUND):\n return REBOUND\n", +], ids=["walrus", "except-alias", "match-capture", "global", "star-import", "del", "for", + "parameter"]) +def test_a_second_binding_in_any_form_or_scope_is_never_a_span(repo, rebinding) -> None: + """Counted fail-closed (Copilot on #43): a name the module binds again, in + any binding form and in any scope, is never taken for one binding.""" + before = SPAN_BEFORE + '\nREBOUND = "r"\n' + rebinding + finding = _span_check(repo, 'REBOUND = "r"\n', 'REBOUND = "R"\n', span="REBOUND", + before=before) + assert finding.admitted_by is None + assert "REBOUND is not one module-level constant or function before" in finding.why + + +def test_a_span_that_shares_a_line_with_another_statement_is_refused(repo) -> None: + """An entry's text is whole lines, so a statement on the span's line would + change with it (Copilot on #43).""" + before = SPAN_BEFORE + '\nSHARED = "r"; OTHER = 1\n' + finding = _span_check(repo, 'SHARED = "r"; OTHER = 1\n', 'SHARED = "s"; OTHER = 2\n', + span="SHARED", before=before) + assert finding.admitted_by is None + assert "SHARED is not one module-level constant or function before" in finding.why + + +def test_the_same_name_bound_once_alone_on_its_lines_is_a_span(repo) -> None: + """The control for the two cases above: one binding, alone on its line.""" + before = SPAN_BEFORE + '\nREBOUND = "r"\nOTHER = 1\n' + finding = _span_check(repo, 'REBOUND = "r"\n', 'REBOUND = "R"\n', span="REBOUND", + before=before) + assert finding.admitted_by == 1, 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, admitted in a + call that passes `--chains`.""" + 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_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..66d16b7 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"] @@ -493,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) { @@ -540,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, @@ -563,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) @@ -772,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