Repository navigation
DRAFT: T029, U-9: the composed run, permanent: composed.yml and its openxFactory pin (plan 038) #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
brettheap
wants to merge
25
commits into
main
Choose a base branch
from
r2/t029-composed-workflow
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
c52900e
T020, U-1: the governed host for the governed suites that drive the g…
brettheap 0736158
T021, U-2: the gate console's schemas, each read where its owner keep…
brettheap c40af91
T022, U-3: the runbook placed from the composed tree, git-ignored, no…
brettheap ecf0ba9
T021: a host schema the console cannot apply refuses as GateRefused (…
brettheap 8d02a1b
Merge T021's fix (Copilot r4194497045) into T022
brettheap 7a149b7
T022: the placement script's guards held in CI, tests/test_composed_p…
brettheap d6f4a81
T029, U-9: the composed run, permanent: composed.yml and its openxFac…
brettheap 541852d
T029: judge a pull request's own edits as the landing they would be (…
brettheap 570efda
Merge T022's placement tests (7a149b70, #41) into T029
brettheap 82d9592
T029: an edited pull request body is judged again (Copilot r4195546545)
brettheap b4993d1
T029: 12.5's oracle call takes --chains, as Brett ruled (#656 6016648…
brettheap 42d7ea9
Merge origin/main (T020, T024, T023 and T021 landed; main f70bc9c3) i…
brettheap dc879be
T022: the placement is ignored by the root .gitignore itself, never o…
brettheap 146ae92
T022: ambient git repository variables do not redirect the placement'…
brettheap c22b5df
Merge origin/main (T026 landed as 36bbef79, #43) into T022
brettheap 49c1026
Merge origin/main (T095 landed as e2340bb6, #46) into T022
brettheap 74f3a4f
T022: the placed bytes are the pinned blob, from a regular file in ea…
brettheap 214f361
Merge origin/main (T101 #48 and T098 #51 landed; main fc1115d6) into …
brettheap c56675a
Merge main fc1115d6 into T029 (T020, T021, T023, T024, T026, T095, T1…
brettheap b43ebb8
Merge T022's head c22b5df7 (#41) into T029
brettheap 60c56a4
Merge origin/main (T103 landed as 6f259156, #49) into T022
brettheap 1f2d685
T022: the content check hashes the raw bytes, past clean filters (Cop…
brettheap e225b24
Merge main da736090 into T029 (T103, T096, T100, T099 and T021's foll…
brettheap a6403f5
Merge T022's head 1f2d6858 (#41) into T029
brettheap 9a3b8ed
Merge main 9720925e into T029 (T022 landed as aefd1091, #41; T097 #50…
brettheap File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,345 @@ | ||
| name: composed | ||
|
|
||
| # THE COMPOSED RUN, PERMANENT (plan 038 task T029, U-9, in openxFactory | ||
| # `specs/038-opendox-document-tool-self-maintenance/`). It runs F12.1, the | ||
| # falsifier of #1144's 12.5 ("the governed flow is unchanged"), where it can | ||
| # run: composed with openxFactory, as F5.2 runs under R1Q23 (a). In a lone | ||
| # checkout 15 of 12.5's 16 governed suites cannot even be collected, because | ||
| # they reach openxFactory's `doc_health`. ARC-Q2 (a) (openxFactory#656 comment | ||
| # 6003918488) makes this composition their permanent home (CF-5). Decision | ||
| # N-7a gives the run its own file and its own pin, apart from `validate.yml`. | ||
| # Both were ruled with plan 038 (comment 6013547504). | ||
| # | ||
| # WHAT IT RUNS, IN ORDER. | ||
| # 1. This checkout, with its whole history, because the oracle reads the | ||
| # arc's landings since ARC_BASE. | ||
| # 2. The package and its test extra, in a fresh venv, with `validate.yml`'s | ||
| # install command. So openDox arrives at the pin `pyproject.toml` names, | ||
| # and F9.2's pin assertion holds it there. | ||
| # 3. openxFactory, at the ONE commit `tests/composed_host_pin.yaml` names, | ||
| # with `openDox`, `openXdox` and `openXwallet` initialized recursively. | ||
| # Without them, `corpus_adapter_openxfactory` refuses at import. The run | ||
| # initializes ONLY those three. openxFactory's fourth submodule, | ||
| # `installs/omnigent-install`, is a private repository that this run never | ||
| # imports and that this repository's token cannot read, so a blanket | ||
| # recursive init would fail before any test ran. | ||
| # 4. T022's `scripts/composed_placements.py`, which links the composed tree's | ||
| # session runbook into `docs/` for the run. | ||
| # 5. 12.5's governed suites, computed the way its falsifier computes them | ||
| # (16 is the floor). Each suite runs alone, with | ||
| # `PYTHONPATH="$OPENXFACTORY/scripts"` (R1Q23 (a)'s form). | ||
| # 6. The oracle, `scripts/protected_suites.py`, in 12.5's call as batch Q | ||
| # amends it (`--chains`): no landing of the arc touched a governed suite | ||
| # outside allow-list entries that hold. | ||
| # 7. The assembled `--help` tree, against the golden it records (R2Q3 (a): | ||
| # the hosts' help goldens are unchanged). `validate.yml` leaves that test | ||
| # out (`LEFT_OUT`), because building the tree needs `doc_health`. Composed, | ||
| # it can run. | ||
| # Steps 6 and 7 run even when step 5 is red, so one run reports every red. | ||
| # | ||
| # WHAT IS NOT HERE. Making `composed` a REQUIRED check is the holder's act, | ||
| # after this workflow's first green run on `main` (N-7b (a), ruled | ||
| # 6013547504), and it is recorded in T029's evidence. This file changes no | ||
| # repository setting. | ||
|
|
||
| on: | ||
| pull_request: | ||
| # `edited` beside the default three: the oracle reads the pull request's | ||
| # body for the arc's trailer line, so an edited body is judged again. | ||
| types: [opened, synchronize, reopened, edited] | ||
| push: | ||
| branches: [main] | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| composed: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 60 | ||
| defaults: | ||
| run: | ||
| shell: bash | ||
| working-directory: openXdox-code | ||
| env: | ||
| # The openxFactory checkout of the composed run, beside this one. | ||
| OPENXFACTORY: ${{ github.workspace }}/openxFactory | ||
| # openXdox-code's ARC_BASE, as plan 034 T003 recorded it (openxFactory | ||
| # `specs/034-opendox-standalone-operation/evidence/arc-base.md`). The | ||
| # oracle's landings are the commits from it to HEAD that carry the | ||
| # arc's trailer. | ||
| ARC_BASE: e28930bf052febff0c7bdd0463a72ce3c4f5e8fe | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| path: openXdox-code | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.12' | ||
|
|
||
| # A fresh venv, as 12.5's falsifier makes one. Its `bin/` goes on PATH, | ||
| # so every later step's `python` is this one. | ||
| - name: install the package and its test extra, openDox at the pin | ||
| id: install | ||
| run: | | ||
| echo "COMPOSED=$RUNNER_TEMP/composed" >> "$GITHUB_ENV" | ||
| mkdir -p "$RUNNER_TEMP/composed" | ||
| python -m venv --clear "$RUNNER_TEMP/venv" | ||
| echo "$RUNNER_TEMP/venv/bin" >> "$GITHUB_PATH" | ||
| "$RUNNER_TEMP/venv/bin/python" -m pip install -e ".[test]" | ||
| # F9.2's pin assertion: the composition names a full-commit pin, and | ||
| # the openDox installed is that one. | ||
| "$RUNNER_TEMP/venv/bin/python" - <<'PY' | ||
| import importlib.metadata as md, json, pathlib, re, tomllib | ||
| deps = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["dependencies"] | ||
| pins = [d for d in deps if re.match(r"opendox\s*@", d)] | ||
| assert len(pins) == 1 and re.search(r"@[0-9a-f]{40}$", pins[0]), f"the composition names no full-commit pin: {pins}" | ||
| got = json.loads(md.distribution("opendox").read_text("direct_url.json") or "{}").get("vcs_info", {}).get("commit_id") | ||
| assert got == pins[0].rsplit("@", 1)[1], f"the installed openDox is {got!r}, not the pinned commit" | ||
| print(f"openDox installed at the pin: openDox-code@{got}") | ||
| PY | ||
|
|
||
| # The pin file's rules are in its own header. A file that breaks one is | ||
| # refused, naming the rule, and nothing is checked out. | ||
| - name: read the host pin | ||
| id: pin | ||
| run: | | ||
| python - <<'PY' | ||
| import os, re, sys | ||
| import yaml | ||
|
|
||
| PIN = "tests/composed_host_pin.yaml" | ||
|
|
||
|
|
||
| def refuse(why): | ||
| sys.exit(f"::error file={PIN}::{PIN} is refused: {why}") | ||
|
|
||
|
|
||
| class Strict(yaml.SafeLoader): | ||
| """Refuses a key given twice, which YAML would quietly collapse.""" | ||
|
|
||
|
|
||
| def mapping(loader, node, deep=False): | ||
| keys = [loader.construct_object(key, deep=deep) for key, _ in node.value] | ||
| twice = sorted({str(key) for key in keys if keys.count(key) > 1}) | ||
| if twice: | ||
| raise yaml.constructor.ConstructorError( | ||
| None, None, f"a key is given twice: {twice}", node.start_mark) | ||
| return loader.construct_mapping(node, deep=deep) | ||
|
|
||
|
|
||
| Strict.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, mapping) | ||
| try: | ||
| with open(PIN, encoding="utf-8") as handle: | ||
| pin = yaml.load(handle, Loader=Strict) | ||
| except (OSError, yaml.YAMLError) as exc: | ||
| refuse(f"it cannot be read as YAML ({exc})") | ||
| if not isinstance(pin, dict): | ||
| refuse("it is not a mapping") | ||
| keys = {"schema_version", "kind", "commit"} | ||
| if set(pin) != keys: | ||
| refuse(f"its keys are {sorted(map(str, pin))}, and they must be exactly {sorted(keys)}") | ||
| if type(pin["schema_version"]) is not int or pin["schema_version"] != 1: | ||
| refuse(f"schema_version is {pin['schema_version']!r}, not the integer 1") | ||
| if pin["kind"] != "composed-host-pin": | ||
| refuse(f"kind is {pin['kind']!r}, not 'composed-host-pin'") | ||
| commit = pin["commit"] | ||
| if not isinstance(commit, str) or not re.fullmatch(r"[0-9a-f]{40}", commit): | ||
| refuse(f"commit is {commit!r}, not a full 40-character lowercase commit id") | ||
| for name, value in (("GITHUB_OUTPUT", f"commit={commit}"), | ||
| ("GITHUB_ENV", f"COMPOSED_HOST_COMMIT={commit}")): | ||
| with open(os.environ[name], "a", encoding="utf-8") as handle: | ||
| handle.write(value + "\n") | ||
| print(f"the composed run's host: opensoft/openxFactory@{commit}") | ||
| PY | ||
|
|
||
| - uses: actions/checkout@v4 | ||
| with: | ||
| repository: opensoft/openxFactory | ||
| ref: ${{ steps.pin.outputs.commit }} | ||
| path: openxFactory | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| # openxFactory's `.gitmodules` spells these three as ssh URLs, and this | ||
| # run holds no ssh key. All three, and the legs nested in them, are | ||
| # public, so the URLs are read over https. They are rewritten in this | ||
| # checkout's own `.git/config`, which `git submodule init` writes, and | ||
| # `.gitmodules` is not touched. | ||
| - name: initialize openDox, openXdox and openXwallet, recursively | ||
| id: submodules | ||
| working-directory: openxFactory | ||
| run: | | ||
| paths=(openDox openXdox openXwallet) | ||
| git submodule init -- "${paths[@]}" | ||
| for path in "${paths[@]}"; do | ||
| url=$(git config --get "submodule.$path.url") | ||
| case "$url" in | ||
| git@github.com:*) git config "submodule.$path.url" "https://github.com/${url#git@github.com:}" ;; | ||
| esac | ||
| done | ||
| git submodule update --init --recursive -- "${paths[@]}" | ||
| head=$(git rev-parse HEAD) | ||
| if [ "$head" != "$COMPOSED_HOST_COMMIT" ]; then | ||
| echo "::error::the openxFactory checkout is at $head, and tests/composed_host_pin.yaml pins $COMPOSED_HOST_COMMIT" | ||
| exit 1 | ||
| fi | ||
| echo "OPENXFACTORY (an openxFactory checkout) at $head" | ||
| git submodule status --recursive -- "${paths[@]}" | ||
|
|
||
| - name: place the composed tree's files (T022) | ||
| id: placements | ||
| run: python scripts/composed_placements.py --openxfactory "$OPENXFACTORY" | ||
|
|
||
| # 12.5's falsifier computes the set and holds it to a floor: every suite | ||
| # that drives `open-pr` or injects the fake. Fewer than 16 means a proof | ||
| # vanished. | ||
| - name: the governed set, computed | ||
| id: governed | ||
| run: | | ||
| status=0 | ||
| git grep -l -e 'open-pr' -e 'open_pr' -e 'FakePullRequests' -- 'tests/test_*.py' > "$COMPOSED/governed.txt" || status=$? | ||
| # git grep exits 1 when nothing matches, which the floor below names. | ||
| if [ "$status" -gt 1 ]; then | ||
| echo "::error::git grep could not read this checkout (exit $status)" | ||
| exit "$status" | ||
| fi | ||
| count=$(wc -l < "$COMPOSED/governed.txt") | ||
| echo "governed suites: $count (the floor is 16)" | ||
| cat "$COMPOSED/governed.txt" | ||
| if [ "$count" -lt 16 ]; then | ||
| echo "::error::12.5's governed set has $count suites, below its floor of 16: a proof vanished" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Each suite runs alone, as 12.5's falsifier runs it. pytest's stdin is | ||
| # /dev/null, so no test can read the list this loop reads. Every suite | ||
| # runs whatever the one before it did, and the step fails at the end if | ||
| # any of them did. | ||
| - name: the governed suites, composed, each alone | ||
| id: suites | ||
| run: | | ||
| export PYTHONPATH="$OPENXFACTORY/scripts" | ||
| echo "OPENXFACTORY (an openxFactory checkout) at $(git -C "$OPENXFACTORY" rev-parse HEAD)" | ||
| echo "python $(python -c 'import platform; print(platform.python_version())'), $(python -m pytest --version 2>&1), node $(node --version 2>&1)" | ||
| mkdir -p "$COMPOSED/reports" | ||
| red=0 | ||
| while read -r suite <&3; do | ||
| echo "::group::$suite" | ||
| rc=0 | ||
| python -m pytest -q -p no:cacheprovider -rfE \ | ||
| --junitxml="$COMPOSED/reports/$(basename "$suite" .py).xml" "$suite" < /dev/null || rc=$? | ||
| echo "::endgroup::" | ||
| echo "$suite: pytest exit $rc" | ||
| if [ "$rc" -ne 0 ]; then red=1; fi | ||
| done 3< "$COMPOSED/governed.txt" | ||
| python - "$COMPOSED" <<'PY' | ||
| import os, pathlib, sys | ||
| import xml.etree.ElementTree as ET | ||
|
|
||
| out = pathlib.Path(sys.argv[1]) | ||
| suites = [l.strip() for l in (out / "governed.txt").read_text().splitlines() if l.strip()] | ||
| rows, red, total = [], set(), dict.fromkeys(("passed", "failed", "errors", "skipped"), 0) | ||
| for suite in suites: | ||
| report = out / "reports" / (pathlib.PurePosixPath(suite).stem + ".xml") | ||
| if not report.exists(): | ||
| rows.append(f"{suite}: NO REPORT (pytest did not finish)") | ||
| red.add(suite) | ||
| continue | ||
| root = ET.parse(report).getroot() | ||
| n = dict.fromkeys(("tests", "failures", "errors", "skipped"), 0) | ||
| module = suite[:-3].replace("/", ".") | ||
| for ts in (root.iter("testsuite") if root.tag == "testsuites" else [root]): | ||
| for key in n: | ||
| n[key] += int(ts.get(key, 0)) | ||
| for case in ts.iter("testcase"): | ||
| if case.find("failure") is None and case.find("error") is None: | ||
| continue | ||
| cls, name = case.get("classname", ""), case.get("name", "") | ||
| if not cls: # a collection error names the file, not a test | ||
| red.add(suite) | ||
| continue | ||
| rest = cls[len(module):] if cls == module or cls.startswith(module + ".") else cls | ||
| red.add("::".join([suite, *[p for p in rest.split(".") if p], name])) | ||
| passed = n["tests"] - n["failures"] - n["errors"] - n["skipped"] | ||
| for key, value in (("passed", passed), ("failed", n["failures"]), | ||
| ("errors", n["errors"]), ("skipped", n["skipped"])): | ||
| total[key] += value | ||
| rows.append(f"{suite}: {passed} passed, {n['failures']} failed, " | ||
| f"{n['errors']} errors, {n['skipped']} skipped") | ||
| lines = [*rows, | ||
| f"TOTAL over {len(suites)} suites: {total['passed']} passed, " | ||
| f"{total['failed']} failed, {total['errors']} errors, {total['skipped']} skipped; " | ||
| f"{total['failed'] + total['errors']} red, in {len(red)} red nodes", | ||
| *(f"RED {node}" for node in sorted(red))] | ||
| print("\n".join(lines)) | ||
| (out / "red-nodes.txt").write_text("".join(f"{node}\n" for node in sorted(red))) | ||
| if os.environ.get("GITHUB_STEP_SUMMARY"): | ||
| with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as handle: | ||
| handle.write("## 12.5's governed suites, composed\n\n```\n" + "\n".join(lines) + "\n```\n") | ||
| PY | ||
| exit "$red" | ||
|
|
||
| # 12.5's call, with `--chains`: Brett Heap ruled "Amend F12.1 with | ||
| # --chains (Recommended)" (openxFactory#656 comment 6016648451), which | ||
| # batch Q (T005) records at 12.5's falsifier. So several entries, chained | ||
| # by blob, may together admit one landing's edits to one governed suite, | ||
| # each still one edit inside its own named test or span, under batch K's | ||
| # rule unchanged. | ||
| # | ||
| # ON A PULL REQUEST, THE LANDING IT WOULD BE. GitHub checks out a merge | ||
| # commit whose first parent is the base, so the walk below finds only | ||
| # what has landed already, and an edit the pull request makes to a | ||
| # governed suite would be judged only once it is on `main`. The oracle | ||
| # judges a landing by its diff against its first parent and by the | ||
| # blobs on either side, so the merge commit IS the landing as it would | ||
| # be. It joins the landings where the pull request's body carries the | ||
| # arc's trailer line, which the lander copies into the landing's | ||
| # message. A body with no such line makes no arc landing, so 12.5 does | ||
| # not judge it. | ||
| - name: the oracle, protected_suites.py | ||
| id: oracle | ||
| if: ${{ !cancelled() && steps.governed.outcome == 'success' }} | ||
| env: | ||
| PR_BODY: ${{ github.event.pull_request.body }} | ||
| PR_HEAD: ${{ github.event.pull_request.head.sha }} | ||
| run: | | ||
| git log --first-parent --format=%H --grep='^Arc: neutral-product-standalone-operability$' "$ARC_BASE..HEAD" > "$COMPOSED/x-arc.txt" | ||
| if [ ! -s "$COMPOSED/x-arc.txt" ]; then | ||
| echo "::error::no landing since ARC_BASE $ARC_BASE carries the arc's trailer, so the trailer was dropped" | ||
| exit 1 | ||
| fi | ||
| echo "the arc's landings since ARC_BASE: $(wc -l < "$COMPOSED/x-arc.txt")" | ||
| if [ "$GITHUB_EVENT_NAME" = pull_request ]; then | ||
| if [ "$(git rev-parse --verify --quiet 'HEAD^2')" != "$PR_HEAD" ]; then | ||
| echo "::error::HEAD is not this pull request's merge commit (its second parent is not the head $PR_HEAD), so the change cannot be judged as the landing it would be" | ||
| exit 1 | ||
| fi | ||
| if python -c 'import os, sys; sys.exit("Arc: neutral-product-standalone-operability" not in os.environ.get("PR_BODY", "").replace("\r", "").split("\n"))'; then | ||
| git rev-parse HEAD >> "$COMPOSED/x-arc.txt" | ||
| echo "the pull request's body carries the arc's trailer, so its merge commit $(git rev-parse HEAD) is judged as the landing it would be" | ||
| else | ||
| echo "the pull request's body carries no arc trailer line, so its change is not an arc landing" | ||
| fi | ||
| fi | ||
| python scripts/protected_suites.py --chains --landings="$(cat "$COMPOSED/x-arc.txt")" --suites="$(cat "$COMPOSED/governed.txt")" | ||
|
|
||
| # The test builds the tree in a child process. It hands the child this | ||
| # run's environment LESS `PYTHONPATH` (its `SCRUBBED_ENVIRONMENT`), so | ||
| # the composition of step 5 never reaches it, and the child needs | ||
| # `doc_health` (`cli_gate` imports `gate_console`, which imports it). So | ||
| # the composed tree's `scripts/` reaches the child through a `.pth` file | ||
| # in this run's own venv. The file is written here, after the governed | ||
| # suites have run in R1Q23 (a)'s form, and nothing runs after it. | ||
| - name: the assembled --help tree, against its recorded golden | ||
| id: help-tree | ||
| if: ${{ !cancelled() && steps.submodules.outcome == 'success' }} | ||
| run: | | ||
| purelib=$(python -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])') | ||
| printf '%s\n' "$OPENXFACTORY/scripts" > "$purelib/composed-openxfactory-scripts.pth" | ||
| python -m pytest -q -p no:cacheprovider -rfE \ | ||
| "tests/integration/test_assembled_surface.py::test_the_assembled_help_tree_is_the_32_entry_tree_the_manifest_records" < /dev/null | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # THE COMPOSED RUN'S HOST: the one openxFactory commit the composed workflow | ||
| # checks out (plan 038 task T029, U-9, in openxFactory | ||
| # `specs/038-opendox-document-tool-self-maintenance/`; decision N-7a, ruled | ||
| # with the plan on openxFactory#656 comment 6013547504). | ||
| # | ||
| # WHAT READS IT. `.github/workflows/composed.yml`, and nothing else. It checks | ||
| # openxFactory out at `commit`, initializes that checkout's `openDox`, | ||
| # `openXdox` and `openXwallet` submodules recursively, and runs 12.5's | ||
| # governed suites with that checkout's `scripts/` on `PYTHONPATH` (R1Q23 (a)'s | ||
| # form; F12.1 runs composed, CF-5). The workflow refuses the file, naming the | ||
| # rule, unless all of these hold: | ||
| # * `schema_version` is the integer 1, and `kind` is `composed-host-pin`; | ||
| # * `commit` is a full 40-character lowercase commit id; | ||
| # * no other key is present, and no key is given twice. | ||
| # The repository is openxFactory, always: the workflow names it, and this file | ||
| # names only the commit. | ||
| # | ||
| # WHEN IT MOVES. Never on its own: each advance is an openXdox-code pull | ||
| # request of its own, so the composed run always tests the host that | ||
| # openxFactory's `main` carries (plan 038 plan.md, "The composed workflow's | ||
| # openxFactory pin"). It advances after T030, T064 and T075 land in | ||
| # openxFactory, and T073 re-runs after each advance. | ||
| # | ||
| # ITS FIRST VALUE. openxFactory `main`'s head when T029 authored this file, | ||
| # read with `git ls-remote` at 2026-10-06T11:56:20Z: `92010d3e`, "Land | ||
| # add-factory-mcp-conformance on main as Speckit feature 037, with repairs | ||
| # and validator fixes (#1243)". | ||
| # | ||
| # A CREATED file: no row in openxFactory's `docs/opendox-carve-manifest.yaml` | ||
| # (RULED OQ-C). | ||
| schema_version: 1 | ||
| kind: composed-host-pin | ||
| commit: 92010d3e67f2c556687e1c3583ed3ca1867f4b8b |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.