Skip to content

T076, 10.3: the root README documents the one command - #17

Merged
brettheap merged 10 commits into
mainfrom
build/034-p3o-t076-root-readme
Oct 5, 2026
Merged

brettheap merged 10 commits into
mainfrom
build/034-p3o-t076-root-readme

Conversation

@brettheap

@brettheap brettheap commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Plan 034, task T076, slice P3-O (the root README), phase 3. The plan is
specs/034-opendox-standalone-operation/tasks.md at opensoft/openxFactory main
ba6bb870. This PR realizes box 10.3 of the ratified
add-neutral-product-standalone-operability, as T007 batch H amends it, with the
browser limit of batch P.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)

  • Claim: openxFactory#656 comment 5961416935.
  • Rulings:
    • 5961364221 item 2: T076 was drafted ahead of T063.
    • 5850003126: R1Q15 (b) and R1Q16 (iii), the command itself.
    • 5962754358 item 1, and the holder's ruling of 2026-10-03 (analyze finding H3): until T099 publishes opendox, an install from a recursive clone that keeps the local extra, pip install "./code[local]", stands in for the first line.
    • 5982436447 item 1, "Hint line, accepted limit", and its B7 ruling on the browser's history (12.4a, batch P).
  • State: this PR still lands before T095, and T089 waits on it. The holder posts READY and the lander merges; this writer does neither.

What it waited on has landed

  • T087 landed as openDox#18 (e1e3a3c3, 2026-10-05): the root now pins openDox-code dede32b4, the 0.1.0 version bump, with #84 (T104) and #78 (T099's release workflow) inside it. I merged that main into this branch as a merge commit (4fddf62f; the diff against main is README.md only, and the merge took no conflict).
  • #84 (T104) landed as openDox-code 32943cbf. The "Opening the page" paragraph was written against its design and is now checked against the landed code (below).
  • Still to come, and not this PR's: T099's later root PR replaces the "Where opendox comes from" paragraph with the PyPI line after the publish verifies. This README keeps that paragraph until then.

What changes: README.md only, 82 lines added

A new section, "Install and run", sits after "Get started". Nothing else moves: the doc index (the docs/ table), the section order, Makefile, every pin and every workflow are untouched.

The command, as batch H's 10.3 addendum reads

The addendum (openspec/changes/add-neutral-product-standalone-operability/tasks.md, 10.3, "AMENDED -- T007 Batch H (5850003126; Ruled R1Q15 (b), R1Q16 (iii))") says, verbatim, at openxFactory main ba6bb870:

The command the root's README.md documents is the standalone install and its one start: pip install "opendox[local]", then opendox generate-and-open --local …. The flag selects the local mode explicitly (13.4 as this batch amends it), and the local extra carries the bundled server that mode starts (13.1 as this batch amends it). After the install, that start is the single command requirement 10's second scenario has a user run. The entry point is still the code leg's console script, and no Makefile target is added, for the reason above. Carried out by T070 and T076.

The README block, character for character (the … is U+2026, preceded by one space):

pip install "opendox[local]"
opendox generate-and-open --local …

A mechanical comparison, run on this branch's README.md against that addendum paragraph at openxFactory main ba6bb870:

addendum commands: ['pip install "opendox[local]"', 'opendox generate-and-open --local …']
README has the line 'pip install "opendox[local]"' exactly: 1 time(s); bytes ok=True
README has the line 'opendox generate-and-open --local …' exactly: 1 time(s); bytes ok=True

For the T095 holder check. T095's harness (openDox-code#75, another writer) runs the same command literally. Compare its install line and its start line with the two lines above. The only legitimate difference is that the harness fills the …, as quickstart.md § 3 does: opendox generate-and-open --local --repo-root "$R" --repository fixture --no-open --port "$PORT". Every flag in that line is named in the README's prose, and the harness reads the token from the private copy that the "Opening the page" paragraph describes.

What the … stands for (said in a sentence, no flag invented)

The README says the … stands for the verb's own arguments, which follow it as every option does (10.1 of the change). Two are required: --repo-root (the plain git repository of Markdown documents to open, front matter not required, AT-R1 step 3 (b)) and --repository (a name for it, recorded as the snapshot's repository id). It says the served actor is that repository's git config user.name, that --actor is only a claim that must match an identity the install can establish, and what the start does without an actor. --no-open launches no browser, --port N fixes the port, and opendox generate-and-open --help lists the rest.

Checked against the pin, dede32b4

I installed this root's pinned code leg (dede32b4, version = "0.1.0") into a fresh venv with pip install "./code[local]" (from a copy of the submodule, so this clone stayed clean), and ran the documented start on a three-file plain git repository with no database, broker or model:

  • Program name and install: opendox --help prints usage: opendox, pip list shows opendox 0.1.0 with pixeltable-pgserver, and the local extra installs.
  • The local start: /capabilities answers install.mode == "local". The bundled postgres is the entry point's child and is gone, with the port free, after a SIGTERM to the entry point I started. --host 0.0.0.0 under --local is refused.
  • Host check: Host: 127.0.0.1:<port> got 200, and Host: evil.example got 403.
  • The console copy, with a git identity: the start printed console file:///<state>/console/<port>.html (this user's private copy, mode 0600: open it to open the console page again), then the hint line, then the plain http://127.0.0.1:<port>/index.html. The copy was mode 600 and began with its marker comment. No printed line held the token, /capabilities carried no console_token, and the copy was gone after the stop.
  • The hint line: the README quotes it, and a script confirmed the quoted text equals console_access.UNOPENABLE_HINT at the pin, character for character.
  • Without an actor (no user.name, or --actor stranger): session and edit read false, no console/ directory is made, and only the plain URL is printed.
  • The state directory: one inside the served repository is refused by name (lies inside the served repository), and the bundled postgres that had started before the refusal is gone. (A directory that holds the repository is refused by name too, which I checked at #84's merge commit 32943cbf.)

The README's claims about a browser (the browser is opened through the copy without --no-open, the Snap, Flatpak and WSL limit, and the history limit) come from #84's landed design, the printed hint and the rulings. A browser is what AT-R1's browser half (T096) exercises, and I did not run one here.

Where the package comes from (no PyPI release is claimed)

Neither the addendum nor quickstart.md names an index for the first line. The README says: opendox is the distribution the code leg builds (code/, opensoft/openDox-code at the commit contracts/code-pin.yaml pins); its pyproject.toml names it opendox and declares the local extra and the opendox console script; and, as of 2026-10-04, no release of it is published to PyPI, so the first line has nothing to resolve from the default index. Until one is, the same distribution and extra install from a recursive clone, run from its root, with pip install "./code[local]" in place of the first line, and the second line runs unchanged. That is the stand-in T076's plan entry names.

Checked again on 2026-10-05, the day T087 landed (the 0.1.0 bump is in the pin, and nothing is published):

$ curl -s -o /dev/null -w "%{http_code}\n" https://pypi.org/pypi/opendox/json     -> 404
$ gh release list -R opensoft/openDox-code    (empty)
$ gh api repos/opensoft/openDox-code/git/matching-refs/tags --jq length   -> 0

No Makefile target

Makefile has a row in contracts/shape-pin.yaml (path: Makefile), so a run target would be reported as drift and would red make pins. README.md has no row (grep -n "path:" contracts/shape-pin.yaml lists ten paths, none of them README.md). The README says so in one sentence, and the entry point stays the code leg's console script.

Falsifier

T076's falsifier is review, and AT-R1 step 4 follows it literally. AT-R1 step 4 reads: "The ONE command the openDox root README documents, opendox generate-and-open --local … (R1Q15 (b)), serves the bundle on loopback." The README states the --local command in exactly that spelling, and it is followed literally. Before the cut, the first line cannot be followed literally, because opendox is on no index, so the README's dated paragraph names the checkout install that stands in for it. By the plan's own review criteria, the README also names the browser limit and the OPENDOX_STATE_DIR remedy, and never prints or asks for the console token.

The root's own checks (make validate, which runs the same three scripts the validate workflow's steps run, and make pins), at this PR's head, with both legs initialized (git submodule update --init):

$ make validate
python3 scripts/validate-repository-naming.py --project project.yaml
  openDox                          neutral-product/assembly   also_matches project-leg/assembly
  openDox-spec                     project-leg/spec
  openDox-code                     project-leg/code
python3 scripts/validate-manifest.py
manifest ok: openDox (opendox), 3 legs
python3 scripts/validate-pins.py
  ok  spec: gitlink == contracts/spec-pin.yaml commit f7ee3c763b3a
  ok  spec: tree digest recomputes (e81f8530d93e…)
  ok  code: gitlink == contracts/code-pin.yaml commit dede32b4b6f3
  ok  code: tree digest recomputes (c2672463b4d8…)
  ok  contracts/shape-pin.yaml: 10 copied shape file(s) match their digests
pins ok

$ make pins
python3 scripts/validate-pins.py
  ok  spec: gitlink == contracts/spec-pin.yaml commit f7ee3c763b3a
  ok  spec: tree digest recomputes (e81f8530d93e…)
  ok  code: gitlink == contracts/code-pin.yaml commit dede32b4b6f3
  ok  code: tree digest recomputes (c2672463b4d8…)
  ok  contracts/shape-pin.yaml: 10 copied shape file(s) match their digests
pins ok

Review notes

  • Rounds 1 to 8. Copilot's threads are all resolved: the Python 3.12 prerequisite, the pending T087 pin (now landed), and the console file's actor condition. Other rounds added what landed on openDox-code main: the bundled server and the Host rule (T072, T103), the POSIX and non-root prerequisites, the accepted browser limit (5982436447 item 1) with the hint line quoted, the stale-tab limit, and the history limit (B7). The README never prints or asks for the console token.
  • This head is the merge of main at T087 on top of those rounds, with no change to the README's text.
  • No closing keyword appears in this body or in any commit.

🤖 Generated with Claude Code

Plan 034, task T076, slice P3-O. The root README gains an "Install and run"
section after "Get started". It gives the two lines T007 batch H's 10.3
addendum reads, character for character: pip install "opendox[local]", then
opendox generate-and-open --local … (R1Q15 (b), R1Q16 (iii)).

It says in prose what the "…" stands for (the verb's two required arguments,
--repo-root and --repository, and the options that exist in cli.py), where the
opendox distribution comes from (the code leg, since no release is published
to PyPI), and why no Makefile target is added (it has a shape-pin row).

README.md only. The doc index, the Makefile and every pin are untouched.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 2, 2026 21:06
@sourcery-ai

sourcery-ai Bot commented Oct 2, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a root README section documenting the two-line standalone install/start workflow, its arguments and local runtime behavior, the current checkout-based installation fallback, and the intentional absence of a Makefile target; no other files or pins change.

Sequence diagram for the standalone install and local run

sequenceDiagram
    actor User
    participant Pip
    participant CodeLeg
    participant OpenDox
    participant LocalServer

    User->>Pip: pip install "opendox[local]"
    Pip->>CodeLeg: Install opendox with local extra
    User->>OpenDox: generate-and-open --local …
    OpenDox->>LocalServer: Start bundled server on loopback
    LocalServer-->>OpenDox: Serve generated snapshot
    OpenDox-->>User: Print or open local URL
Loading

Flow diagram for checkout-based standalone installation

flowchart LR
    Clone["Recursive clone of openDox"] --> Install["pip install ./code[local]"]
    Install --> Start["opendox generate-and-open --local …"]
    Start --> Serve["Serve snapshot on loopback"]
Loading

File-Level Changes

Change Details Files
Document the standalone installation and startup workflow in the root README.
  • Add an “Install and run” section with the ruled install line and opendox generate-and-open --local … command.
  • Explain required repository arguments, optional runtime flags, local-mode behavior, and the meaning of the ellipsis.
  • Document that the package currently requires installation from the checked-out code/ leg because no PyPI release exists.
  • Clarify that no Makefile target is added and the code leg’s console script remains the entry point.
README.md

Tips and commands

Interacting with Sourcery

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

Customizing Your Experience

Access your dashboard to:

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

Getting Help

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The supporting T087 pin update remains pending, and the documented workflow needs validation against that updated pin.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Documents standalone installation and local startup in openDox’s assembly-root README.

Changes:

  • Adds install/start commands, argument guidance, and local-mode behavior.
  • Explains package provenance and the checkout installation workaround while PyPI publication is unavailable.
File Description
README.md Adds “Install and run” guidance using the code leg’s console script.

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

Comment thread README.md Outdated
Comment thread README.md
…view round 1)

Copilot's review of the first head asked for the interpreter floor to be
stated before the installation block: the code leg's pyproject.toml declares
requires-python >= 3.12, so an older pip rejects the install before the start
command can run. README.md gains one sentence, before the two lines, which are
unchanged.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 2, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

Approval depends on T087 landing and the documented workflow being validated against its updated code pin.

Review effort: Balanced
Findings: None

Resolved since last review (2)

…ound 2)

The README's two command lines are the ruled addendum text and are unchanged.
The prose around them now states only behavior landed on openDox-code main
(1130e996): --local selects the local mode, it needs no identity broker and
binds loopback only, and the flags named are the ones generate-and-open
declares there.

Removed until their tasks land: the bundled server and the local extra (T072,
openDox-code#69), "the bundled server stops when the command does", and the
checkout fallback's [local] extra, which main does not declare yet (pip warns
"does not provide the extra 'local'"). The fallback reads pip install ./code.
The PyPI date is re-checked: still no release on 2026-10-03.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

Approval depends on T087 landing and revalidation of the documented workflow against the updated code pin.

Review effort: Balanced
Findings: None

Copilot AI balanced review requested due to automatic review settings October 4, 2026 13:15
Plan 034 T076, on the holder's refresh word. The two command lines are the
ruled addendum text and are unchanged. The prose around them now reads against
openDox-code main (c4b55cc4) and T104's draft head (182cac76):

- H3: the stand-in for the PyPI line is pip install "./code[local]", not a bare
  ./code. T072 has landed, so the extra exists and the --local start needs it.
  The provenance paragraph names the extra again, and its PyPI date is
  re-checked (2026-10-04, still 404).
- T072: the local extra carries the runtime packages and the bundled PostgreSQL
  server; the start launches it as its own child under OPENDOX_STATE_DIR (no TCP
  port) and stops it when the command stops.
- T103: the server answers only a request whose Host names it; any other gets
  403 invalid_host.
- T104 (pending openDox-code#84): the start writes a private copy of the
  console's opening page and prints its file:// URL, never the token, with or
  without --no-open; opening that file opens the page again.

README.md only. Merging openDox main was a no-op (main is still d509829).

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The documented workflow still needs verification against T087’s final coordinated code pin.

Review effort: Balanced
Findings: 1 Low severity

Open (1)

Comment thread README.md Outdated
Copilot AI balanced review requested due to automatic review settings October 4, 2026 13:24
…ctor (review round 3)

Copilot's review of 18c23bf asked the README to document the fallback when no
trusted actor is available. Checked at openDox-code#84's head (182cac76) with
the documented start on a plain git repository:

- no git user.name at all, or --actor naming someone the checkout cannot
  establish: /capabilities answers session false and edit false, no console
  file is written, and the start prints only the plain
  http://127.0.0.1:<port>/index.html;
- user.name set, or --actor matching it: session and edit true, and the start
  prints the console file:// URL.

The README now says the served actor is the repository's git user.name, that
--actor is only a claim that must match an identity the install can establish,
and what the start does without an actor. The two command lines are unchanged.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The documented workflow needs verification against the updated code pin after T087 and T104 land.

Review effort: Balanced
Findings: None

Resolved since last review (1)

Copilot AI balanced review requested due to automatic review settings October 4, 2026 17:13
…rivate copy (round 5)

Brett's ruling on openxFactory#656 (comment 5982436447, item 1, "Hint line,
accepted limit"): a Snap or Flatpak browser, and a Windows browser running
under WSL, cannot open the private file:// copy under ~/.local/state. T104
(openDox-code#84) will print one extra line, with no token, and the README
documents the same limit and its remedy in the "Opening the page" paragraph:
set OPENDOX_STATE_DIR to a non-hidden folder and restart.

Written against the ruling, not against #84's wording: #84's head (fb8a1cc4)
does not print the line yet. The two command lines are unchanged.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The documented workflow still awaits T087’s pin update and verification against T104’s final behavior.

Review effort: Balanced
Findings: None

Previously missed (1)

In code that hasn't changed since last review

Low severity Document POSIX and non-root prerequisites for local startup

README.md:29

Document the platform and user prerequisites alongside Python. In openDox-code at c4b55cc4, src/opendox/runtime/bundle.py:135–163,859–867 rejects platforms without the required POSIX primitives and refuses root execution. The documented local start therefore fails on native Windows or when run as root. State these restrictions before the installation block.

Copilot AI balanced review requested due to automatic review settings October 4, 2026 17:18
…lock (review round 5)

Copilot's review of 5b0990a noted, outside any thread, that the documented
local start fails on a platform without the POSIX primitives and when run as
root. Read at openDox-code main (38d3350e), src/opendox/runtime/bundle.py:
unsupported_platform() (lines 135-163) refuses a platform that lacks them and
names it, and BundledServer.start() (lines 859-867) refuses to run as root.
README.md gains one sentence beside the Python prerequisite. The two command
lines are unchanged.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The documented workflow needs verification against T087’s updated code pin and T104’s final console-opening behavior.

Review effort: Balanced
Findings: None

brettheap and others added 3 commits October 5, 2026 05:00
openDox-code#84 landed as 32943cbf. Read against it, the README's "Opening the
page" paragraph is right on how the page is opened (the private copy, its
file:// URL, never the token, the plain URL loading without it), on the actor
condition, on the program name (opendox) and on the install line. Three
changes:

- The browser limit now uses the ruled and printed wording (5982436447, item 1;
  plan 034 T076 and 12.4a's batch P amendment): an accepted limit of release 1,
  the hidden default state directory (~/.local/state/opendox), Ubuntu's default
  Snap browser, a Flatpak browser and a Windows browser under WSL, and the hint
  line quoted as T104's start prints it (console_access.UNOPENABLE_HINT).
- The remedy names what the start refuses (checked on 32943cbf): a state
  directory inside the served repository, or one that holds it, is refused by
  name, so the folder must be apart from the repository.
- 12.4a's stale-tab limit: each start makes a new token, so after a restart
  open the new file.

README.md only. The two command lines are unchanged.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…cal)

12.4a's accepted limit (T007 batch P, the holder's ruling B7): the browser's
own history keeps the opened URL, fragment included, until it is cleared. Each
start makes a new token, so an entry from an earlier start grants no access.
One sentence in "Opening the page", after the stale-tab sentence. The README
still never prints or asks for the token. The two command lines are unchanged.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 5, 2026 12:01
@sonarqubecloud

sonarqubecloud Bot commented Oct 5, 2026

Copy link
Copy Markdown

@brettheap brettheap changed the title DRAFT (phase 3, after T063): T076, 10.3: the root README documents the one command T076, 10.3: the root README documents the one command Oct 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The staged instructions need verification against the final integrated code pin after the documented dependencies land.

Review effort: Balanced
Findings: None

@brettheap
brettheap marked this pull request as ready for review October 5, 2026 12:07
@brettheap

Copy link
Copy Markdown
Contributor Author

READY at 4fddf62 — Lane: openxfactory-4 (openXfactory-4-openDox_extraction)

Holder: T076, 10.3: the root README documents the one command. It is an arc landing.

  • The command. The README gives pip install "opendox[local]" and opendox generate-and-open --local …, byte for byte as batch H's addendum has them. Until T099 publishes, the dated paragraph's stand-in is pip install "./code[local]".
  • What #84 changed, aligned. The console copy is a private file:// URL, the token is never printed, and the program is named opendox. The browser limit is quoted character for character from console_access.UNOPENABLE_HINT, and the stale-tab and history limits (12.4a) are named.
  • Re-verified at the pin. openDox-code dede32b4, 0.1.0, in a fresh venv. The browser-opening claims rest on #84's landed design; T096 exercises a browser.
  • Gates.
    • make validate and make pins pass.
    • CI validate and SonarCloud are green.
    • Copilot has no findings.
    • All 3 threads are resolved.
    • It merges root main e1e3a3c3 (T087) with no conflict.
  • What follows. T089 and T095 wait on this landing.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 10 hours and 16 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@brettheap
brettheap merged commit 504324d into main Oct 5, 2026
4 checks passed
@brettheap

Copy link
Copy Markdown
Contributor Author

Lane: openxfactory-4 (openXfactory-4-openDox_extraction)

LANDED — lane openxfactory-4, 2026-10-05T12:07:55Z, PR #17 → 504324d (opensoft/openDox main; plain gate)

Brett: land phase 1 / phase 2 PRs when green

brettheap added a commit to opensoft/openxFactory that referenced this pull request Oct 5, 2026
…, with phase 3's host wiring: openXdox's columns and the governed binding-trust policy (plan 034) (#1236)

Lane: openxfactory-4 (openXfactory-4-openDox_extraction)

Plan 034, task **T094**: phase 3's consumer pins (T090 step 6) with phase 3's host wiring, `specs/034-opendox-standalone-operation/tasks.md` at openxFactory `main`. The precedents are T047 (opensoft/openXdox#20, then #1181) and T064 (opensoft/openXdox#21, then #1215).

- **Claim:** #656 (comment) (it covers T087 and T094).
- **Landing order, done:**
  - T087: opensoft/openDox#18 → `e1e3a3c3` (openDox-code `dede32b4`, the 0.1.0 commit T099 publishes).
  - T086: opensoft/openXdox-code#37 → `56e1c238`.
  - T090 step 5: opensoft/openXdox#22 → `9564d5d9`.
  - Lane openXfactory-3's ahead PR (D2, non-arc: the CLI help golden, the usage line and the rebound-Host case at both pins) → #1234, `ca1c148646f1111bc8049bc9cceb3c14ef276967`. This branch merges `main` in at `9cd1bf23` (a merge, never a rebase).
- **Holder decisions in force (2026-10-05):**
  - (a) both pin pairs move in ONE commit, as in T047 and T064 (below);
  - (b) T092's phase-3 `edits[].note` annotations ride in T089's checkpoint PR (lane openXfactory-3's D3; #656 comment `5987195870`, D5), NOT here. This PR touches no manifest note;
  - (c) the stale `src/opendox/consumer_reach.py` admission is removed and asserted ABSENT;
  - (d) the 4 + 3 carve-time files the walk still names stay as T064 left them;
  - (e) the merge of `main` (`9cd1bf23`) carries the `Lane:` line and no `Arc:` line. T091's trailer belongs on realization commits and on the landing, and the squash landing writes `Arc:` from this body. On the merge, F11.1's guard would charge #1234's non-arc files to the arc.
- It stays a DRAFT until the holder marks it READY.

## The pins: both pairs in ONE commit (`caa8d377`)

| pair | gitlink | pin file `commit:` | `digests.tree_sha256` |
| --- | --- | --- | --- |
| openDox | `d5098297` → **`e1e3a3c3`** | same (`contracts/opendox-pin.yaml`:118) | `2815ca23…57cc` → **`55a110f49d268e22d382d47d778dfaf9298c93b555e3c4fcb621afc99f9d011b`** (28 records) |
| openXdox | `f257e021` → **`9564d5d9`** | same (`contracts/openxdox-pin.yaml`:109) | `52f0598e…6959` → **`11585eadf4fef2ffcb4419194fa6a52444f56759cfc427d41c352a6b8803cbd2`** (29 records) |

- **`e1e3a3c3` is opensoft/openDox#18's squash (T087), one commit above `d5098297`.** It moves `code` `047bb4fa` → `dede32b4` with `contracts/code-pin.yaml` and changes no other path, so openDox's `contracts/manifest.yaml`, its `spec` gitlink and the bundle (`dox-v1.1`) are unchanged. openDox's `main` has since moved to `504324de` (opensoft/openDox#17, T076, the root README only). Step 2's root commit is the one T090 pins, and no check reads `main`'s tip.
- **`9564d5d9` is opensoft/openXdox `main`, where #22 squash-landed** (its tree equals #22's head `db6cc2a4`). It moves `code` `6a3b93b9` → `56e1c238`, which is openXdox-code `main` after #37 (T086), and `contracts/opendox-pin.yaml` `d5098297` → `e1e3a3c3` (T090 step 5).
- Every digest was recomputed three ways: `repo_shape.tree_digest`, an independent `ls-tree` + `sha256`, and the forge's tree listing (`repo_shape.tree_digest_from_gh`). Controls: the same three methods reproduce the recorded `52f0598e…6959` at `f257e021` and `2815ca23…57cc` at `d5098297`.
- **Why one commit and not "one commit each" (T090 step 6).** #22 changes the openDox pin that `verify-opendox-pin.py` check 5 reads through the openXdox gitlink, and `docs/openxdox-pin-resync-runbook.md` § 4 prints DIFFERENT. Each split order was measured on this branch's base, and `verify-opendox-pin.py` exits 2 in both:
  - the openDox pair alone: `REFUSE opendox-pin-lockstep-mismatch: this pin names openDox@e1e3a3c3f8dd38214510412b71a2e858c6179532, but openXdox's own openXdox/contracts/opendox-pin.yaml names openDox@d5098297a5c262f9977193305210bccb4ec51e89; two direct declarations of one product's bytes disagree.`
  - the openXdox pair alone: `REFUSE opendox-pin-lockstep-mismatch: this pin names openDox@d5098297a5c262f9977193305210bccb4ec51e89, but openXdox's own openXdox/contracts/opendox-pin.yaml names openDox@e1e3a3c3f8dd38214510412b71a2e858c6179532; two direct declarations of one product's bytes disagree.`

  So "one commit each" cannot be met without an intermediate red commit. **The holder ruled the single commit for phase 3 on 2026-10-05**, as for phase 1 (T047, #1181) and phase 2 (T064, #1215, decided 2026-09-28). This PR therefore departs from T090 step 6's "one commit each" for the third time. Each pair still moves as one unit (box 9.5).
- Both pin files are edited in place, keeping their line counts (216 and 154). The openDox `migration:` block keeps `range` (`0001..0002`) and `reversible` (`false`): no migration path changes over openDox-code `047bb4fa..dede32b4`. Its `runbook` names the new code leg `dede32b4`, and the block is identical to openXdox's derived copy at `9564d5d9`, which `tests/opendox_pin` asserts.
- **Gates.**
  - `verify-opendox-pin.py`: `OK opendox-pin verified: openDox@e1e3a3c3f8dd38214510412b71a2e858c6179532, gitlink read from HEAD, sorted-ls-tree-r-v1 tree digest recomputed (55a110f4…011b), lockstep with openXdox confirmed`.
  - `verify-openxdox-pin.py`: `OK openxdox-pin verified: openXdox@9564d5d9462ffd1a3155d9177206368e5061efa8, gitlink read from HEAD, sorted-ls-tree-r-v1 tree digest recomputed (11585ead…cbd2)`.

## What else it carries

| commit | what | F11.1 surface |
| --- | --- | --- |
| `3c1c1146` | **The host wiring.** `scripts/opendox_host.register_openxfactory()` calls `openxdox.column_contributions.register()` (T086) after the projection line, and refuses by name when it registers nothing. **`GovernedBindingTrust`** (RULED #656 `5970369724`, "Governance approval (Recommended)") is the sixth `seams()` entry, before the home seam, with its take-back row. Its verdicts: a binding pending in the governed declarations is untrusted (host basis, the governed reason); an unreadable declarations document admits nothing; otherwise trusted, host basis. It answers the console intake's own question, `intake_verdict`, as plan 034's T100 entry says a host policy must. Its `record()` writes nothing and returns None, which openDox-code#86 reads as "no record" (`doxbench_trust.recording_for`; #656 `5986391296`, applied to T094 by `5988088910`), so a failed `add` or `edit` write is refused as the write's own failure and never with `REASON_NO_WITHDRAWAL`. `tests/domain_profile/test_host_registers_binding_trust.py` (new, 9 tests, 11 cases at `7e31eca1`) and `test_openxfactory_host_wiring.py` (six seams; the three columns the facet composes; each declared once) cover it. They pin openDox-code#86's final `REMEDY_NOT_BY_TRUST` and `INTAKE_HOST_NOT_ADMITTED` strings. Planted mutants, measured over the draft rounds of this same code, are each killed: M1-M3 and M5 by `test_host_registers_binding_trust.py`, the column line removed (M4) by `test_doxbench_routes`, `intake_verdict` removed or admitting everything (M6, M7), and `record()` returning the verdict, returning True, or writing MachineTrust's store (M8, M9, M11). | HOST, HOST_TESTS |
| `3c1c1146` | **Composition tests**, amended in the arc: `test_extension_point_parity.py` (the `/capabilities` arm of T073 and T103, and the MRO's T084 layout), `test_serve_column_split.py` (the snapshot arm's four handlers are the core's own after T086's trim; every column method is the column's own), and `test_doxbench_status_exemption.py` (T085's `register_default_status_exemption` joins the readers of the registered rail). | COMPOSITION_TESTS |
| `1c5f8edb` | The render lane's `RENDER_LEG_MODULES` and the seal test's `RENDER_UNIT_IMPORTS` gain `opendox/doxbench_trust.py`, `opendox/doxbench_intake.py` and `openxdox/column_contributions.py`, which the host bootstrap now imports. The seal carries a leg's whole `src/`, so no sealed artifact changes. `scripts/profile_openxfactory.py`'s note on the two openXdox columns moves to the past tense. | ADMITTED_ARC_EDITS (both paths); HOST |
| `24215223` | `test_ruling_q7_two_direct_upstreams_in_lockstep`'s snapshot literal moves `d5098297` → `e1e3a3c3`, as its comment says it does on every bump. | ADMITTED_ARC_EDITS |
| `eda75977` | **The lane route's predicate check** (T086's Q8 (a)). openXdox-code's `test_the_hosted_session_arrival_path_is_recorded_and_not_built` no longer reads openxFactory's `_handle_refresh_action` (opensoft/openXdox-code#37 narrowed its loop to the leg's own routes), so `tests/domain_profile/test_lane_route_asks_the_hosted_ref_predicate.py` (new, 2 tests) asserts it here: the route's body calls `hosted_ref_refused(` (openXdox's predicate), and on a hosted plane a refresh naming a session ref is refused with `session_unavailable` before it runs, while a ref-less one runs. Three planted mutants are each killed: the check removed, the call kept but not deciding, and the call asked as loopback. | HOST_TESTS |
| `b3a4b6e0` | **29 `created:` admissions** in `docs/opendox-carve-admissions.yaml`, pinned in `tests/carve_arrival` by path, `since` and count, as T047's 37 and T064's 129 are, **and one removed** (below). | ADMITTED_ARC_EDITS (both paths) |
| `16136990` | The admissions test's ordinal paragraph and its sixteenth-bump docstring and comment name this PR, #1236 (a follow-up commit: the number did not exist before the PR). | ADMITTED_ARC_EDITS |
| `9cd1bf23` | Merge of `main` at `ca1c1486` (lane openXfactory-3's ahead PR). It merged with no conflict, and its four files are byte-identical to the ahead commits this branch was measured over. It is not a realization commit, so it carries the `Lane:` line and no `Arc:` line (T091): F11.1's guard selects commits by that line, and walked over this branch it would otherwise charge #1234's two non-arc files to the arc. | (not on `main`'s first-parent line) |
| `7e31eca1` | **Copilot's finding at `16136990`, and the pre-review's six fix-now findings.** (Copilot) A refusal now gives back the unread defaults this call replaced: `_DEFAULTS` names the two globals and the default-registration call of each default-holding seam (T085's doxBench pair, T100's trust default). A write that replaced an unread default is recorded with it, and the take-back registers it again as the unread default, as `openxdox.column_contributions` does. The pre-review's six: (1) the no-columns refusal is tested, and no host seam is written; (2) a refusal at the trust seam is tested on fakes and on the pinned leg; (3) "five seams" becomes six where the count is meant; (4) `GovernedBindingTrust`'s docstring names the one case that changed; (5) the failed-write case skips as root; (6) `__all__` is in ASCII order and names `GOVERNED_PENDING_REASON`. The give-back case, the six-seam message and the exports case each failed before the fix. Planted mutants M12-M19 are each killed. | HOST, HOST_TESTS |

The 29 admissions, each `since` the leg's own squash landing (`git log --diff-filter=A`), each path present at the new pin, and none placed by a row or declared before:

- **`opendox_code`, 25**: #65 (T088) 1, #67 (T070) 1, #69 (T072) 1, #71 (T085) 2, #72 (T073) 1, #74 (T081) 1, #77 (T084) 9, #78 (T099) 1, #80 (T103) 1, #81 (T102) 1, #82 (T100) 2, #84 (T104) 3, #85 (T102 follow-on) 1. All are openDox-code landings over `047bb4fa..dede32b4`.
- **`openxdox_code`, 4**: #37 (T086) at `56e1c238`: `src/openxdox/column_contributions.py`, `tests/test_column_contributions.py`, `tests/test_column_contributions_governed.py` and `tests/test_host_plane.py`.
- **One leaves: `src/opendox/consumer_reach.py`** (openDox-code#9, `da8aae96`). openDox-code#77 (T084) deleted it at `e49b17c3` when it retired the reach module, so the walk at `dede32b4` never consumes the admission and the verifier would report it stale (`declared_admissions_unused`). It is removed, as split-opendox § 3.4 slice S5 removed its two re-homed modules', and `tests/carve_arrival` asserts it ABSENT. openXdox-code's own `src/openxdox/consumer_reach.py` (the RULED seed) is still at `56e1c238`, and its entry stays.
- Neither spec leg moved (`f7ee3c76`, `f088b097`), so neither admits anything.
- **Derived, not run green, for the `-code` legs**, as at T047 and T064: the walk, with every rule `verify-carve-arrival.py` applies, reports stale 0 and only the carve-time files that are unchanged since the old pins (4 at `opendox_code`, 3 at `openxdox_code`), which T064 left as well (decision (d)).

## T094's "Owed elsewhere" list from opensoft/openXdox-code#37, each item to the file that carries it

- [x] `scripts/opendox_host.register_openxfactory()` calls `column_contributions.register()` after T064's projection line (Q6 (a)): `scripts/opendox_host.py` (`3c1c1146`).
- [x] The module seals: `scripts/ideation_dashboard/dashboard_refresh_lane.py` and `tests/ideation-dashboard/test_dashboard_source_seal.py` (`1c5f8edb`). The carve admissions (`created:` gains `src/openxdox/column_contributions.py`, `tests/test_column_contributions.py`, `tests/test_column_contributions_governed.py` and `tests/test_host_plane.py`): `docs/opendox-carve-admissions.yaml` and `tests/carve_arrival/test_verify_carve_arrival.py` (`b3a4b6e0`).
- [x] The parity test's MRO layout and `test_serve_column_split.py`'s four snapshot rows, which move to the core: `tests/ideation-dashboard/test_extension_point_parity.py` and `tests/ideation-dashboard/test_serve_column_split.py` (`3c1c1146`).
- [x] `test_openxfactory_host_wiring.py`'s "second copy of the column" check and its contributed tuple, which now has three entries: `tests/domain_profile/test_openxfactory_host_wiring.py` (`3c1c1146`): `test_no_route_extension_declares_a_second_copy_of_the_column` holds each column to one declaration, and `CONTRIBUTED_COLUMNS` is `LaneRoutes`, `GateRoutes`, `ProjectionRoutes`.
- [x] The lane route's `hosted_ref_refused(` check (Q8 (a)): `tests/domain_profile/test_lane_route_asks_the_hosted_ref_predicate.py` (`eda75977`).
- [x] The trust policy at T100's seam (openXdox registers none; "Governance approval", #656 comment `5970369724`): `GovernedBindingTrust` in `scripts/opendox_host.py`, tested by `tests/domain_profile/test_host_registers_binding_trust.py` (`3c1c1146`).

## F11.1, on the real commits

`PACKET_MERGE=94b6f7f1… ARC_TIP=<this branch's head>` over the guard extracted from `openspec/changes/add-neutral-product-standalone-operability/tasks.md` prints:

> `requirement 1 holds: 0 note(s) annotated, every other path a declared surface (11.1)`

Every path this PR touches is on a declared surface:

- PIN_PAIRS: `openDox`, `contracts/opendox-pin.yaml`, `openXdox`, `contracts/openxdox-pin.yaml`;
- HOST: `scripts/opendox_host.py`, `scripts/profile_openxfactory.py`;
- HOST_TESTS: `tests/domain_profile/test_host_registers_binding_trust.py`, `tests/domain_profile/test_lane_route_asks_the_hosted_ref_predicate.py`, `tests/domain_profile/test_openxfactory_host_wiring.py`;
- COMPOSITION_TESTS: `tests/ideation-dashboard/test_extension_point_parity.py`, `tests/ideation-dashboard/test_serve_column_split.py`, `tests/ideation-dashboard/test_doxbench_status_exemption.py`;
- ADMITTED_ARC_EDITS: `scripts/ideation_dashboard/dashboard_refresh_lane.py`, `tests/ideation-dashboard/test_dashboard_source_seal.py`, `tests/openxdox_pin/test_openxdox_pin_verifier.py`, `docs/opendox-carve-admissions.yaml` and `tests/carve_arrival/test_verify_carve_arrival.py`.

The admitted list does not grow, and no manifest note is annotated here (decision (b)).

## The falsifiers, at the COMMITTED pins

| run | head | verifiers | `pytest-suite` | other checks |
| --- | --- | --- | --- | --- |
| **CI** | `16136990` | OK, OK | **`selected=9362 passed=9356 skipped=6 failures=0 errors=0`**, floors met (margin 2312), run `37315023063` | all success, `openxdox-consumer-gate` included |
| local | `eda75977` (`16136990` changes only a docstring and a comment) | OK, OK | **8943 passed, 7 skipped, 0 failed** (`pytest tests/ -q -m "not postgres"`) | the composition, `domain_profile`, carve-mapping, both pin dirs and `carve_arrival`: 569 passed |
| **CI** | `7e31eca1` | | PENDING | PENDING |
| local | `7e31eca1` | OK, OK | PENDING | `domain_profile`, the seal and the carve mapping: 626 passed; mutants M12-M19 killed |

Each local run uses Python 3.12.3, with `TMPDIR` and `--basetemp` under `~/.local/state`, outside the aggregation tree, in a checkout named `openxFactory`. A local run skips 7 where CI skips 6: the work tree sits inside an aggregation checkout, so the aggregation ignore-file case runs and passes there (#1215's reason).

- `validate-carve-manifest.py` prints OK (456 rows), and `validate-former-id-arrival.py --base origin/main --head HEAD` passes.

## Accepted limits

- A leg from before T100 now fails with an ImportError, because `seams()` imports `doxbench_trust`, rather than with `HostSeamsIncomplete` by name. The pins only move forward.
- A directory at the declarations path reads as "no document". That is openDox's own store rule (`document_present` checks for a regular file), and the change opens no new hole.
- Under the governed host, `model-binding trust` prints `trusted "m1" on this machine` although `record()` records nothing. That wording is openDox-code's (`cli_model_binding._trusted_line` ignores `recording.recorded`), and it is deferred to an openDox-code follow-on after release 1.
- The trust seam's take-back entry cannot fire today. Only the home seam follows it, and the home seam refuses nothing. Nit: `_TAKE_BACK` is keyed by the bare call name `"register"`.

## What this leaves for the aggregation

This PR does not do the opensoft/xFactory root pin-sync. When it runs, it needs:

- the root `openDox` gitlink → `e1e3a3c3f8dd38214510412b71a2e858c6179532`;
- the root `openXdox` gitlink → `9564d5d9462ffd1a3155d9177206368e5061efa8`;
- the root `openxFactory` gitlink → this PR's landed sha, with `.github/clearing/openxfactory/PIN.yaml` in the same commit (CLAUDE.md rule 2);
- `.github/workflows/dashboard-image-worker.yml`'s `RENDER_LEG_MODULES` and its mirror in `tests/test_dashboard_image_worker_contract.py` gain the same three modules as this PR's `1c5f8edb`.

Arc: neutral-product-standalone-operability

🤖 Generated with [Claude Code](https://claude.com/claude-code)


Lane: openxfactory-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
brettheap added a commit to opensoft/openDox-code that referenced this pull request Oct 5, 2026
…h no database service (plan 034) (#75)

Plan 034, task **T095**, slice `acceptance` (AT-R1, the HTTP half), phase 3. The plan is `specs/034-opendox-standalone-operation/tasks.md` at opensoft/openxFactory `main` `596a9a90`, § "Acceptance: AT-R1", as T007 batch N (`bdd0f586`) amends it. It realizes **FR-011's HTTP half**.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)

- **Claim:** openxFactory#656 comment [`5961416730`](https://github.com/opensoft/openxFactory/issues/656#issuecomment-5961416730).
- **Rulings:**
  - [`5961364221`](https://github.com/opensoft/openxFactory/issues/656#issuecomment-5961364221) item 2: T095 is drafted now, and does not go READY before T063 lands.
  - [`5850003126`](https://github.com/opensoft/openxFactory/issues/656#issuecomment-5850003126): R1Q10 (a), R1Q12 (a), R1Q13 (a) with (c), R1Q15 (b), and R1Q16 (iii) and (iv).
  - [`5963851934`](https://github.com/opensoft/openxFactory/issues/656#issuecomment-5963851934): the console token travels in the opened URL, and the harness reads the private copy.
  - [`5973854291`](https://github.com/opensoft/openxFactory/issues/656#issuecomment-5973854291), F1 (i): the chat rail reads a thread only with a branch session (openDox-code#85).
- **State: DRAFT.** Its `After` line is T089, T076, T104 and T007 batch N. Batch N (openxFactory `bdd0f586`), T104 (openDox-code#84, `32943cbf`) and T076 (openDox#17, `504324de`) have landed. Its READY waits only on T089, lane openXfactory-3's phase-3 checkpoint. The holder posts READY and the landers merge; this writer does neither.
- **Release 1, for T096's evidence:**
  - **P** = `dede32b4b6f3d0f147d599776f83628c5af8ff3d`, the openDox-code commit T087 pins in the openDox root (`e1e3a3c3`).
  - **X** = this PR's landing commit, RELEASE1_TIP, which lane openXfactory-3's T096 evidence fills in.
  - **Build inputs: this PR changes none; it changes only CI and tests.** It touches `.github/workflows/validate.yml`, `acceptance/at_r1_http.py`, `tests/test_at_r1_http_harness.py` and `tests_runtime/test_deploy_shape.py`. It does not touch `pyproject.toml` or anything under `src/`. To check, both distributions were built locally the way `release.yml` builds them: the hash-locked release tools, `python -m build --no-isolation`, and `SOURCE_DATE_EPOCH` set to the commit's time. That was done at P and at `d29e68ca`. The two **wheels** hold the same 129 files, each with the same bytes. Built at P's `SOURCE_DATE_EPOCH`, `d29e68ca`'s wheel is byte-identical to P's (`sha256 8ecea00d…`). With each commit's own time they differ only in the archive's timestamps. The **sdist** at `d29e68ca` holds one file more, `tests/test_at_r1_http_harness.py`, because setuptools' default sdist takes `tests/test*.py`; its `SOURCES.txt` lists that file. So what installs at X is what installs at P, and the source archive differs by that one test file.
- **Convergence:** the lexer's and the resolver's residual misreadings are accepted limits of the harness, under the holder's ruling. They are listed, with the checks showing that no shipped file uses them, in "Accepted limits" below.

## On this PR, `acceptance` and `validate` are both green

The stack the harness drives has landed. `main` `dede32b4` (2026-10-05), which is P, carries:
- #60 (T071), #65 (T088), #67 (T070) and #69 (T072, the bundled server and the `local` extra);
- #71 (T085), #72 (T073), #73 (T075) and #74 (T081);
- #64, #77 (T084) and #80 (T103);
- #81 (T102), #85 (the T102 follow-on), #76 (T082) and #82 (T100);
- **#84 (T104, the console token via the opened URL)**, which landed on 2026-10-05;
- #86 (the T100 follow-on, the trust store's review findings), #78 (T099, the release workflow) and #79 (version 0.1.0).

The harness reads the console token the way T104 delivers it (step 6 below). Until T104 landed, this PR's `acceptance` job failed **by design**, at `[a.capabilities carries no console token]`. This branch now merges `main` `dede32b4` (P), so its own job runs the harness against the delivery it was written for, #86's trust store included. At this head `d29e68ca` it printed `AT-R1 HTTP half: PASS (314 assertions held)` (run 37316440405, job 111784259300). The `validate` job runs the whole suite and is green: `triple: selected=5379 passed=5368 skipped=11 failures=0 errors=0` (run 37316440405, job 111784258794).

**Making `acceptance` a required check is a ruleset change for the repository's owner** (`docs/branch-protection.md`). This PR does not touch the ruleset.

## What changes (four files, nothing else)

| file | change |
|---|---|
| `acceptance/at_r1_http.py` | NEW. The harness. Standard library only. Not a pytest module: it sits outside `tests/` and `tests_runtime/`, so `testpaths` never collects it and #1144's F9.1 is unchanged (FR-006's "no exclusion" holds). |
| `.github/workflows/validate.yml` | A new `acceptance` job, appended after the last line. It has **no services**, runs no pytest and installs nothing itself: one step, `python3 acceptance/at_r1_http.py`. The `validate` job and its triple are untouched. T082 (#76) re-pinned the triple's floors on `main` (3977 / 3966), and T100 (#82) changed only the `validate` job's comments; this PR merged both cleanly. |
| `tests_runtime/test_deploy_shape.py` | Required, because `test_the_workflow_declares_the_required_job_and_its_steps` pinned `sorted(jobs) == ["validate"]`. It now admits `["acceptance", "validate"]`. A new case holds `acceptance` to no service or container, no pytest run, no `pip install` step, and one harness step outside `testpaths`. And `test_every_install_of_this_package_reads_one_dependency_lock` now also requires that the harness passes the lock to pip. |
| `tests/test_at_r1_http_harness.py` | NEW. It tests the harness's DECISIONS in process, as `tests/test_smoke_signals.py` tests the browser half's oracle: it loads the harness from its file, against an in-process loopback server, so nothing is installed or started. **564 cases.** Every decision below has its cases, and 221 mutants of those decisions are each killed (below). |

The two test changes add cases to SELECTED and PASSED, and none skips; the floors permit a rise, and no floor is edited.

## The documented command, and how the harness runs it

T007 batch H's 10.3 addendum (`openspec/changes/add-neutral-product-standalone-operability/tasks.md`, 10.3, "AMENDED — T007 Batch H (`5850003126`; Ruled R1Q15 (b), R1Q16 (iii))"), verbatim:

> The command the root's `README.md` documents is the standalone install and its one start: `pip install "opendox[local]"`, then `opendox generate-and-open --local …`. The flag selects the local mode explicitly (13.4 as this batch amends it), and the `local` extra carries the bundled server that mode starts (13.1 as this batch amends it). After the install, that start is the single command requirement 10's second scenario has a user run. The entry point is still the code leg's console script, and no `Makefile` target is added, for the reason above. Carried out by T070 and T076.

The harness holds both lines as constants (`DOCUMENTED_INSTALL`, `DOCUMENTED_START`, the `…` being U+2026 after one space). Compared mechanically with T076's README (opensoft/openDox#17 at `7eef03b4`):

```
DOCUMENTED_INSTALL = 'pip install "opendox[local]"' (6f63616c5d22 tail bytes): README lines [32]
DOCUMENTED_START = 'opendox generate-and-open --local …' (616c20e280a6 tail bytes): README lines [33]
```

- **The start line.** The harness runs `opendox generate-and-open --local --repo-root <repo> --repository fixture --no-open --port <free port>`. That is quickstart.md § 3's filled form, with `--port` given a port found free at run time and checked free on 127.0.0.1 and ::1 just before the start. Before it starts anything, it asserts that its argv begins with exactly the tokens of `DOCUMENTED_START` before the `…` (`start.documented-command`). The `…` is the verb's own arguments, as the README says, and every flag above is one the README names. `opendox` is the fresh venv's console script, resolved on the child's PATH and asserted to lie inside that venv; argv[0] stays the literal `opendox`.
- **The install line.** No release of `opendox` is published (PyPI answers 404 for `opendox`; the release-channel question is with Brett). So the harness installs the same distribution with the same extra from the checkout: `pip install "<checkout>[local]"`, from a copy of this checkout's tracked files. That is exactly the substitution the README makes for its first line (`pip install "./code[local]"`, run from the root). It installs through `constraints-cpython312-linux.txt` where the lock's own interpreter runs it (CPython 3.12 on Linux, as in the job). The lock pins versions and adds no package, so the install is still `opendox[local]` and nothing else (AT-R1 step 2).

## What the harness asserts, in order (AT-R1 steps 1-4, and the route answers behind 5-8)

Each assertion has an id. The run stops at the first that fails and prints `AT-R1 HTTP half: FAIL [<id>]: <why>`, then exits 1. `--keep-going` reports every failure instead, and still exits 1. A harness that breaks before a verdict exits 2, never 0.

1. **Install.** A fresh venv, then `opendox[local]` (R1Q16 (iii)). Then `install declares the local extra`, read from the installed distribution's metadata.
2. **The clean machine, ASSERTED** (`clean.*`), in a fresh `OPENDOX_STATE_DIR` created empty for the run:
   - none of `openxdox`, `ideation_dashboard`, `doc_health` and `corpus_adapter_openxfactory` is importable in the venv (`python -I`, from an empty directory);
   - `omp` is not on the PATH, nor is the product's own `doxbench_bridge.HARNESS_COMMAND`;
   - no identity broker (`openprofiler-broker`) is on the PATH, and no issuer setting is set;
   - nothing listens on 127.0.0.1:5432 or [::1]:5432;
   - no PostgreSQL Unix socket answers in a shared directory. The harness reads every listening `.s.PGSQL.*` socket the kernel lists in `/proc/net/unix`, plus the distribution sockets as a fallback;
   - no DSN or `PG*` setting reaches a child;
   - neither repository holds a binding at the product's own `doxbench_binding.DEFAULT_BINDINGS_RELPATH`;
   - no bundled-server process exists before the start.

   Every child runs with `GIT_*`, `XF_*`, `OPENDOX_*`, `PG*`, `DATABASE_URL` and `PYTHONPATH` dropped, a fresh `HOME`, and `TMPDIR` inside the scratch directory. Before anything is installed, the harness also refuses a `TMPDIR` that another user could change (exit 2, by T104's own rules for the state tree; step 6), so its verdict on that tree judges what the product made of it.
3. **Two plain repositories**, each a fresh `git init` (spec.md AT-R1 step 3): (a) `tests/fixtures/plain-documents`, and (b) quickstart.md § 2's three notes, byte for byte, with no front matter. Each gets `git config user.name`/`user.email`, which the served actor is read from.
4. **The documented start**, once per repository (`start.*`): the port is free, the argv matches, the console script lies in the venv, the server is ready within 120 s, and the process that answered is still the one it launched.
5. **Fetches.**
   - `/` must be HTML by its type's essence (`text/html`, parameters aside), with an `<html>` element. It must also be UTF-8, as the harness reads it: a UTF-16 BOM, or a `charset` naming anything but a UTF-8 label, in its `Content-Type` or anywhere in the page, fails `[x.http / is UTF-8]`.
   - `/snapshot.json` must be a JSON object, non-empty, every document an object with a non-empty string `path`, and neutral per F5.3: none of #1144's fourteen declared words (5.5's falsifier list, verbatim) in any string value.
   - The grouping station, named by `/capabilities`' own display facet, must hold a tile with an id and a member document, so a grouping tile can open the chat pane (R1Q13 (a) with (c); this fails, and does not skip).
   - `/capabilities` must give `install.mode == "local"`. As quickstart § 3 asserts it (T007 batch N), its **raw** payload carries the console token **by name nowhere**: `console_token` is in neither the raw text nor any key of the parsed payload, however deep or however escaped (`[x.capabilities carries no console token]`). The by-VALUE half runs once step 6 has the token.
6. **The console token, as the user's browser is handed it** (T104). The start prints `console <file URL>`, the PATH of a private opener, and never the token. The harness reads that opener itself, with the standard library, and imports nothing from the product. The checks:
   - the start printed the opener, at `<OPENDOX_STATE_DIR>/console/<port>.html`, outside the served repository;
   - it is private: this user's regular file, mode exactly 0600, one link, in `console/` of mode exactly 0700 (as quickstart § 3 checks it). Its whole path is judged by T104's own rules for that tree: the state directory is this user's, and no one else can write it; every directory above it, as written and as resolved, is this user's or root's, and sticky where others can write it; every symbolic link on the way is this user's or root's;
   - it is opened with `O_NOFOLLOW | O_NONBLOCK`, read only if `fstat` calls it a regular file, and read WHOLE: a file past 64 KiB is refused by name, never judged by a truncated prefix;
   - it is a page the harness reads as the browser does: UTF-8 by the same rule as `/`, and holding no SVG, MathML, `frameset`, or refresh or script inside a `select`. Otherwise it forwards nothing, by name;
   - it has exactly one LIVE meta-refresh, as a browser that runs scripts parses the page. None inside a `<template>`, a `<noscript>`, a raw-text or escapable raw-text element, a `select`, or after a `frameset` counts. The first of a repeated attribute is read, `http-equiv` is `refresh` exactly, and `/>` closes only a void element. A tag that holds a named character reference with no `;` is not followed: `html.unescape` decodes it, and a browser's attribute rule does not;
   - that refresh is one a browser follows (the HTML standard's declarative refresh steps). It goes to this plane's console page (`/` or `/index.html`), on a loopback host the launched plane answers on, with no backslash, whitespace, control character or user information. `#console_token=<token>` is in the FRAGMENT, and never in the query or the path, raw or in any percent-decoding;
   - that token is one the page accepts: `[A-Za-z0-9_-]{16,512}`, read as T104's `takeDeliveredConsoleToken` reads it (`URLSearchParams` over the fragment);
   - its JSON record (`<script type="application/json" id="opendox-console">`, kind `opendox-console-access` v1, live, parsed as `JSON.parse` parses it) describes that same forward: its port, its token, its `opened_url` and its `page_url`;
   - the PAGE that forward opens is asked as the browser asks it: of the forward's own host (in `Host`, which the plane's loopback gate reads), with its path and query, and without its fragment. It must answer 200 as HTML (`[x.console page is HTML]`) and be UTF-8 (`[x.console page is UTF-8]`). Steps 7 and 8 read that page and its origin, never `/` in its place. T104's forward is `/index.html` on `127.0.0.1`;
   - and, BY VALUE (T007 batch N): the token is nowhere in `/capabilities`' raw payload, nor in any key or string value of the parsed one, under whatever name (`[x.capabilities carries the opener's token nowhere]`).
7. **The model catalog.** `/workbench/model-catalog` must refuse a caller with no token (4xx). With the opener's token in `X-XF-Console-Token`, it must answer 200 in the envelope the chat rail adopts (`views/doxbench-chat-model.js`, `adoptCatalog`):
   - `schema_version` is any JSON number equal to 1, never `true` and never `"1"`;
   - `kind` is `workbench-model-catalog`;
   - `models` is an array with no entry the rail would offer: none whose `available` is exactly `true`, as the rail reads it (16.4).

   The harness also asserts that the served bundle names both this route and this kind, so neither constant can drift from the bundle.
8. **Every route the panes can request**, asked at the console page's origin, answers, and none answers 5xx or drops the connection (derivation below). And the chat rail reads no thread: it reads `/workbench/thread` only through a branch-session column (`gate.workbench.session`, openDox-code#85), and the plane must contribute none (`[x.chat rail reads no thread (no branch session)]`).
9. **Stop and look.** SIGTERM to the entry point alone, as `kill` would, and a wait of up to 90 s. Then:
   - the entry point must exit 0;
   - no live process whose executable lies in the venv's `pixeltable_pgserver` package, or whose command line names the fresh state directory (R1Q16 (iv)), checked at the operating system;
   - the opener must be gone;
   - the token must appear nowhere in either output stream of the whole serve.

Cleanup runs however the run ends, but only AFTER the verdict is taken, so a leftover still fails the run.

**No line the harness prints quotes a console token.** Every token the run has seen is blanked out of every id, reason, note and the report (`Verdict.redact`), as it is and inside any run of `%XX` escapes that decodes to it, partly encoded or encoded twice: the tokens the opener carries, delivered or refused, and its record's; and one `/capabilities` publishes, under its name at any depth. `serve_one` reads the opener's tokens as soon as the start answers, before step 5 quotes anything (T104 writes the opener before it serves). No reason quotes the server's own bytes either: not the entry point's output, a `/capabilities` payload, a catalog body or its envelope's values or an available entry's id, the snapshot's documents or its grouping field, the opener record's values, a `<base href>`, a peer's error text, a printed opener path other than the expected one, or a `Content-Type` that is not a plain MIME type. Every printed line escapes what is not printable (a control, a newline, a bidirectional override, a lone surrogate), so no text a server sends can forge a line of the log or break one.

**Every parser reads as the browser reads, or refuses by name what it does not model:**
- JSON is parsed as `response.json()` parses it: a BOM dropped, an invalid byte read as U+FFFD, and `NaN` and `Infinity` refused. A body nested past the parser's depth is a named failure, never exit 2.
- JavaScript is lexed by its own whitespace (U+FEFF and every space separator) and line terminators, a hashbang line is a comment, and its strings are decoded with its escapes. An escape a module refuses is a named failure.
- HTML is read with the first of a repeated attribute, and live elements only. A script is what its `type` and `language` make it. SVG, MathML, a `frameset`, a script, link or base in a `select`, and one whose attributes hold a named reference with no `;` (`&copy=2`, which `html.unescape` decodes and a browser does not) are refused by name. A page is read as UTF-8, and refused by name where a browser might read it otherwise.
- URLs are resolved against the running server's origin. A reference a browser reads differently is refused by name: a backslash, a tab or newline, whitespace or a control at an end, user information, or a percent-encoded dot segment. So are a `<base>` other than `/` and an import map.
- A string from JavaScript or JSON is read in Unicode scalar values, as a browser reads a URL from it: a surrogate pair joined, and a lone surrogate read as U+FFFD.
- Request targets are percent-encoded as a browser encodes them. A `Content-Type` is parsed as the MIME Sniffing standard parses it, and one with more than one value is refused by name. A redirect is refused by name, because a browser follows it.

## How the route list is derived (from the served JS, not by hand)

`derive_bundle` reads the bundle **from the running server**, resolving against its origin (`http://127.0.0.1:<port>`):

1. It fetches `/`, then follows the page's LIVE module scripts (`<script type="module" src>`, `type` and `language` read as the HTML standard reads them) and `rel~=stylesheet` links. Both resolve against the page's base URL: its `<base href>`, which must be `/`, or the page's own. A view module `/capabilities` declares is walked too, but it is no entry:
   - a page with no live module script is `[bundle.entry]`;
   - a classic script, by `src` or inline, is `[bundle.classic-script <src>]`, unless it is marked `nomodule`;
   - an inline module script is `[bundle.inline-module]`;
   - markup the harness does not model is `[bundle.unmodeled]`.
2. In every module, a small JS lexer strips comments and regular-expression literals, decodes JavaScript's string escapes, and yields each string literal, in Unicode scalar values, with the code before it. Every run of JavaScript whitespace, and every comment, reads as one space there, so no run is too long. A template's `${…}` is read as code, starting an expression, so an import or a route inside one is found. A `/` divides after an operand (a name, a literal, a call, a group, a postfix `++` or `--`), and opens a regular expression after a prefix operator or the `)` of an `if`, `while`, `for` or `with` condition. After a `}` it does either, by whether the `}` ends a block or an expression, which a lexer cannot tell: a module with a `/` right after a `}` is `[bundle.ambiguous-slash <path>]`. A keyword is read as one only where it is no member's name (`obj.return`, `obj?.of`) and no part of a longer or a private name (`ñreturn`, `this.#if`); after a spread's `...` or a decimal point (`1. in`) it still is. `of` is a keyword only as a `for` head's separator, after its binding or target (`for await (` opens a head too), and a name elsewhere, a classic loop's initializer, condition and update included; a `/` after a spread's `...`, a division's own `/`, `export default`, `extends`, or `break`, `continue` or `debugger` and a line break opens a regular expression. So does one after a line break that ends a statement by automatic semicolon insertion: before a `++` or `--` (then a prefix), and after a labelled `break` or `continue`, a declaration's lone binding, or a static import's specifier. That code tells an `import … from "x"`, an `export … from "x"` or an `import "x"` from an ordinary string, and from a method named `import` (`loader.import("x")`). A module holding an escape a module refuses (a legacy octal escape, `\8`, `\9`, or a malformed `\x` or `\u`, outside a tagged template) is `[bundle.syntax <path>]`.
3. It follows every static specifier, every literal dynamic `import("x")`, and every view-binding module `/capabilities` declares, transitively. Each module is fetched once, and judged once per way it is imported:
   - A static import must answer 200, even of a path another module imports dynamically. A failed one is a module-load `pageerror`, which AT-R1 step 8 forbids.
   - A dynamic import may be refused, but not with a 5xx. Standalone, `/views/intent-feed.js` answers 404 (10.2a, not owed), and its importer degrades.
   - A served module must be JavaScript by its type's essence, and a linked stylesheet `text/css`. A redirect is refused by name, never judged by its status.
   - A BARE specifier is `[bundle.bare <specifier>]`. A module or stylesheet from another origin (another host, port or scheme, `data:`) is `[bundle.external <url>]`, and is never fetched from loopback by its path. A same-origin absolute URL (`http://127.0.0.1:<port>/x.js`) is a path of this plane.
4. In every module of that graph, every string literal that is a same-origin path (`/` or `./` followed by a letter, not a `.js`/`.css` path) is a route the bundle can request. At the integration below, that is **38 modules and 18 routes**.

A static read cannot tell a load-time request from an on-click one, so the harness requests ALL of them, a superset of the wheel's, the lens's and the chat rail's load-time reads. T096's browser run observes the load-time set itself. The nine `/actions/…` routes get a GET too; a GET there finds no handler (404), so it never executes an action.

A prefix ending in `/` (`/source/`) is completed with each document the snapshot lists, plain and keyed by the workbench's key (`/source/fixture%40main/<doc>`), as the panes complete it.

The thread read (`/workbench/thread`) is asked as its bare literal only, as every literal is. Since openDox-code#85, a standalone plane's rail sends no thread read at all, and the harness no longer completes it with the query it once claimed the rail sends (the holder's T096 dry run, F4).

Every request carries the console token the opener delivered, as the doxBench transports do, so a guarded read answers from its handler and not from the console check.

## Accepted limits (the holder's convergence ruling)

The harness reads the bundle with a lexer, not a parser. The holder ruled, under the rule of openxFactory#656 comment [`5988818366`](https://github.com/opensoft/openxFactory/issues/656#issuecomment-5988818366) (written for #86 and applied to this PR), that misreadings only a parser could fix are recorded as accepted limits of the acceptance harness, not chased round after round. A form is fixed now only if a shipped openDox web file contains it, or if the fix is one line with a test. The limits are recorded in `JsStrings`'s docstring and in `_resolve`'s.

**The lexer** (`JsStrings`):
- A `/` right after a `}` divides after an expression's `}` and opens a regular expression after a block's. Such a module is refused by name (`[bundle.ambiguous-slash <path>]`).
- In one form, the `/` is read wrongly. After a declaration list's last binding with no initializer, then a line break (`let x, y` [line break] `/re/`), automatic semicolon insertion ends the declaration, so the `/` opens a regular expression. The lexer reads a division, and an import after it on that line can be missed. Telling that comma from an expression's comma takes knowing that it lies in a declaration.

**URL resolution** (`_resolve`). `urllib.parse` reads a few references differently from the URL standard, and these are not refused:
- `///a.js` and `http:///127.0.0.1:<port>/a.js`;
- an empty path segment (`.//a.js`);
- a dot segment or an empty path in an absolute same-origin URL;
- an unencoded `^`;
- a loopback host that the URL standard rewrites (`127.1`, `0x7f.0.0.1`, `2130706433`, `127.0.0.1.`), which is judged an external host.

**No shipped file uses any of them.** These checks ran locally at `d29e68ca` over the 42 files the build packages (`web/**`, `web/**/.*`): 39 modules, the page, its sheet and `vendor/.gitkeep`.
- The lexer against acorn 8.18's exact token stream (a full module parse): they agree on all 205 `/` decisions, 132 regular expressions and 73 divisions. They agree on all 82 static imports and export-froms. No module is ambiguous or malformed. No regular expression follows a name, a literal, a `)`, a `]` or a `}` (3 follow `return`), so the residual form, which needs a binding name and then a line break before the `/`, occurs nowhere.
- The resolution against node's WHATWG `URL`: all 84 of the shipped modules' import specifiers, static and dynamic, resolve alike.

**How the limits were found.** A differential check of the lexer against node's own module parser, the static module requests of a `vm.SourceTextModule`. It covers 196 contexts, each a regular expression or a division before a static import, with five kinds of separator: 1960 snippets, 978 of them valid modules. In those, the lexer hid the import in 195 at `ff04e015`, in 85 at `09d9d632`, and now in the 10 of the residual form. It has no false failure at any of these heads, and refuses the same 10 by name at each. A second check, of 81 contrived references against node's WHATWG `URL`, finds 13 differences: the 12 that the URL list above records, and `//a.js`, which is an external URL either way. The overview at `e4f81d48` named identifier-parsing errors; that class was a one-line fix with a test, so it is fixed in `d698c6ea`.

## Falsifier: the harness itself

### At this branch, `main` `32943cbf` plus the harness (what this PR's own `acceptance` job runs): PASS

The job at `d29e68ca` (run 37316440405, job 111784259300):

```
AT-R1 HTTP half: PASS (314 assertions held)
```

Locally, the same tree, run as that job runs it (`python3 acceptance/at_r1_http.py` from the checkout's root, with no database service; `r83`, at `d29e68ca`, which merges `main` `dede32b4`):

```
AT-R1 HTTP half: PASS (314 assertions held)
```

Its log is line for line the same as `r82`'s (`d698c6ea`, before `dede32b4`) and `r76`'s (`ff04e015`, the first head with `main` `32943cbf`), apart from ports, process ids and scratch paths. That includes `[a.capabilities carries no console token]` and every console-opener check.

The `validate` job's suite, run locally at `d29e68ca` with the job's install line and a PostgreSQL 16 standing in for its service, printed `triple: selected=5379 passed=5368 skipped=11 failures=0 errors=0`. So no change since then moved a module, a route or an assertion. Neither log holds a token-shaped run.

The first green `acceptance` job ran at `d50e8cef`, once #84 had landed. Its merge ref with `main` `32943cbf` (`ff68275a`) gave `PASS (314 assertions held)`, and so did `r75`, locally, on a merge of the same two commits whose tree is identical to CI's.

### Before T104 landed: FAIL, named

At `main` `38d3350e` plus the harness (`d50e8cef`, `r74`, `--keep-going`), the harness failed only where T104 was missing, in both repositories. In CI it stopped at the first of these: `1 failed, 34 held`.

```
== r74-r27-pr-tree-keepgoing rc=1 held=282
AT-R1 HTTP half: FAIL [a.capabilities carries no console token]: …
      also FAIL [a.console opener printed]: the start printed no `console <file URL>` line naming the opener, …
      also FAIL [a.catalog answers]: /workbench/model-catalog with no console token to present (step 6 found none) answers HTTP 403 (its body is not quoted)
      also FAIL [b.capabilities carries no console token]: …
      also FAIL [b.console opener printed]: …
      also FAIL [b.catalog answers]: …
      6 failed, 282 held
```

No token-shaped string appears anywhere in that log, though that tree's `/capabilities` published the token.

The expected red moved as the stack landed:
- at `main` `047bb4fa` and `66ff7257`, it was `[install declares the local extra]`;
- once #69 landed, it was `[a.capabilities install.mode == local]`;
- from #72, it was the token delivery above, which only T104 supplies;
- since #84 landed (`32943cbf`), it is green.

### Against T104 before it landed (local integrations, never pushed): PASS

**#84 `fb8a1cc4` with `main` `38d3350e` merged in** (`fc9b39b5`, local only). #84 already merges `main` `ca9e1bd5`. The merge's one conflict was the web census's class-A total, resolved as base plus both deltas (18486 + 40 + 95 = 18621). With `b7b9b843`'s harness, `--keep-going` (`r71`):

```
== r71-r26-on-live15-t104-keepgoing rc=0 held=314
AT-R1 HTTP half: PASS (314 assertions held)
```

Each earlier head of this round passed at the integration of its day:
- `r73` (`d50e8cef`'s harness) and `r69` (`0717f72f`'s): the same integration, 314 held;
- `r67` (`3392f934`'s), `r65` (`9229c659`'s) and `r63` (`ba216f84`'s): the same integration, 310 held, before the console page checks;
- `r61` (`4bdb41fb`'s) and `r59`: #84 `fb8a1cc4`, 306 held, before the UTF-8 and entry checks;
- `r57` and `r58`: #84 `182cac76` plus `main` `ca9e1bd5`;
- `r53`: #84 `d4b99436` plus `main` `c4b55cc4`, after #85;
- `r49`: #84 `d4b99436`.

**Verdict at `main` `32943cbf` with this branch: AT-R1's HTTP half passes, with 314 assertions held.** That covers:
- the install, the clean machine and both repositories;
- the documented start: ready, with the process the harness launched;
- `/` as HTML, and UTF-8;
- the neutral, non-empty snapshot with a grouping tile: repository (a) has 8 documents, and repository (b), with no front matter, has 3;
- `install.mode == local`, and the token on `/capabilities` neither by name nor by value;
- the opener: printed, at `<OPENDOX_STATE_DIR>/console/<port>.html`, private down its whole path, a page the harness reads as the browser does, forwarding with the token in its fragment, and with a record that agrees;
- the page it opens, `/index.html` on `127.0.0.1`: 200, HTML and UTF-8, and the page whose bundle, routes and catalog the rest of the run reads;
- the catalog: it refuses a caller with no token, and with the token answers in the envelope the chat rail adopts, with no available entry;
- one live module script as the entry, and 38 modules, none bare, divergent, malformed or from outside the plane, every one served as JavaScript and every static import 200; `/views/intent-feed.js` refused as 10.2a's not-owed dynamic import; `/styles.css` served as `text/css`; no `<base>`, no import map, no classic or inline script, and nothing unmodeled;
- all 18 derived routes and every document's `/source/` form below 500, none a redirect, and no branch-session column, so no thread read;
- the stop: exit status 0 within the bound, no bundled PostgreSQL process left, the opener removed, and the token in neither output stream.

Pass (a)'s route answers (`r76`):

```
/actions/{dtn-seed,edit,notebook,refresh,staging-seed}                        HTTP 404
/actions/workbench/{chat-turn,document-abstract,model-approval,model-intake}  HTTP 404
/capabilities HTTP 200       /logout HTTP 404            /snapshot-index.json HTTP 404
/snapshot.json HTTP 200      /source/ HTTP 404           /source/<each of 8 documents> HTTP 200
/source/fixture%40main/<each of 8 documents> HTTP 200    /project-register.json HTTP 404
/workbench/model-catalog HTTP 200    /workbench/model-intake HTTP 200    /workbench/thread HTTP 400
```

### Before T084: two product gaps, both T084's, both removed by #77

The same harness against the integration without #77 (`e7187f06`: main + #69 `f66e5f82`, #72 `1b0c3a63`, #73 `71d24af6`, #71 `83213eb2`, #74 `9061b22a`, #65 `c0a97648`) held 198 and failed 4. The 4 were these two gaps, each hit in both repositories:

```
== r6-integ-keepgoing rc=1 held=198
AT-R1 HTTP half: FAIL [a.route /project-register.json]: GET /project-register.json answers no answer (RemoteDisconnected: ...)
      also FAIL [a.route /workbench/thread?repository=fixture&ref=main&tile_kind=cluster&tile_id=grouping-compost-corner&document=grouping-compost-corner.md]: ... no answer (RemoteDisconnected: ...)
      also FAIL [b.route /project-register.json]: ... no answer (RemoteDisconnected: ...)
      also FAIL [b.route /workbench/thread?repository=fixture&ref=main&tile_kind=cluster&tile_id=budget-roadmap&document=budget.md]: ... no answer (RemoteDisconnected: ...)
      4 failed, 198 held
```

1. **`GET /project-register.json` dropped the connection.** `src/opendox/serve_project.py:271` (at `main` `047bb4fa`) did `from openxdox.gate_console import DEFAULT_RECORDS_DIR` (and `:272`, `from openxdox.kickoff import …`), so it raised `ModuleNotFoundError: No module named 'openxdox'`. The page requests this route on every load (`app.js:1004` → `views/repo-selector.js:138-139`). It is batch L's named crash site (`5920216845`).
   **Removed by #77.** The handler now reads `column_seams.gate` and `column_seams.kickoff`. openDox's default discovers no register, so the route answers the existing structured `404 "no project register"`, and the picker hides.
2. **The chat rail's thread read dropped the connection.** `src/opendox/serve_workbench.py:560` at the pre-T084 integration (`:548` at `main`) did `from openxdox import doxbench_scope`, which is reached once the five query fields are present. The rail requests it when it opens on a document (`views/staging-workbench.js:2925`). It was **not** among batch L's three named crash sites; the holder relayed it to T084's writer.
   **Removed by #77.** The handler now uses openDox's own scope type. With no live session on that scope, it answers `403` with the no-live-session absence body.

No `from openxdox` import is left in either file at `d05c266e`, now that #77 has landed on `main`, and the runs with #77 found **no new gap**.

### The assertions bite: twelve product mutants, four workflow mutants and 221 decision mutants, all killed

Product mutants, each run through the full harness: m1, m2, m4 and m5 at `b4fc637f`, and m3 again at `b440d12b`. The only harness change since `b4fc637f` is `main()`'s handling of an error while preparing. m1 and m2 were merged from `main` when #69 was at `fedfa75d` and #72 at `20032d02`. m3 to m5 are working-tree edits of the integration as it was then, `8e4203d8` (before T084):

| mutant | tree | killed at |
|---|---|---|
| m1, no T081 | integration without #74 (`25f1f6af`) | `[a.catalog offers no available entry]`: "no model is configured, yet the catalog offers ['omp-local']" |
| m2, no T073 | integration without #72 (`f3957515`) | `[a.capabilities install.mode == local]`: "the served install block is None" |
| m3, the server is never stopped | `bundle.BundledServer.stop()` returns at once, and no parent-death signal | `[a.stop leaves no bundled PostgreSQL process]`: "still running after the entry point stopped: pid 599656: …/pixeltable_pgserver/pginstall/bin/postgres -D …" (and the harness's cleanup then removed it) |
| m4, a governance word in the fixture | `title: A slow leak at the rain barrel, ratified` | `[a.snapshot neutral (F5.3)]`: "openxFactory's vocabulary leaked into the neutral snapshot: ['ratified']" |
| m5, a missing module | `src/opendox/web/views/lens-model.js` deleted | `[a.bundle.module /views/lens-model.js]`: "imported statically by /app.js, answers HTTP 404" |

T104's delivery, each a working-tree edit of the integration WITH #84, `a945ef01`, run through the full harness at `d53a7378` (`r33`):

| mutant | edit | killed at |
|---|---|---|
| m6, a readable opener | `console_access.PRIVATE_MODE = 0o644` | `[a.console opener is private]`: "… has mode 644, not 600" |
| m7, the token in the query | `opened_url` joins with `?` instead of `#` | `[a.console opener forwards with the token in its fragment]`: "the opener's forward carries the token in its QUERY …" |
| m8, the opener outlives the server | `remove_private_copy` returns at once | `[a.stop removes the console opener]`: "… is still there after the server stopped …" |
| m9, the token back on `/capabilities` | `build_server` publishes it on every plane | `[a.capabilities carries no console token]` |
| m10, the token printed | `generate-and-open` prints `token <token>` | `[a.console token never printed]` |
| m11, an unguarded catalog | the catalog's console check removed | `[a.catalog refuses a caller without the console token]`: "… answers HTTP 200 …" |
| m12, the token on `/capabilities` under another name | `build_server` adds `capabilities["session_hint"] = console_token`, at #84 `d4b99436` (`r51`) | `[a.capabilities carries the opener's token nowhere]`, and no token in the log |

The harness's own decisions: **221 mutants**, each a one-line edit of `acceptance/at_r1_http.py` at this head, run against `tests/test_at_r1_http_harness.py`. **All 221 are killed**, each at a named case. By family:
- the inert content of a page (`lv1`-`lv9`);
- the state tree's rules and the `TMPDIR` refusal (`tr1`-`tr8`), and `console/` at 0700 exactly (`dm1`);
- the redaction (`rd1`-`rd9`), the published token at any depth (`nv1`), the payloads never quoted (`fo1`, `im1`), and the tokens learned before step 5 (`pk1`);
- the raw payload by name and by value (`rp1`-`rp6`), and the by-value check run (`rw1`);
- the chat rail's thread read (`th1`-`th3`);
- the opener read whole (`ol1`, `ol2`), the bodies and peer text never quoted (`cb1`, `cb2`, `en1`), and the `Content-Type` shown only as a plain type (`ct1`);
- the plane's own origin (`og1`-`og5`);
- JSON as the browser parses it (`js1`, `jc1`, `bj1`, `bj2`);
- divergent references (`dv1`-`dv4`, `ui1`), and encoded dot segments only (`ed1`-`ed5`); JavaScript escapes (`le1`-`le4`), line comments and a hashbang (`le5`, `le6`), and escapes a module refuses (`ms1`-`ms8`); the page's links (`ht1`, `il1`, `il2`, `bs1`, `im2`); encoded targets (`bt1`); unterminated references (`ur1`); and availability as the rail reads it (`av1`);
- only a live module script is an entry (`sr1`-`sr10`), and markup the harness does not model refused (`um1`-`um6`, `um6` an attribute's unterminated reference);
- the page walked is the one the opener opens, asked of its own host with its query, as HTML and UTF-8, with its modules, sheets, routes and catalog at its origin (`cp1`-`cp12`), an IPv6 host bracketed (`og6`), and the page's links resolved against its base URL (`bd1`, `bd2`);
- Unicode scalar values (`su1`-`su4`), and every printed line escaped (`pr1`-`pr5`);
- JavaScript's whitespace, collapsed, and a method named `import` (`ws1`-`ws4`, `ff1`); a template's `${…}` read as code, starting an expression (`tp1`-`tp3`); a `/` read as division or a regular expression by what ends before it (`rg1`-`rg6`), and one right after a `}` refused (`am1`, `am2`); a member's name never a keyword (`km1`-`km4`), a spread's and a decimal literal's `.` no member access, and a name read whole, a private one with its `#` (`km5`-`km16`); every keyword read by where it stands, `of` only in a `for` head, `for await (` a condition, a spread before an expression (`kw1`-`kw15`), and `of` the separator only after a `for` head's binding or target (`os1`-`os10`); a regular expression after a division's `/` (`pu1`) and after a line break that ends a statement (`lb1`-`lb12`); and every character of a name read as the name's (`id1`);
- a redirect refused (`rx1`, `rx2`), a repeated header joined (`hj1`), and `Content-Type` parsed as a browser parses it (`mt1`-`mt3`);
- UTF-8 pages (`cs1`-`cs7`);
- redaction through percent-encoding (`re1`, `re2`), and no reason quoting a value the catalog, the snapshot, the record or `/` sent (`cq1`, `cq2`, `sq1`, `gq1`, `rq1`, `rq2`, `bq1`).

Earlier heads' decision mutants (64, from `d53a7378` to `64dc06f5`) were each killed at their own head; their decisions stand, and their cases still pass.

Workflow mutants, against the three affected cases of `tests_runtime/test_deploy_shape.py`:

| mutant | killed by |
|---|---|
| `acceptance` gains a `postgres` service | `test_the_acceptance_job_runs_the_harness_on_a_clean_machine` |
| the harness drops `-c <lock>` | `test_every_install_of_this_package_reads_one_dependency_lock` |
| `acceptance` chains `&& python -m pytest -q` | `test_the_acceptance_job_runs_the_harness_on_a_clean_machine` |
| `acceptance` gains its own `pip install .` step | both cases |

Restored: `3 passed`. Locally, `tests_runtime/test_deploy_shape.py` and `tests/test_triple_pin.py` give `115 passed`.

### How the harness is run, and where the bundled server's state lives

The harness takes **no path and no port** (only `--keep` and `--keep-going`). It installs the checkout its own file is in, and its scratch and `OPENDOX_STATE_DIR` are fresh `tempfile` directories under `TMPDIR`. To measure an integration or a product mutant, this writer copied the harness, untracked, into that tree's `acceptance/` and ran it there.
- **In CI**, they sit in the runner's temporary directory, `/tmp`, which is sticky and root's.
- **The socket path is capped.** The bundled server's socket is `<OPENDOX_STATE_DIR>/postgres/run/.s.PGSQL.5432`, and Linux takes at most 107 bytes. A run whose socket could not fit is refused with exit 2, before anything is installed.
- **The local runs above** used `TMPDIR=~/.local/state/t095-tmp`, a 0700 directory whose every ancestor is this user's or root's. It was empty after every run, and no bundled PostgreSQL started by these runs was left running.

## Review rounds

- **SonarCloud** (advisory; the gate failed at `1c064bb3` on Security Rating D, and at `0717f72f` on Security Rating B, and passes at every other head since `bbdeb9ec`). At `0717f72f`, four `python:S5332` at `plane_origin`, which built an `http://` literal around the forward's host: fixed in `b7b9b843`, which builds the origin with `urlunsplit`. Fixed in `bbdeb9ec`:
  - `pythonsecurity:S8703`, `S8705`, `S8707`: the `--port`, `--scratch`, `--state-base` and `--checkout` options flowed into a socket, the server's argv and the filesystem. They are gone.
  - `python:S5443`: the `/tmp/.s.PGSQL.5432` probe is replaced by the distribution sockets.
  - `python:S3776`: the lexer is a class, and the long functions are split one per step.
  - `python:S9073`: the acceptance job's two assertions are split.
- **Copilot**, 32 rounds. Every finding was real and was fixed with evidence, a case and a mutant; every thread is answered and resolved. Three overviews named a class with no finding: the line comments' (fixed), the identifiers' (fixed) and the URL resolution's (an accepted limit, see "Accepted limits").
  - At `1c064bb3`, fixed in `32ef3e8c`:
    - `r4170450448`: a module refused as a dynamic import hid a later static import of the same path.
    - `r4170450491`: a malformed 200 catalog raised a harness error instead of a named failure.
  - At `bbdeb9ec`: `r4170537382`, the same static-after-dynamic case. Already covered by `32ef3e8c`, and answered there.
  - At `32ef3e8c`, fixed in `b4fc637f`:
    - `r4170537350`: a server on `/tmp`'s socket alone, with TCP off, passed the fixed socket list.
    - `r4170567257`: a non-list `documents` value passed as "non-empty".
    - A "previously missed" item: a grouping value with no tile holding an id and a member document passed, and silently dropped the rail's thread read.
  - At `b4fc637f`, "Needs a closer look" with no new finding. It noted that the PR needs integrated verification with the dependent product changes: that is the integration above, now including T084. Its "previously missed" item, fixed in `b440d12b`: an `OSError` in `prepare()` escaped `main()` with exit 1. This head returns 2; at `b4fc637f`, an `ENOSPC` from `tempfile.mkdtemp` escaped.
  - At `b440d12b`, "Needs a closer look" with no findings. Its "previously missed" item, fixed in `f0e0ffe1`: the module-graph verdict had no collected regression test, and on this branch the acceptance job stops before `derive_bundle`. The new `tests/test_at_r1_http_harness.py` above answers it.
  - At `f0e0ffe1`, "Needs a closer look" with no findings. Its "previously missed" item, fixed in `4dcb4221`: a 200 catalog of `{"models": []}` passed, though `adoptCatalog` adopts only `schema_version === 1` and `kind === "workbench-model-catalog"`, and shows any other catalog as unreadable. The envelope is now a named check, and the served bundle must name the kind. Seven refused envelopes are tested, and relaxing the int check fails the `true` case.
  - At `4dcb4221`, "Needs a closer look" with no findings.
  - At `1c0ff975`, fixed in `f29b4ddd`: `r4173473346`, the envelope check refused `"schema_version": 1.0` and `1e0`, which JavaScript's `=== 1` adopts. It now admits any JSON number equal to 1, and never `true` or `"1"`.
  - At `f29b4ddd` ("Changes recommended"), fixed in `de8b2274`:
    - `r4173769822`: a module served 200 as `text/plain` or `text/html` passed;
    - `r4173769844`: so did a stylesheet.

    Both now require their type.
  - At `d53a7378` ("Changes recommended"), fixed in `5636eb8d`:
    - `r4173842763`: a forward holding a backslash or user information passed, though a browser opens another host;
    - `r4173842794`: a refused destination's message quoted a URL that could hold the token;
    - `r4173842805` and `r4173842811`: an unparseable printed location or refresh URL raised a harness error instead of a named failure.
  - At `5636eb8d` ("Changes recommended"), fixed in `486e426e`:
    - `r4173894317`: a refresh whose delay a browser aborts (`invalid;url=`, `-1;url=`) still yielded a token;
    - `r4173894352`: a token that `takeDeliveredConsoleToken` discards (`short`) passed.
  - At `486e426e`, "Needs a closer look" with no findings. Its "previously missed" item, fixed in `142d1352`: with an agreeing record, a forward to `/missing.html` or `/snapshot.json` passed. The forward must now open `/` or `/index.html`.
  - At `142d1352` ("Changes recommended"), fixed in `27479495`:
    - `r4174355680`: a percent-encoded copy of the token in the query passed;
    - previously missed: a forward to `[::1]` passed, though the plane listens on 127.0.0.1 alone;
    - previously missed: an import from `https://…` was fetched as its local path and passed;
    - previously missed: any stop exit status passed.
  - At `27479495` ("Changes recommended"), fixed in `64dc06f5`:
    - `r4174411680`: a bare specifier (`import "child.js"`) passed when `/child.js` was served;
    - previously missed: a FIFO opener hung the harness under `--keep-going`.
  - At `33841d4a` ("Changes recommended"), fixed in `4809b3d2`:
    - `r4174621486`: a start or route failure quoted the tail of the entry point's output, which may hold the token;
    - `r4174621535`: `documents: [{}]` passed, while every document's source reads were skipped.
  - At `4809b3d2` ("Changes recommended"), fixed in `32fbb6dc`:
    - `r4174671390`: a refresh inside `<template>` or `<noscript>` counted as the opener's forward. Only a live refresh counts now, as a browser that runs scripts parses the page;
    - `r4174671426`: the privacy check stopped at `console/`. The opener's whole path is now judged by T104's own rules, and `prepare()` refuses an unsafe `TMPDIR`;
    - and, from the holder's brief, no line the harness prints quotes a token (`Verdict.redact`).
  - At `ec95f451` ("Changes recommended"), fixed in `4d0e6d10`:
    - `r4175016672`: a token nested in `/capabilities` was echoed by the install-mode reason. No step-5 reason quotes a payload now, a published token is kept secret at any depth, and the opener's tokens are learned before step 5;
    - `r4175016692`: a body nested past the JSON parser's depth was a harness ERROR (exit 2). It is a named failure now.
  - At `82869769` ("Changes recommended"), fixed in `2dcb98d3`:
    - `r4177924060`: an opener past 64 KiB was judged by its truncated prefix. It is read whole or refused;
    - `r4177924097`: the catalog's reasons quoted its body, where a `\u`-escaped token passed the literal redaction. No reason quotes the server's bytes now;
    - `r4177924129`: same-origin was `http://loopback`, so an absolute same-origin import was refused. It is the running server's origin now.
  - At `2dcb98d3` ("Changes recommended"), fixed in `4bdb41fb`:
    - `r4178069345`: `json.loads` admitted `NaN` and `Infinity`, which `response.json()` refuses;
    - `r4178069374`: a backslash URL resolved to a local file, where a browser goes elsewhere;
    - and, on the holder's word, one adversarial pass over every parser, closing the class: JSON decoding, JavaScript escapes, the page's links, `<base>` and import maps, user information, encoded dot segments, request-target encoding, unterminated character references, and availability as the rail reads it.
  - At `4bdb41fb` ("Changes recommended"), fixed in `ba216f84`:
    - `r4178395669`: every `<script src>` was a module root, whatever its type, and inside a `<template>` too. Only a live module script is an entry now; a classic or inline script is refused by name, and a page with no entry fails;
    - `r4178395690`: `\uD83D\uDE00` left two surrogates that no codec encodes. Every string from JavaScript or JSON is read in Unicode scalar values now;
    - and the pass that closed each class: markup the harness does not model, non-UTF-8 pages, escapes a module refuses, redirects, `Content-Type` parsing, and printed lines a server could forge.
  - At `ba216f84` ("Changes recommended"), fixed in `9229c659`:
    - `r4178913887`: U+FEFF after `import` hid the import, as Python's `\s` lacks it. The lexer reads JavaScript's own whitespace now, and every run as one space, so no run or comment hides an import either;
    - `r4178913911`: the catalog's reasons quoted its envelope values and available ids, where a percent-encoded token passed the redaction. No reason quotes a value the product sent now, and the redaction sees through percent-encoding;
    - `r4178913926`: an encoded dot inside a file name (`child%2Ejs`) was refused as a dot segment. Only a whole segment is one now.
  - At `9229c659`, "Needs a closer look" with no findings. Its overview, fixed in `3392f934`: a line comment ended only at LF, so one ended by CR, U+2028 or U+2029 hid the next line's import. A hashbang line is read as a comment too.
  - At `3392f934` ("Changes recommended"), fixed in `0717f72f`:
    - `r4179115282`: a template's `${…}` was skipped by counting braces, so an import or a route inside one was never seen. It is read as code now;
    - `r4179115311`: only `/` was checked and walked, though the opener opens `/index.html`. The page the forward opens is asked of the forward's own host, with its query, must be HTML and UTF-8, and is the page whose bundle, routes and catalog are read.
  - At `0717f72f` ("Changes recommended"), fixed in `b7b9b843`:
    - `r4179220628`: a `${` left the lexer after an expression, so a `/` opening a regular expression read as division, and the expression's `}` closed the interpolation early. An interpolation starts an expression now;
    - `r4179220641`: `html.unescape` read `&copy=2` in a page's `src` or `href`, which a browser keeps as text. Such a tag is refused by name, as the opener's refresh already was.
  - At `b7b9b843` ("Changes recommended"), fixed in `d50e8cef`:
    - `r4179348386`: after a postfix `++` or `--` (`n++ / 2`), a `/` was read as a regular expression, which swallowed the next import. The lexer now reads what ends before a `/`: a postfix operator, a call or a group divides, and a prefix operator or the `)` of an `if`, `while`, `for` or `with` condition opens a regular expression;
    - `r4179348414`: an accepted `<base href="/">` was ignored, so `src="?m=1"` on `/index.html` was asked as `/index.html?m=1`, not `/?m=1`. The page's scripts and stylesheets resolve against its base URL now.
  - At `d50e8cef` ("Changes recommended"), fixed in `04579fcd`: `r4180454790`, a `/` after a `}` that ends an expression (`{} / 2`, a function or class expression) divides, and was read as a regular expression. A lexer cannot tell such a `}` from a block's, so a module with a `/` right after a `}` is refused by name now (`[bundle.ambiguous-slash <path>]`). The real bundle has none (the lexer decides 213 `/` there, none after a `}`). `ff04e015` then merged `main` `32943cbf` (T104).
  - At `ff04e015` ("Changes recommended"), fixed in `1d581541`: `r4180554323`, a member named as a keyword (`obj.return / 2`, `obj.if(1) / 2`, `obj.of++ / 2`) was read as the keyword, so each `/` hid the import after it. A member's name is never a keyword now. The self-pass after it, in `ea4f7838`: the last `.` of a spread (`[...typeof /re/]`) and a decimal literal's own point (`1. in /re/`) are no member access, so the keyword after either is still one; and the last name is read whole, so `ñreturn` and `this.#return` are names.
  - At `ea4f7838`, "Needs a closer look" with no findings. Its overview named "a verified lexer error" that "can hide missing static dependencies". The pass that followed, in `bd50f405` (with a case in `234229d7` for a mutant that survived), found four, each hiding an import: `of` read as a keyword outside a `for` head (`const of = 4; of / 2`); no condition opened by `for await (`; a regular expression after a spread's `...` read as a division; and `default`, `extends`, and `break`, `continue` and `debugger` before a line break, after which an expression or a statement starts.
  - At `234229d7` ("Changes recommended"), fixed in `09d9d632`: `r4180899047`, `of` was a keyword anywhere in a `for` head, so in a classic loop's initializer, condition or update (`let of = 4; for (; of / 2; ) break;`) the `/` opened a regular expression that swallowed the import after it. `of` is a keyword only as the head's separator now, right after its binding or target. The self-pass after it, in `e4f81d48`, is a differential check of the lexer against node's own module parser, run locally (the static module requests of a `vm.SourceTextModule`). It covers 196 contexts, each a regular expression or a division before a static import, with five kinds of separator. Node accepts 978 of the snippets as modules. The lexer hid the import in 195 of them at `ff04e015`, and in 85 at `09d9d632`. It now hides it in 10, all one form: a declaration list's last binding with no initializer, then a line break and a regular expression (`let x, y` [line break] `/re/`), which would take a parser. It has no false failure, and the same 10 are refused by name throughout. The 85 were a regular expression after a division's own `/`, and after a line break that ends a statement by automatic semicolon insertion: before a `++` or `--`, after a labelled `break` or `continue`, a declaration's lone binding, or a static import's specifier.
  - At `e4f81d48`, "Needs a closer look" with no findings. Its overview named "URL-resolution and identifier-parsing errors". Under the holder's convergence ruling (see "Accepted limits"), the identifier class was a one-line fix with a test, made in `d698c6ea`: a ZWNJ, a ZWJ, a combining mark or another name character that Python's `\w` lacks split a name. The URL class is an accepted limit: it appears in contrived references only, and all 84 shipped specifiers resolve as the URL standard resolves them.
  - At `d698c6ea`, "Needs a closer look" with no findings. The overview says the harness's "custom browser parsing and database-process lifecycle checks warrant final integrated human verification", and names no form. It was the last push of the review rounds.
  - `d29e68ca` merges `main` `dede32b4` for the T095 refresh. The merge is clean (main and this branch share no file) and changes nothing of this PR's own. At `d29e68ca`: "Needs a closer look" with no findings and no thread. Its overview repeats `d698c6ea`'s: the browser modeling and the database-process lifecycle checks "warrant human sign-off despite reported green CI". It names no form, so there is nothing to classify.

## Plan changes this PR carries

- **T007 batch N** (openxFactory `bdd0f586`, RULED `5963851934`): the raw `/capabilities` payload carries the console token neither by name, at any depth, nor by value, as quickstart § 3 asserts it; `console/` is 0700 exactly (`eb81b3d8`). The id CI's by-design failure names, `[a.capabilities carries no console token]`, is the by-name check. Product mutant m12 (the token under another key) is killed by the by-value check.
- **The holder's T096 dry run, F4, after openDox-code#85** (the T102 follow-on, holder ruling F1 (i)): a standalone plane's rail sends no thread read, so the harness asks none with a query, and asserts the condition, no branch-session column (`4d0e6d10`).

## Not done here

- No ruleset change: making `acceptance` required is the owner's.
- No fix to any other writer's PR; gaps found are reported.
- No README edit: the openDox root's README is T076's (openDox#17).

🤖 Generated with [Claude Code](https://claude.com/claude-code)


Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants