unaltraweb 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33193d7900939e3cf2e4e9769e8fd9443e0a7242eaa73aef1d93b8ffcee6e79b
4
- data.tar.gz: 3ef63e4f55c4b369e0926be413eb2da4d3702f49ac733d84dedc952bb1ec563c
3
+ metadata.gz: 2ed24da568933de15be12350ea7b867a6aa7e14abac71ec303755f5b808e1837
4
+ data.tar.gz: a4176d255c14de252144d7669db6479bc43ee033342332cd596870eea1ddf71b
5
5
  SHA512:
6
- metadata.gz: eb799646f841a4444d315aa4a71d161ec83acacbef75f36fe00b8db7242699be40147c2829bedee18f249f3085b51328cd18a902f317196d518b4f2b8e11a9f2
7
- data.tar.gz: cfe5a103146718d7bbae56c9821b2dabdc0fdf1bcde966779ee41ea96c468bd795446202844a56a0974919ce4bca33e2d90e5c6de98e8e95936b1335c44dca86
6
+ metadata.gz: 768943c9b012a7e165ab9136d364783203c35caaaae62c2715f95361107f63377dbac30e45252ebeff78b0707d71dd14eb0481cecca18f52b15688c82f4c8c10
7
+ data.tar.gz: 49edeb75312f6d45e53db50a55a65f25ddb81f7ad8cc8438c9568ada3c9d699c9c00f0a329ea9b8cc2fb5812b5e48acd254e0e967b659d16650a0ea8612dad16
data/Makefile CHANGED
@@ -1,9 +1,9 @@
1
1
  PYTHON ?= python3
2
2
  override PROJECT := $${MCP_CONSUMER_WORKSPACE:?MCP_CONSUMER_WORKSPACE is required}
3
3
  override PROJECT_ROOT := $(PROJECT)
4
- MCP_RUNTIME_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.6.0
5
- MCP_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.6.0
6
- MCP_RELEASE_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:908b4ce54c7bdf355e14ed55b31ed4b9baae319e211af90004a680d1d1cb8692
4
+ MCP_RUNTIME_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.7.0
5
+ MCP_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.7.0
6
+ MCP_RELEASE_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:736c4ddd0a543454e3edaeac2e9cfd97a1279ce472923ac54e326c1281b2ba05
7
7
  MCP_DOCKER_BUILD_NETWORK ?= default
8
8
  INIT_SITE_PROFILE ?= unaltreselfie
9
9
  NEW_WEB_PROFILE ?= unaltreselfie
@@ -40,7 +40,7 @@ WEB_CAPTURE_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-web-capture@sha256:0bf1b
40
40
  WEB_CAPTURE_DEV_IMAGE ?= unaltraweb-web-capture:dev
41
41
  WEB_CAPTURE_DOCKER_BUILD_NETWORK ?= default
42
42
  VEGAVISUALS_CLI ?=
43
- DOCKER_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.6.0
43
+ DOCKER_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.7.0
44
44
  MANUAL_PDF_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-manual-pdf@sha256:0ba267cb87f53ebaca4e31805fe00610cd61fdf97a8d2c3692f4655700dceaed
45
45
  MANUAL_PDF_DEV_IMAGE ?= unaltraweb-manual-pdf:dev
46
46
  MCP_SMOKE_MANUAL_PDF_IMAGE ?= $(MANUAL_PDF_IMAGE)
@@ -71,7 +71,7 @@ export MANUAL_RELEASE_SELECTOR MANUAL_PDF_LANG MANUAL_PDF_PUBLISH_DRY_RUN MANUAL
71
71
  UNALTRAWEB_WORKER_ROLE ?=
72
72
  UNALTRAWEB_WORKER_PROJECT ?=
73
73
  UNALTRAWEB_WORKER_TOKEN ?=
74
- WORKER_LABEL_ARGS = $(if $(strip $(UNALTRAWEB_WORKER_TOKEN)),--label "io.context.mcp-factory=unaltraweb" --label "io.context.mcp-role=$(UNALTRAWEB_WORKER_ROLE)" --label "io.context.mcp-project=$(UNALTRAWEB_WORKER_PROJECT)" --label "io.context.mcp-worker-token=$(UNALTRAWEB_WORKER_TOKEN)",)
74
+ WORKER_LABEL_ARGS = $(if $(strip $(UNALTRAWEB_WORKER_TOKEN)),--label "io.context.mcp-factory=unaltraweb" --label "io.context.mcp-role=$(UNALTRAWEB_WORKER_ROLE)" --label "io.context.mcp-project=$(UNALTRAWEB_WORKER_PROJECT)" --label "io.context.mcp-worker-token=$(UNALTRAWEB_WORKER_TOKEN)",) $(if $(strip $(UNALTRAWEB_RUNTIME_SESSION)),--label "io.context.mcp-session=$(UNALTRAWEB_RUNTIME_SESSION)",)
75
75
  DOCS_CONTAINER ?= unaltraweb-docs-local
76
76
  DOCS_HOST ?= 0.0.0.0
77
77
  DOCS_PORT ?= 4000
@@ -295,17 +295,18 @@ manual-compute-render: ## Execute sources and atomically publish Markdown and fi
295
295
  python_image="$(COMPUTE_PYTHON_IMAGE)"; r_image="$(COMPUTE_R_IMAGE)"; \
296
296
  if test "$$engine" = "r"; then r_image="$$image"; else python_image="$$image"; fi; \
297
297
  if ! docker image inspect "$$image" >/dev/null 2>&1; then \
298
+ if test "$${UNALTRAWEB_MANAGED_RUNTIME:-0}" = 1; then printf '%s\n' 'Selected computation image is not prepared; managed execution never builds or pulls' >&2; exit 1; fi; \
298
299
  COMPUTE_PYTHON_IMAGE="$(COMPUTE_PYTHON_IMAGE)" COMPUTE_R_IMAGE="$(COMPUTE_R_IMAGE)" COMPUTE_DOCKER_BUILD_NETWORK="$(COMPUTE_DOCKER_BUILD_NETWORK)" $(PYTHON) "$(COMPUTE_SCRIPT)" image --project "$(PROJECT_ROOT)" --engine "$$engine" >/dev/null; \
299
300
  fi; \
300
301
  identity=$$(docker image inspect "$$image" --format '{{.Id}}'); \
301
302
  digest=$$(docker image inspect "$$image" --format '{{join .RepoDigests ","}}'); \
302
303
  set --; if test -n "$(UNALTRAWEB_WORKER_TOKEN)"; then cidfile="$$runtime_dir/worker-$(UNALTRAWEB_WORKER_TOKEN)-$$engine.cid"; rm -f "$$cidfile"; cidfiles="$$cidfiles $$cidfile"; set -- --cidfile "$$cidfile"; fi; \
303
- docker run --rm "$$@" $(WORKER_LABEL_ARGS) --user "$(LOCAL_UID):$(LOCAL_GID)" --network none --read-only --cap-drop ALL --security-opt no-new-privileges --pids-limit "$(COMPUTE_PIDS_LIMIT)" --cpus "$(COMPUTE_CPUS)" --memory "$(COMPUTE_MEMORY)" --tmpfs /tmp:rw,noexec,nosuid,size=1g \
304
+ docker run --rm --pull never "$$@" $(WORKER_LABEL_ARGS) --user "$(LOCAL_UID):$(LOCAL_GID)" --network none --read-only --cap-drop ALL --security-opt no-new-privileges --pids-limit "$(COMPUTE_PIDS_LIMIT)" --cpus "$(COMPUTE_CPUS)" --memory "$(COMPUTE_MEMORY)" --tmpfs /tmp:rw,noexec,nosuid,size=1g \
304
305
  -e HOME=/tmp -e COMPUTE_PYTHON_IMAGE="$$python_image" -e COMPUTE_R_IMAGE="$$r_image" \
305
306
  -e UNALTRAWEB_COMPUTE_IMAGE_ID="$$identity" -e UNALTRAWEB_COMPUTE_IMAGE_DIGEST="$$digest" \
306
307
  --mount "$$project_mount" \
307
308
  --mount "$$runtime_mount" \
308
- -w /project --entrypoint python3 "$$image" \
309
+ -w /project --entrypoint python3 "$$identity" \
309
310
  /opt/unaltraweb/computations/render.py render --project /project --engine "$$engine" $(if $(strip $(COMPUTE_SOURCE)),--source "$(COMPUTE_SOURCE)",) $(if $(filter 1 true TRUE yes YES y Y,$(COMPUTE_CONFIRM_OVERWRITE)),--confirm-overwrite,) $(if $(filter 1 true TRUE yes YES y Y,$(COMPUTE_STALE_ONLY)),--stale-only,) $(if $(strip $(COMPUTE_MODE)),--mode "$(COMPUTE_MODE)",) >> "$$results"; \
310
311
  done; \
311
312
  if test -z "$(strip $(COMPUTE_SOURCE))"; then $(PYTHON) "$(COMPUTE_SCRIPT)" prune --project "$(PROJECT_ROOT)" >/dev/null; fi; \
@@ -382,13 +383,14 @@ define run_manual_pdf_worker
382
383
  if test -n "$(UNALTRAWEB_WORKER_TOKEN)"; then runtime_dir="$(PROJECT_ROOT)/tmp/.unaltraweb/manual-pdf"; mkdir -p "$$runtime_dir"; cidfile="$$runtime_dir/worker-$(UNALTRAWEB_WORKER_TOKEN).cid"; rm -f "$$cidfile"; set -- --cidfile "$$cidfile"; fi; \
383
384
  mount=$$(/bin/sh "$(DOCKER_MOUNT_SCRIPT)" "$(PROJECT_ROOT)" /project); \
384
385
  trap 'test -z "$$cidfile" || rm -f "$$cidfile"' EXIT; \
385
- docker run --rm "$$@" $(WORKER_LABEL_ARGS) --user "$(LOCAL_UID):$(LOCAL_GID)" -e HOME=/tmp -e "UNALTRAWEB_MANUAL_RELEASE_SELECTOR=$${MANUAL_RELEASE_SELECTOR}" --mount "$$mount" -w /project "$(MANUAL_PDF_IMAGE)" $(1) --project /project $(if $(strip $(MANUAL_PDF_LANG)),--language "$(MANUAL_PDF_LANG)",) $(2)
386
+ identity=$$(docker image inspect "$(MANUAL_PDF_IMAGE)" --format '{{.Id}}'); \
387
+ docker run --rm --pull never --network none --cpus 2 --memory 2g --pids-limit 256 "$$@" $(WORKER_LABEL_ARGS) --user "$(LOCAL_UID):$(LOCAL_GID)" -e HOME=/tmp -e "UNALTRAWEB_MANUAL_RELEASE_SELECTOR=$${MANUAL_RELEASE_SELECTOR}" --mount "$$mount" -w /project "$$identity" $(1) --project /project $(if $(strip $(MANUAL_PDF_LANG)),--language "$(MANUAL_PDF_LANG)",) $(2)
386
388
  endef
387
389
 
388
390
  manual-pdf-preflight: ## Run required PDF gates without contaminating the worker JSON stream
389
391
  @$(MAKE) --silent --no-print-directory manual-compute-check >/dev/null
390
392
  @$(MAKE) --silent --no-print-directory web-capture-check >/dev/null
391
- @docker image inspect "$(MANUAL_PDF_IMAGE)" >/dev/null 2>&1 || docker pull "$(MANUAL_PDF_IMAGE)" >/dev/null
393
+ @docker image inspect "$(MANUAL_PDF_IMAGE)" >/dev/null 2>&1 || { if test "$${UNALTRAWEB_MANAGED_RUNTIME:-0}" = 1; then printf '%s\n' 'Selected PDF image is not prepared; managed execution never pulls' >&2; exit 1; fi; docker pull "$(MANUAL_PDF_IMAGE)" >/dev/null; }
392
394
 
393
395
  manual-pdf-status: ## Inspect manual PDF configuration and artefacts without Docker, network, or writes
394
396
  @UNALTRAWEB_MANUAL_RELEASE_SELECTOR="$${MANUAL_RELEASE_SELECTOR}" $(PYTHON) "$(CURDIR)/scripts/manual/build_pdf.py" status --project "$(PROJECT_ROOT)" $(if $(strip $(MANUAL_PDF_LANG)),--language "$(MANUAL_PDF_LANG)",)
@@ -25,12 +25,25 @@ The template is the better place to validate gem consumption, centralized styles
25
25
 
26
26
  ## Component Contract
27
27
 
28
- ### Pending 0.6.0 retained-letter increment
29
-
30
- The source branch prepares a separate 0.6.0 identity for native
31
- [retained-letter import]({{ '/retained-documents/' | relative_url }}).
32
- Gem, wheel, base runtime and MCP are authorized as `ready` for final same-source
33
- candidates. The consumer core selects reviewed integration
28
+ ### 0.7.0 D0 runtime increment
29
+
30
+ The next containing runtime adds [live instance identity and session-scoped
31
+ resource control]({{ '/live-runtime/' | relative_url }}). Preparation is explicit,
32
+ launch is pull-never, and startup identity remains distinct from current disk
33
+ metadata. The owner-tested rendering closure selects Diavisuals 0.5.0 and Vega
34
+ 0.5.1 with exact artifacts; old 0.4.0 static receipts remain integrity-checked.
35
+ Diavisuals' separate live-process identity correction still gates the full
36
+ managed coordinator closure. Publication and activation evidence belong to the
37
+ [D0 owner record](https://github.com/dosquartsdedocs/unaltraweb/blob/main/docs/agents/d0-runtime-identity.md).
38
+
39
+ ### Published 0.6.0 release
40
+
41
+ [**0.6.0**](https://github.com/dosquartsdedocs/unaltraweb/releases/tag/v0.6.0)
42
+ delivers native [retained-letter import]({{ '/retained-documents/' | relative_url }}).
43
+ Gem, wheel, base runtime and MCP were published from
44
+ `de52db351490e94939aac20c6af264f22a6a1678`; receipt-only integration and tag target
45
+ `ec6dcb28e890d32718aa173934527a7b4213a042` bind their verified bytes.
46
+ The consumer core selects reviewed integration
34
47
  `5cf9817489c8dc47cee726bfe42fe3071cd32b85`; the PDF 0.6.0 worker is already
35
48
  published, signed and tested at immutable digest
36
49
  `sha256:0ba267cb87f53ebaca4e31805fe00610cd61fdf97a8d2c3692f4655700dceaed`.
@@ -38,12 +51,14 @@ The PDF worker changes to include complete retained documents and the same
38
51
  integrity checkers used by its controller. Computation, capture and visual
39
52
  companions retain their tested 0.4.0 selections.
40
53
 
41
- The checkout's active launcher remains pinned to the published 0.5.1 MCP digest.
42
- Its receipt and package bytes remain unchanged. Local development images and an
43
- installed wheel exercise the new import without claiming a public 0.6.0 release
44
- or changing real consumer sites. The
45
- [owner acceptance record](https://github.com/dosquartsdedocs/unaltraweb/blob/main/docs/agents/retained-letter-import.md)
46
- records the authenticated Carta packet, receiver mapping and relocation proof.
54
+ The post-release checkout launcher selects published MCP digest
55
+ `sha256:736c4ddd0a543454e3edaeac2e9cfd97a1279ce472923ac54e326c1281b2ba05`.
56
+ Signed candidates, protected CI, strict receipt/ancestry gates, registry promotion
57
+ and Trusted Publishing passed. Anonymous downloads match the receipt. The
58
+ [delivery report](https://github.com/dosquartsdedocs/unaltraweb/blob/main/docs/agents/owner-closeout-80.md)
59
+ records the installed Carta relocation proof and four-profile populated-cache
60
+ roundtrip against those exact bytes. Existing clients keep their loaded image
61
+ until reconnecting; real consumer adoption remains explicit.
47
62
 
48
63
  ### Published 0.5.1 release
49
64
 
@@ -53,7 +68,7 @@ state. Source `aab5a7b030cd711b40770a3f44ad3a356eab90da` produced the signed
53
68
  images and verified gem/wheel. Receipt/tag commit
54
69
  `e842c733c8a274279dd5efdc3618e20669f29037` changes only the receipt from that
55
70
  first parent. Strict release/publication gates passed before promotion, and
56
- anonymous registry downloads match the receipt. The checkout launcher now selects
71
+ anonymous registry downloads match the receipt. That release's launcher selected
57
72
  `ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:908b4ce54c7bdf355e14ed55b31ed4b9baae319e211af90004a680d1d1cb8692`.
58
73
 
59
74
  The four-profile populated-cache roundtrip also passed against the actual signed
@@ -91,25 +106,25 @@ candidate, receipt, tag and package evidence.
91
106
 
92
107
  The BOM is an interoperability contract, not a bundle. The wheel contains its Python control/inspection modules, schema/BOM, clean package-owned scaffolds and a small host Docker launcher, described below. It does not include Ruby theme assets, Docker image layers, the factory's build/worker implementations, TeX, Chromium, computation environments, `diavisuals`, or `vegavisuals`.
93
108
 
94
- The selected public core release is `0.5.1`; earlier distributions remain immutable. Its BOM reuses immutable PDF, compute and web-capture worker digests and selects published `diavisuals v0.4.0` and `vegavisuals v0.4.0` through SHA-256-pinned wheel URLs. Companion references can describe either a provider/release-matching Git reference or a provider/release/version-matching wheel with its content hash. The wheel boundary remains external. The scaffold's Vega revision is the published `68c0b231402ae9485cc34ce530dc5239cb0ec194` commit. `distribution-check` validates structural integrity for normal CI. `distribution-release-check` blocks coordinated publication while any component is `pending` or `unavailable`; reviewed source authorized to produce final same-commit candidates is `ready`, while an already-published component is `released`. Final receipt values remain immutable after publication.
109
+ The selected public core release is `0.6.0`; earlier distributions remain immutable. Its BOM reuses immutable PDF, compute and web-capture worker digests and selects published `diavisuals v0.4.0` and `vegavisuals v0.4.0` through SHA-256-pinned wheel URLs. Companion references can describe either a provider/release-matching Git reference or a provider/release/version-matching wheel with its content hash. The wheel boundary remains external. The scaffold's Vega revision is the published `68c0b231402ae9485cc34ce530dc5239cb0ec194` commit. `distribution-check` validates structural integrity for normal CI. `distribution-release-check` blocks coordinated publication while any component is `pending` or `unavailable`; reviewed source authorized to produce final same-commit candidates is `ready`, while an already-published component is `released`. Final receipt values remain immutable after publication.
95
110
 
96
111
  ## Docker-First Hybrid Policy
97
112
 
98
- ### 0.5.1 component selection
113
+ ### 0.6.0 component selection
99
114
 
100
- The coordinated core identity is **0.5.1**: gem, wheel, base runtime and MCP
115
+ The coordinated core identity is **0.6.0**: gem, wheel, base runtime and MCP
101
116
  images were published together. The installed host launcher and runtime-aware
102
- generated Bundler state are included. The unchanged PDF worker retains its
103
- published 0.5.0 digest, alongside the 0.4.0 computation/capture workers and visual
117
+ generated Bundler state are included. The PDF worker uses its reviewed, signed
118
+ 0.6.0 digest, alongside the 0.4.0 computation/capture workers and visual
104
119
  companions. A released PDF can be reused only by full digest; new/pending workers
105
120
  still require the coordinated version. The historical 0.5.0 receipt remains
106
- unchanged. Signed image/package evidence is recorded in the 0.5.1 receipt;
121
+ unchanged. Signed image/package evidence is recorded in the 0.6.0 receipt;
107
122
  promotion and anonymous-download checks passed before the post-release launcher
108
123
  pin was advanced.
109
124
 
110
125
  ### Local delivery
111
126
 
112
- GHCR is the canonical delivery channel for normal local use. The released package scaffold selects `ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.5.1`; existing sites can remain on their immutable previous release until deliberately updated. Its `make build`, `make serve` and `make test` targets mount the thin child site and run inside that image. The image contains both the installed Python control plane and the reviewed factory source at `/opt/unaltraweb`, so those targets load the theme as a path gem without downloading PyPI or RubyGems packages.
127
+ GHCR is the canonical delivery channel for normal local use. The released package scaffold selects `ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.6.0`; existing sites can remain on their immutable previous release until deliberately updated. Its `make build`, `make serve` and `make test` targets mount the thin child site and run inside that image. The image contains both the installed Python control plane and the reviewed factory source at `/opt/unaltraweb`, so those targets load the theme as a path gem without downloading PyPI or RubyGems packages.
113
128
 
114
129
  Factory registration uses a stricter pin. gContExt runs `mcp-build`, which inspects or pulls the full `MCP_RELEASE_IMAGE` digest, and `mcp-stdio` launches that exact image. Checkout builds use local `:dev` names by default through `mcp-image`, `mcp-check` and `mcp-smoke`, so they do not shadow public semver references unless a maintainer explicitly overrides them. The digest is advanced in a separate post-release change after each new receipt exists; candidate source continues to select the last completed release instead of attempting to embed an unknown self-digest.
115
130
 
@@ -139,7 +154,7 @@ unaltraweb-mcp-docker serve --project /absolute/consumer
139
154
  ```
140
155
 
141
156
  The installed default selects its package's full MCP release image, currently
142
- `ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.5.1`. It resolves that selection to the
157
+ `ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.6.0`. It resolves that selection to the
143
158
  local immutable image ID before executing. The checkout's `MCP_RELEASE_IMAGE`
144
159
  is a separate digest pin advanced only after publication; it is not the installed
145
160
  wheel's default. This avoids sending a new package back to an older MCP while
@@ -284,7 +299,7 @@ For that reason:
284
299
 
285
300
  ## Docker Runtime
286
301
 
287
- Release `0.5.1` publishes the selected base/MCP runtime and packages while pinning the verified PDF worker and reusing unchanged specialized workers. Local maintainers continue to use explicit development names such as `unaltraweb:dev`; generated sites select the reviewed semver MCP image rather than `main` or `latest`.
302
+ Release `0.6.0` publishes the selected base/MCP runtime and packages while pinning the verified PDF worker and reusing unchanged specialized workers. Local maintainers continue to use explicit development names such as `unaltraweb:dev`; generated sites select the reviewed semver MCP image rather than `main` or `latest`.
288
303
 
289
304
  The base runtime owns Ruby, Jekyll and system dependencies. The MCP image builds on its exact candidate digest and adds the full reviewed factory plus the Python package. Specialized workers remain separate. This keeps each layer focused without adding Chromium, TeX or computation stacks to every site; the coordinated core-image workflow still rebuilds and verifies runtime, MCP and manual PDF candidates together.
290
305
 
@@ -19,7 +19,7 @@ provenance needed to inspect its origin. The importer keeps that complete unit i
19
19
  the receiving repository and creates a native page or chapter referencing the PDF.
20
20
  The website displays the original; a manual PDF includes all its pages.
21
21
 
22
- This capability belongs to the **0.6.0 development increment**. It requires a
22
+ This capability ships in **0.6.0**. It requires a
23
23
  containing Python package and Jekyll core, plus the matching PDF worker for manual
24
24
  output. The published 0.5.1 runtime predates it. Changing the global MCP selection
25
25
  does not update a site's Gemfile, Makefile or PDF-worker selection.
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: Inspect And Close A Live MCP Session
3
+ description: Startup identity, exact prepared images and session-scoped resource release.
4
+ lang: en
5
+ ref: live_runtime_identity
6
+ profiles:
7
+ - unaltredocs
8
+ documentation_profiles:
9
+ - local-authors
10
+ - core-developers
11
+ section: Core Development
12
+ weight: 635
13
+ permalink: /live-runtime/
14
+ nav_title: Live Runtime
15
+ ---
16
+
17
+ The 0.7.0 runtime increment distinguishes the process serving a connection from
18
+ the installed package, source checkout and rendering workers. Call
19
+ `runtime_identity` or read `web://runtime-identity` through that live MCP
20
+ connection. Both report the same backend instance and startup information.
21
+
22
+ The observation includes the loaded package version, startup code fingerprint,
23
+ current disk fingerprint, interpreter, PID/start information, workspace mapping
24
+ and selected versus observed image identities. A disk edit is reported as drift;
25
+ it does not upgrade an already-running process. Docker-host and container PID
26
+ namespaces are reported separately. Identity checks read engine/runtime metadata,
27
+ not website prose, private records or arbitrary environment variables.
28
+
29
+ ## Prepare Explicitly, Then Launch
30
+
31
+ Use `unaltraweb-mcp-docker prepare --image REF` to acquire a selected release.
32
+ Launch, check, smoke and identity observations do not build or pull missing images.
33
+ For a managed launch, supply a full image ID or repository digest, or bind a local
34
+ reference to its independently verified configuration ID:
35
+
36
+ ```bash
37
+ unaltraweb-mcp-docker serve --project /absolute/site \
38
+ --image "$PREPARED_REFERENCE" --expected-image-id "$EXPECTED_IMAGE_ID" --managed
39
+ ```
40
+
41
+ The expected value is Docker's full `sha256:` configuration ID, not the archive
42
+ checksum or OCI index digest. A missing or mismatched selection is an error.
43
+ Execution uses that inspected ID with `--pull never`; existing aliases are not
44
+ rewritten. Add `--offline` for a controller without networking. The declared
45
+ Docker socket remains available for local inspection and prepared workers.
46
+
47
+ The normal host launcher mounts the canonical site path and a `/workspace` alias.
48
+ A launcher itself running in a container must supply its explicit daemon-host
49
+ mapping using `--host-project`. Unsupported direct controller mappings require
50
+ inspection rather than guessing a host path from `/workspace`.
51
+
52
+ ## Close One Connection Safely
53
+
54
+ 1. Retain the identity's consumer binding, session ID and exact container ID.
55
+ 2. Call `runtime_drain(confirm=true)`. The backend stops admitting new operations.
56
+ 3. Close that stdio connection. Bounded active work finishes before its idle
57
+ session resources are released.
58
+ 4. Use the installed `session-status` command to confirm resource termination.
59
+
60
+ ```bash
61
+ unaltraweb-mcp-docker session-status --project /absolute/site \
62
+ --session-id "$SESSION_ID" --container-id "$CONTAINER_ID"
63
+ ```
64
+
65
+ `connected`, `busy`, `orphaned` and `unknown` do not mean resources are free.
66
+ Only `stopped` with `resources_released=true` establishes observed absence.
67
+ After a crash, `reap-session` with the same retained tuple can recover eligible
68
+ idle leftovers. It preserves live backends, active jobs, other sessions, prepared
69
+ images, volumes and authored files. Disk cleanup remains explicit and separate.
70
+
71
+ The older `down` command is workspace-wide, and `mcp-down-all` is factory-wide.
72
+ Use them only for those intended scopes, not as a substitute for closing one
73
+ client. The managed stdio profile has one connection per backend; it does not
74
+ claim shared-HTTP backend reattachment semantics.
75
+
76
+ ## Workers And Helpers
77
+
78
+ Worker references are fixed at startup. Managed rendering requires those exact
79
+ images to be prepared; it refuses a fallback pull/build or a conflicting authored
80
+ computation-image choice. The identity reports resource bounds and image
81
+ observations without running a renderer.
82
+
83
+ Visual helpers are separate MCP processes. Their versions and immutable package
84
+ selections describe the accepted rendering closure, not their current live
85
+ instances. The composing controller must obtain each helper's own live identity.
86
+ The selected Diavisuals 0.5.0 still requires its separately tracked D0 identity
87
+ correction before the entire coordinator closure can be marked managed-ready.
88
+ Existing 0.4.0 static provider receipts remain subject to full integrity checks
89
+ and do not force regeneration of authored figures during an upgrade.
@@ -0,0 +1,200 @@
1
+ # D0 native runtime identity and lifecycle — issue 85
2
+
3
+ Requirements: gaContExt
4
+ `5634ea2e3e42122bb365992dfca9f248feb89dec`,
5
+ [NATIVE_RUNTIME_REQUIREMENTS.md](https://github.com/dosquartsdedocs/gacontext/blob/5634ea2e3e42122bb365992dfca9f248feb89dec/src/bash/mcp_factories/NATIVE_RUNTIME_REQUIREMENTS.md).
6
+ Owner issue: [85](https://github.com/dosquartsdedocs/unaltraweb/issues/85).
7
+ Coordination: [gaContExt 15](https://github.com/dosquartsdedocs/gacontext/issues/15).
8
+
9
+ ## Native API mapping
10
+
11
+ The containing 0.7.0 increment adds `runtime_identity` and
12
+ `web://runtime-identity` on the actual serving MCP instance. Both use one
13
+ startup-created object, not a helper CLI. It records a random backend instance,
14
+ process PID/start ticks/PID namespace, start time, interpreter, loaded package
15
+ version and startup package fingerprint. Current bounded package bytes and
16
+ metadata are separate observations; changing metadata cannot rename the loaded
17
+ version. Only engine code/contracts and allowlisted runtime selections are read,
18
+ not site content or arbitrary environment values.
19
+
20
+ The identity's component/profile/selection hashes bind the fixed requirements,
21
+ startup BOM, behavioral profile, worker selections and consumer mapping. The
22
+ package fingerprint is a startup code-content observation, not a wheel checksum
23
+ or a permanent process attestation. The live container's image ID, source label,
24
+ limits and mount mapping are inspected separately. Its daemon-host init PID is
25
+ not compared numerically with the inner serving PID. Session ID, exact container
26
+ ID, factory/role/project labels and mapping correlate those namespaces.
27
+
28
+ `runtime_drain(confirm=true)` freezes admission on that connection. Ordinary
29
+ tools execute in a bounded one-operation lane, leaving identity/drain responsive
30
+ while a worker runs. Closing stdio waits for active work and releases that
31
+ session's idle preview/capture resources. A new connection creates a new backend
32
+ instance. This profile is **one stdio connection per backend**, not a shared HTTP
33
+ service: shared-backend reattachment/idle leases are not claimed.
34
+
35
+ Host-side installed API:
36
+
37
+ ```text
38
+ unaltraweb-mcp-docker prepare --image REF [--expected-image-id SHA256_ID]
39
+ unaltraweb-mcp-docker serve --project ROOT --image REF --expected-image-id SHA256_ID --managed
40
+ unaltraweb-mcp-docker session-status --project ROOT --session-id SESSION --container-id CONTAINER
41
+ unaltraweb-mcp-docker reap-session --project ROOT --session-id SESSION --container-id CONTAINER
42
+ ```
43
+
44
+ Preparation may acquire the explicitly selected image. Launch/check/smoke never
45
+ build or pull; execution uses its inspected exact configuration ID with
46
+ `--pull never`. Managed mutable aliases require the expected full image ID.
47
+ Full local IDs and repository digests are immutable selections. Ordinary mutable
48
+ local selections without an expected ID remain an explicitly weaker mode.
49
+ Archive hashes, repository/index digests and Docker configuration IDs are distinct.
50
+
51
+ `--offline` disables controller networking; the explicitly mounted local Docker
52
+ socket still permits image/owned-container inspection. Preview HTTP probing and
53
+ intentional external metrics calls need their documented network access.
54
+ `--worker-images` accepts bounded JSON mapping supported worker names to exact
55
+ `reference` / `expected_image_id` pairs. Otherwise startup BOM/dedicated image
56
+ environment selections apply. Managed execution refuses missing/mismatched
57
+ workers and never substitutes a preparation build/pull. Authored computation
58
+ image choices must match the approved startup identity before execution.
59
+
60
+ The normal host launcher canonicalizes the consumer and binds it at its canonical
61
+ path and `/workspace`. A containerized launcher must supply `--host-project` (or
62
+ its explicit inherited daemon mapping); the child uses that verified canonical
63
+ mirror. Identity reports launcher-visible, daemon-host and effective paths.
64
+ Other direct controller layouts are not silently inferred. In particular,
65
+ [issue 31](https://github.com/dosquartsdedocs/unaltraweb/issues/31) remains a separate
66
+ boundary for arbitrary direct `/workspace` controllers and is not automatically
67
+ closed by this profile.
68
+
69
+ ## Resource ownership
70
+
71
+ Each launch has a 32-hex session ID and a uniquely named owned stdio container.
72
+ Previews are per-session for these backends. Worker invocation tokens are retained
73
+ and augmented with session labels. Cleanup validates full IDs and ownership and
74
+ confirms absence before claiming release. Image and volume removal is outside
75
+ these APIs.
76
+
77
+ After EOF or a crash, the caller uses the **retained** binding/session/container
78
+ tuple with `session-status`. A live backend is `connected`; a running one-shot
79
+ worker is `busy`; idle leftovers are `orphaned`; only proven absence is `stopped`
80
+ with `resources_released=true`. Unavailable/changed ownership is `unknown` and
81
+ does not authorize removal. `reap-session` preserves a live backend or running
82
+ job, and recovers only that retired session's idle resources. Occupied or foreign
83
+ networks fail closed. No fresh-helper PID is presented as the backend's PID.
84
+
85
+ The legacy `down` / `make mcp-down` is an explicit **workspace-wide** shutdown;
86
+ `mcp-down-all` is maintainer/factory-wide. Neither is the hub's per-client toggle
87
+ operation. The managed sequence is live drain, close this transport, inspect its
88
+ exact retained identity, and recover eligible orphans when necessary.
89
+
90
+ The profile reports controller limits (2 CPUs, 4 GiB, 512 PIDs), preview/PDF/capture
91
+ limits (2 CPUs, 2 GiB, 256 PIDs), and managed computation limits (4 CPUs, 8 GiB,
92
+ 512 PIDs). Existing per-invocation time bounds remain 1800 seconds for PDF/compute
93
+ and 900 for captures; requests may compose several bounded jobs. Per-backend
94
+ operation concurrency is one. A drain does not falsely claim immediate RAM/CPU
95
+ release while a job continues.
96
+
97
+ ## Accepted visual-helper points and remaining closure blocker
98
+
99
+ The candidate selects the published Diavisuals 0.5.0 and Vega 0.5.1 packages:
100
+
101
+ | Helper | Wheel SHA-256 | Renderer configuration ID |
102
+ | --- | --- | --- |
103
+ | Diavisuals 0.5.0 | `f52e20f20f1fb3a2418f07adb8d6a68a5567d0c321fb7c0ccc0f4db04917b96b` | `sha256:5a6887b372a0e1c386a7b54981d11ae1910215baeb6a12dfd0706b3e85ef0846` |
104
+ | Vega 0.5.1 | `5aba4f267a37179c520b3cd164e1cec62ac5561872aa33b069cce73442dc52b1` | `sha256:695125943d0fbc3aa7c877babb9a11a6501bbc7ac265b0975bb5657c60439d98` |
105
+
106
+ Native selector mapping is Diavisuals `--runtime-image` plus
107
+ `--runtime-expected-id`, and Vega `--renderer-image-id`. Published metadata,
108
+ wheel/acceptance hashes and locally prepared renderer IDs were verified. Actual
109
+ Mermaid, PlantUML, Vega-Lite/CSV and Vega rendering passed native receipt checks,
110
+ tamper refusal and consumer relocation without rewriting renderer aliases.
111
+ Retained evidence: `/tmp/opencode/unaltraweb-d0-85/helpers-proof/evidence.json`.
112
+
113
+ Previously accepted **0.4.0 static receipts remain valid** under the unchanged
114
+ input/output/request integrity rules. This is an exact legacy point, not a
115
+ continuous version interval or permission to overwrite edited outputs. New work
116
+ uses the selected helper points. No existing client or real consumer is migrated
117
+ by this source change.
118
+
119
+ The coordinator does not claim to observe another MCP's serving process. Its live
120
+ identity returns the helper selections and requires their independent connection
121
+ observations. **Diavisuals 0.5.0 still lacks the full live-instance identity**;
122
+ [Diavisuals 14](https://github.com/dosquartsdedocs/diavisuals/issues/14) is therefore
123
+ an external D0 managed-closure blocker. Rendering compatibility alone does not
124
+ close that gate or make the whole coordinator dependency graph managed-ready.
125
+
126
+ ## Acceptance and delivery state
127
+
128
+ `test/d0_runtime_smoke.py` exercises installed launchers, actual images, tool/resource
129
+ agreement, independent consumers, reconnect, running-container metadata drift,
130
+ busy drain, offline launch, wrong/missing image refusal, manual/web work and
131
+ 0.6.0 → containing 0.7.0 → 0.6.0 activation with retained authored/generated state.
132
+ The published 0.6.0 point accurately remains **partial** for live identity; no
133
+ fixture or relabelled package is counted as a second D0-capable release.
134
+ Backend-crash/orphan recovery is exercised separately from graceful disconnect.
135
+ The existing Carta and populated Bundler acceptance drivers remain release gates.
136
+
137
+ This is owner development evidence until reviewed source, containing release
138
+ receipts, installed signed-byte acceptance and remaining blockers are returned to
139
+ the central issue. Published 0.6.0 and its active checkout launcher pin remain
140
+ unchanged during candidate development. The worker/data choices and external
141
+ Diavisuals identity blocker must remain visible in the hub's adapter decision.
142
+
143
+ ## Retained local acceptance — 2026-10-05
144
+
145
+ The owner explicitly authorized the full 0.7.0 review/PR/integration, gated
146
+ publication and post-release pin flow. No other checkout is a write workspace.
147
+
148
+ The installed local wheel
149
+ `67689b76225511439175c82b6df033005d84e990b147b8fa4875f3adbd7d684f` and owned MCP
150
+ image `sha256:d8eafba61a2e1f02bc22b244610d8174879e04d9ea36b47a71e8cd7f993a54b1`
151
+ passed eight stages: reconnect/scope, loaded-versus-disk, busy drain, backend
152
+ crash recovery, offline launch, explicit daemon mapping, wrong worker and missing
153
+ worker. The crash record observed a real PDF job still **busy** after backend
154
+ death, then confirmed eligible recovery and resource absence. The other live
155
+ instance was preserved. The published 0.6.0 A/B/A point remained explicitly
156
+ partial for live identity; authored files and its old cache entries kept their
157
+ hashes. This is prepared development evidence, not a signed release identity.
158
+
159
+ | Local retained evidence under `/tmp/opencode/unaltraweb-d0-85/` | SHA-256 |
160
+ | --- | --- |
161
+ | `acceptance-final/evidence.json` | `e35457153c5ffe98b4bd231f608735a2c4ad07e9db756699d7446e06e6b0b9c7` |
162
+ | `helpers-proof/evidence.json` | `df994ccd8afaff45a7e3cfa9b6a29b650c9d01d63db2c9c36e59c31834273473` |
163
+ | `carta-proof/evidence.json` | `ac305ad222a792291ebd27679a72a37a3ff29ab4746301ed9db7f64705ca4755` |
164
+ | `bundler-proof/evidence.json` | `b304c4c5abe45ae9295f6eba86eae36be92e348e6be33463fcb50e119550d341` |
165
+
166
+ The Carta proof retains the published sender seal and unchanged original PDF,
167
+ removes the copied producer, relocates the receiver and forces another PDF build.
168
+ Both 11-page manuals have SHA-256
169
+ `46e50f815c007a97ac59898194023de3fa4e916e77e7eac21b73f8792244713c`; the letter is
170
+ on page 10. The four-profile populated Bundler regression passed 0.4.0 → prepared
171
+ 0.7.0 → 0.4.0, preserving all six tracked author/cache checksums and separately
172
+ reproducing the known unpatched 0.5.0 failure. No caches were cleared.
173
+
174
+ Review additionally tightened Docker absence classification: an unavailable
175
+ daemon socket is **unknown**, not evidence that a container was removed. That
176
+ source-only correction has a dedicated regression; final release acceptance must
177
+ be repeated against the actual source-bound candidates after integration.
178
+
179
+ The local suite passes 608 tests with 30 optional/environment skips. Distribution,
180
+ workflow, fresh-wheel and extracted-gem gates pass, as do the real MCP smoke,
181
+ manual preparation, docs build, prose checks and whitespace checks. These checks
182
+ do not override the separate signed-candidate/publication gates or the external
183
+ helper-process blocker.
184
+
185
+ ## Reviewed core selection
186
+
187
+ Implementation `0c87e8c276e58ac9c747833df69cdb439aa6132b` was reviewed and
188
+ integrated through [PR 87](https://github.com/dosquartsdedocs/unaltraweb/pull/87)
189
+ as `216ab2e8b6b8b45a5bb7da0a54bffd10edee895d`. Protected PR CI
190
+ [37271200816](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37271200816)
191
+ and CodeQL [37271200817](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37271200817)
192
+ passed. The recorded review is agent-attributed.
193
+
194
+ The next candidate source pins that integrated core and authorizes gem, wheel,
195
+ runtime and MCP as `ready` for same-source candidate preparation. The selected
196
+ signed PDF 0.6.0 and computation/capture 0.4.0 workers remain unchanged: the new
197
+ selection/lifecycle behavior is controller-owned. Strict receipt, ancestry,
198
+ candidate, installed-byte and publication gates still apply. This readiness is
199
+ for the containing owner release, not approval of the full managed coordinator
200
+ closure while the external Diavisuals identity gap remains.
@@ -1,5 +1,10 @@
1
1
  # MCP Contract
2
2
 
3
+ The D0 additions require a containing 0.7.0 runtime. Their pinned requirements,
4
+ exact API mapping, lifecycle scopes and external helper-identity blocker are
5
+ documented in [the owner contract](d0-runtime-identity.md). A source descriptor
6
+ does not upgrade an already-running 0.6.0 connection.
7
+
3
8
  `unaltraweb` exposes one global, on-demand stdio MCP server for website workspaces. Every client session starts an independently named Dockerized MCP process and mounts its current consumer project at `/workspace` and at its canonical host path. Stable factory, role, and project labels preserve project-scoped lifecycle control without forcing concurrent sessions to share a deterministic container name. The image includes both FastMCP and the Jekyll runtime, so the host does not need Python, the optional `mcp` package, Ruby, or Bundler.
4
9
 
5
10
  The client registration should launch:
@@ -32,8 +37,8 @@ it to an image ID before execution. The source checkout's digest pin is separate
32
37
  it advances only after a new receipt exists. Explicit `--image` (or the console
33
38
  adapter's `UNALTRAWEB_MCP_IMAGE`) permits a reviewed immutable selection.
34
39
 
35
- The post-release checkout pin selects published 0.5.1 MCP digest
36
- `sha256:908b4ce54c7bdf355e14ed55b31ed4b9baae319e211af90004a680d1d1cb8692`.
40
+ The post-release checkout pin selects published 0.6.0 MCP digest
41
+ `sha256:736c4ddd0a543454e3edaeac2e9cfd97a1279ce472923ac54e326c1281b2ba05`.
37
42
  Existing processes keep their running image until clients reconnect. Site build
38
43
  defaults and customized Makefiles remain separate, explicitly reviewed selections.
39
44
 
@@ -43,7 +48,7 @@ Repository editing coordination remains a control-plane responsibility rather th
43
48
 
44
49
  For a new agent-driven workspace, create or select the empty Git repository first, open that directory in the IDE, register the factory, restart the client, and then call `new_web`. The tool remains confined to the configured project root and does not accept an arbitrary destination. Generated sites include user-owned `README.md` and `AGENTS.md`, profile-specific source directories, and a managed runtime baseline; `scaffold_sync` never rewrites those user-owned guidance or content files.
45
50
 
46
- The native Python CLI remains modular. Without a factory checkout it supports `version`, `new-web`, top-level `doctor`, the host-only `import-calibre` command, and the exact package-only MCP inventory declared in `component-contract.json`. The complementary exact inventory fails clearly with `UNALTRAWEB_FACTORY_DIR` remediation. The separate Docker adapter delegates to the full image and does not expand that native inventory. The BOM selects the published `diavisuals v0.4.0` and `vegavisuals v0.4.0` releases. Its `consumer_integration` object is also the single source for the immutable core, workflow, PDF worker, and Vega revisions rendered into consumer scaffolds. Neither companion is bundled in the wheel or MCP server namespace.
51
+ The native Python CLI remains modular. Without a factory checkout it supports `version`, `new-web`, top-level `doctor`, the host-only `import-calibre` command, and the exact package-only MCP inventory declared in `component-contract.json`. The complementary exact inventory fails clearly with `UNALTRAWEB_FACTORY_DIR` remediation. The separate Docker adapter delegates to the full image and does not expand that native inventory. The 0.7.0 BOM selects owner-tested `diavisuals v0.5.0` and `vegavisuals v0.5.1`; existing 0.4.0 static receipts retain their exact integrity-checked compatibility point. Its `consumer_integration` object is also the single source for the immutable core, workflow, PDF worker, and Vega revisions rendered into consumer scaffolds. Neither companion is bundled in the wheel or MCP server namespace.
47
52
 
48
53
  Static Vega-Lite and Vega rendering remains owned by the required companion `vegavisuals` MCP dependency. Use its `visualization_status`, `visualization_check`, `render_visualizations`, and `vegavisuals://project/*` resources directly; `unaltraweb` exposes the authoring syntax but does not proxy those tools into the `web://` server.
49
54
 
@@ -81,6 +86,7 @@ baseline format nor runtime cleanup changes.
81
86
 
82
87
  | Resource | Description |
83
88
  | --- | --- |
89
+ | `web://runtime-identity` | Bounded identity of this serving process: startup/current code, instance/PID namespaces, binding, runtime selections and cooperative lifecycle. |
84
90
  | `web://distribution` | Package-owned component BOM plus offline, feature-aware doctor findings for the current factory and project. |
85
91
  | `web://site-context` | Site profile, feature flags, content inventory, bibliography, bibliometrics, build state, and offline consumer-update advisory. |
86
92
  | `web://site-doctor` | Read-only offline distribution, project-contract, freshness, scaffold-drift, and core-override findings. |
@@ -109,6 +115,8 @@ baseline format nor runtime cleanup changes.
109
115
 
110
116
  | Tool | Notes |
111
117
  | --- | --- |
118
+ | `runtime_identity` | Observe the actual serving instance, startup/current package evidence and selected/observed images; never prepare a runtime or inspect site content. |
119
+ | `runtime_drain` | With `confirm=true`, stop admission on this connection; then close stdio and verify exact container/session resource termination. It is not workspace-wide down. |
112
120
  | `distribution_doctor` | Return offline component/factory/project findings with stable codes and optional local Docker image inspection that never pulls. |
113
121
  | `new_web` | Create a profile-specific site from package-owned assets after a complete collision, path, and symlink preflight. |
114
122
  | `starter_templates` | List package-owned profile scaffolds under the legacy inventory name. |
@@ -182,7 +190,7 @@ Manual PDF publication is a local workspace operation: it copies reviewed artefa
182
190
 
183
191
  ### Retained Letter Integration
184
192
 
185
- The 0.6.0 development increment adds package-only import/check commands and the
193
+ The published 0.6.0 release adds package-only import/check commands and the
186
194
  `web://artifact-imports` resource. Web rendering needs its containing Jekyll core;
187
195
  manual inclusion also needs the matching PDF worker. Published 0.5.1 components
188
196
  do not acquire this capability from a launcher-pin change.
@@ -0,0 +1,185 @@
1
+ # unaltraweb 0.6.0 delivery closeout
2
+
3
+ Published **2026-10-02 17:28:14 UTC**:
4
+ [v0.6.0](https://github.com/dosquartsdedocs/unaltraweb/releases/tag/v0.6.0).
5
+ Owner coordination: [issue 80](https://github.com/dosquartsdedocs/unaltraweb/issues/80).
6
+ This report supersedes the pending delivery state in the chronological
7
+ [preparation record](retained-letter-import.md). Issue 76 and release 0.5.1
8
+ remain closed and immutable.
9
+
10
+ ## Review, source and integration
11
+
12
+ The owner explicitly authorized commits, push, PR review/integration, signed
13
+ candidate workflows, gated publication and the post-release launcher pin. The
14
+ checkout was reconciled against all existing unit-80 paths; no unrelated edits
15
+ were found. Work used one checkout and ordinary serial branches.
16
+
17
+ | Role | Revision / evidence |
18
+ | --- | --- |
19
+ | Reviewed implementation | `7af652fb83629e959af05c407a34a2f5a15f7936` |
20
+ | Integrated core selected by consumers | `5cf9817489c8dc47cee726bfe42fe3071cd32b85`, [PR 81](https://github.com/dosquartsdedocs/unaltraweb/pull/81) |
21
+ | Immutable core/PDF selection | `8e93b8b808204faef54cccaee622699f04a11805` |
22
+ | Final artifact-producing source | `de52db351490e94939aac20c6af264f22a6a1678`, [PR 82](https://github.com/dosquartsdedocs/unaltraweb/pull/82) |
23
+ | Receipt-only child | `3ce7606bc6148aee19d067c559823d8c5b52db5d` |
24
+ | Receipt integration and tag target | `ec6dcb28e890d32718aa173934527a7b4213a042`, [PR 83](https://github.com/dosquartsdedocs/unaltraweb/pull/83) |
25
+ | Annotated tag object | `1605578861a1a09157037e6085020baad4948d72` |
26
+
27
+ All three PRs have explicit agent review records and protected CI; those records
28
+ do not claim human review. Merge commits preserve the reviewed core's ancestry.
29
+ The receipt integration's first parent is exactly the artifact-producing source,
30
+ and its first-parent diff contains only `release-candidates.json`.
31
+
32
+ Review closed independent metadata-reference validation, malformed Pandoc-link
33
+ handling, internal PDF OpenAction destinations, and direct signed-runtime
34
+ acceptance. The vendored v1 verifier/schema still match the established hub pin
35
+ byte for byte; 26 reference conformance tests passed against matching fixtures.
36
+ Source checks reported 587 Python cases with 30 optional skips. Ruby, actual PDF,
37
+ MCP, wheel/gem, workflow/distribution, docs and reproducibility gates passed.
38
+
39
+ ## Receipt and published identities
40
+
41
+ [Tagged native receipt](https://github.com/dosquartsdedocs/unaltraweb/blob/v0.6.0/release-candidates.json):
42
+
43
+ ```text
44
+ schema_version: 1
45
+ release: v0.6.0
46
+ source_commit: de52db351490e94939aac20c6af264f22a6a1678
47
+ receipt SHA-256: 5ed4e6dd615367927e6ea8a3541c9e7dca9dad73a738851705f563e0e6895280
48
+ ```
49
+
50
+ Exactly the four `ready` components are bound to that source:
51
+
52
+ | Component | Registry artifact | SHA-256 / OCI index digest |
53
+ | --- | --- | --- |
54
+ | Gem | `unaltraweb-0.6.0.gem` | `276d04a71d1170220c3881cce8d0800633c42b0ee7ad07a376f80996c6afd177` |
55
+ | Wheel | `unaltraweb_mcp-0.6.0-py3-none-any.whl` | `3effb471ffd264f7fb2a8c88dd6536bc86639eae52806527c2d872653944c85a` |
56
+ | Base runtime | `ghcr.io/dosquartsdedocs/unaltraweb` | `sha256:a4e9d82b0e9a72e519c44e09ca449b2dc37964fbb9953ae6e4adff648e39b94b` |
57
+ | Full MCP | `ghcr.io/dosquartsdedocs/unaltraweb-mcp` | `sha256:736c4ddd0a543454e3edaeac2e9cfd97a1279ce472923ac54e326c1281b2ba05` |
58
+
59
+ Both `0.6.0` and `v0.6.0` OCI aliases were inspected anonymously and match those
60
+ index digests. The MCP's local configuration ID is
61
+ `sha256:73320210b19597243c7183ef255ffa1cbc36a07157e6ab4e1b3d9349fa89a063`;
62
+ its revision label is the exact artifact source above. Configuration IDs and
63
+ registry index digests are distinct identities.
64
+
65
+ The selected PDF 0.6.0 is the already-published, signed and tested specialization:
66
+ `ghcr.io/dosquartsdedocs/unaltraweb-manual-pdf@sha256:0ba267cb87f53ebaca4e31805fe00610cd61fdf97a8d2c3692f4655700dceaed`.
67
+ It was produced from core `5cf9817489c8dc47cee726bfe42fe3071cd32b85` by the
68
+ [initial signed image run](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37029374441),
69
+ and its local configuration ID is
70
+ `sha256:d7e7d3243616270fc9e6eed7f8238c412dbe3d9ffc18d2832d17d66ca630d139`.
71
+ The BOM, generated consumer tuple and Make default select that same digest.
72
+ It is reused as `released`, outside the four-component coordinated receipt.
73
+ The later image workflow also tested a verification-only PDF candidate
74
+ `sha256:9b3ae6fe1f04ff7de6c81c258d7c16f79cc7f2857c9b46b0829fc71812ace11f`;
75
+ that candidate is not the selected worker and received no coordinated semver
76
+ promotion. No self-digest or fabricated core SHA was introduced.
77
+
78
+ Computation, capture, Diavisuals and Vegavisuals retain the tested 0.4.0 selections.
79
+ Their exact immutable references remain in the BOM, and earlier published
80
+ artifacts and receipts remain at their original tags.
81
+
82
+ ## CI, signing and publication
83
+
84
+ | Gate | Successful run |
85
+ | --- | --- |
86
+ | Implementation PR CI / CodeQL | [37027921742](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37027921742) / [37027922332](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37027922332) |
87
+ | Core integration CI | [37028677932](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37028677932) |
88
+ | Tuple PR CI / CodeQL | [37032368507](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37032368507) / [37032368609](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37032368609) |
89
+ | Final source CI / CodeQL | [37033064607](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37033064607) / [37033064633](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37033064633) |
90
+ | Signed final image build/test | [37033593312](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37033593312) |
91
+ | Package preparation | [37033596931](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37033596931) |
92
+ | Receipt PR CI / CodeQL | [37038403708](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37038403708) / [37038403802](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37038403802) |
93
+ | Receipt/tag target CI / CodeQL | [37039075585](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37039075585) / [37039075748](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37039075748) |
94
+ | Tag CI | [37039786598](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37039786598) |
95
+ | Semver image promotion | [37040420721](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37040420721) |
96
+ | PyPI/RubyGems Trusted Publishing | [37040424945](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/37040424945) |
97
+
98
+ The signed image workflows checked exact OCI digests, signer workflow, source
99
+ revision and labels before executing candidates. Promotion reverified those
100
+ bindings and reused the tested manifests without rebuilding. The package
101
+ authorizer bound workflow/run/source, immutable artifact ID `11237594327`, exact
102
+ inventory and checksums to the receipt. Its archive digest is
103
+ `sha256:79a67d7a8370468abe1f0265b248f3e9d6cf6187ac0bc891a0b3801e5a9931a6`.
104
+ OIDC jobs published only the staged verified gem and wheel.
105
+
106
+ The actual receipt-only branch, merged target and annotated tag passed strict
107
+ `distribution-release-check` and publication-ref/ancestry gates. Native package
108
+ run authorization, exact staging and `verify_package_publish.py release` passed
109
+ before privileged publication. Strict release checks apply to the receipt/tag
110
+ revision, not subsequent ordinary post-release source commits.
111
+
112
+ ## Real retained-letter and rollback proofs
113
+
114
+ Acceptance consumed the authenticated published Carta 0.3.0rc1 packet, not a local
115
+ producer checkout or synthetic unit fixture. The sender bundle hash is
116
+ `aeaa757ee1be19bfe56dc9af8dd1178032929b4a2bec2dad2ebb621b158ad999`.
117
+ All 12 seal files remain under `.unaltraweb/artifacts/carta/bundle/`.
118
+ The mapping `payload/output/letter.pdf` → `assets/documents/carta.pdf` retains
119
+ SHA-256 `4a661740a18fe1cf75b84aa0d8532511fd105eb5484a5af700e11e8e30828ea4`.
120
+ The native chapter is `_chapters/en/carta.md`, with its separate binding and
121
+ standard-v1 integration record outside the seal.
122
+
123
+ The downloaded final CI wheel ran against the exact signed MCP and selected PDF
124
+ digests above, without deriving another controller image. Both copied producer
125
+ and incoming trees were removed, author prose survived an identical reimport,
126
+ and the receiver was moved before native/generic verification and a forced PDF
127
+ rebuild. Web/HTTP checks passed at both locations. The complete original letter
128
+ appears on page 10 of an 11-page composed manual. Both composed PDFs have hash
129
+ `416e96f66bb6608b1e20bf5e36b6b16133bd4018c17e6c079e8987b736fe9cae`.
130
+ The integration record hash is
131
+ `ebe07ee10ad740a2bbbd36d532f336374cf4bb19ccb373b6590b2279f664a222`.
132
+ Generic verification confirms one bundle, one mapping and 14 verified files;
133
+ the native checker additionally binds the source and rendered page.
134
+
135
+ Local evidence root: `/tmp/opencode/unaltraweb-closeout-060/`.
136
+
137
+ | Retained evidence | SHA-256 |
138
+ | --- | --- |
139
+ | `candidate-letter-proof/evidence.json` | `618573b1a867daf6b1f3023e0d5700c27a6de429abea160b4c51ff8699f86f9c` |
140
+ | `candidate-roundtrip/evidence.json` | `23ec8885e8306931fb8ee4899e95b7bff4b3d23d2b63cdd3321e3f68722c3bb8` |
141
+ | `published-packages/verified.json` | `073d7ca1bbe46b8be5d89211501ca8fc7b71297d7eb542c4c9cc27ff167aa91e` |
142
+
143
+ The roundtrip exercised all four profiles using immutable MCP references:
144
+ 0.4.0 `sha256:389bc585cdb4fc89d3372f4896a55fe26e15df38b46bc114ce44fdb3f1c8deb9`,
145
+ unpatched 0.5.0 `sha256:36d17edbade77edb40a687f6a744203c6329acb33fbc2eb255e88d9ff1a42c98`,
146
+ then the new 0.6.0 digest. The unpatched failure was reproduced on a separate
147
+ copy. Two builds and a preview passed at 0.6.0 and again after returning to 0.4.0.
148
+ All six author/cache files retained their hashes; no caches were emptied.
149
+
150
+ ## Public downloads and post-release selection
151
+
152
+ Anonymous PyPI and RubyGems metadata and package bytes match the receipt exactly.
153
+ GitHub Release assets were downloaded again and pass `SHA256SUMS`, whose own hash
154
+ is `60d80d6e0803154a7b5c98c1731f4bec905def3a481152d4e59d7daf8932fc1a`.
155
+ A fresh public-wheel venv passed default `prepare`, `check` and real stdio `smoke`,
156
+ selecting the expected MCP configuration ID without a local factory dependency.
157
+ That public installation rechecked the relocated receiver and rendered mapping.
158
+ The public gem installed into an isolated gem home and activated as 0.6.0 using
159
+ the runtime's existing Ruby dependencies; this is not a network-fresh dependency
160
+ installation claim. The downloaded candidate gem also passed isolated native
161
+ helper-closure inspection.
162
+
163
+ The source-only post-release branch `chore/80-pin-published-mcp-0.6.0` selects the
164
+ published MCP digest in `Makefile` and `scripts/unaltraweb-mcp-bootstrap.sh`, updates
165
+ the pin regression and current docs, and accompanies this report. Its PR/merge
166
+ and final clean-main confirmation are recorded in issue 80 after protected CI.
167
+ It does not rebuild or alter the published receipt, tag or packages.
168
+
169
+ The post-pin local recheck passed 587 Python cases (558 passed, 29 optional
170
+ skips), distribution/workflow/wheel and prose checks, `mcp-check`, real
171
+ `mcp-smoke` with the selected public PDF, docs build and whitespace checks.
172
+ Development builds used only `unaltraweb-closeout-060-*:post-pin` resources.
173
+ The actual `MCP_CONSUMER_WORKSPACE`-bound `make mcp-stdio` transport also initialized
174
+ a fresh synthetic consumer and exposed both native import tools; Docker inspection
175
+ confirmed the published MCP configuration ID above. Its retained result is
176
+ `/tmp/opencode/unaltraweb-closeout-060/post-pin-proof/evidence.json` and the session
177
+ exited normally. `mcp-build` and the bootstrap's `--check` selected the public
178
+ runtime directly.
179
+
180
+ Acceptance covers the exact Linux/amd64 tuple and published English one-page
181
+ Carta sample. The generic conformance tests do not establish Diapora composition
182
+ or other domain imports. Active IDE processes keep their loaded runtime until
183
+ reconnected. No real manual, author artifact, client registration or helper
184
+ selection was migrated. Consumer adoption and hub catalogue integration remain
185
+ separate owner actions; this report supplies their verified intake evidence.
@@ -1,5 +1,9 @@
1
1
  # Native retained-letter import — issue 80
2
2
 
3
+ Delivery completed on 2026-10-02; see [the 0.6.0 closeout](owner-closeout-80.md)
4
+ for final revisions, receipts, registry downloads and the post-release pin.
5
+ The following sections retain the chronological preparation and review evidence.
6
+
3
7
  ## Scope and release state
4
8
 
5
9
  This is a separate 0.6.0 development increment after the administrative closeout
@@ -142,14 +142,14 @@ def main() -> int:
142
142
  fake_bin = temp / "fake-bin"
143
143
  fake_bin.mkdir()
144
144
  fake_docker = fake_bin / "docker"
145
- fake_docker.write_text("#!/bin/sh\n[ \"$1 $2\" = 'image inspect' ] || exit 99\n[ \"$5\" = \"$EXPECTED_IMAGE\" ] || exit 98\nprintf 'sha256:fixture\\n'\n", encoding="utf-8")
145
+ fake_docker.write_text("#!/bin/sh\n[ \"$1 $2\" = 'image inspect' ] || exit 99\n[ \"$5\" = \"$EXPECTED_IMAGE\" ] || exit 98\nprintf 'sha256:1111111111111111111111111111111111111111111111111111111111111111\\n'\n", encoding="utf-8")
146
146
  fake_docker.chmod(0o755)
147
147
  expected_image = json.loads((ROOT / "src/unaltraweb_mcp/component-contract.json").read_text())["components"]["mcp"]["reference"]
148
148
  launcher_env = {**no_consumer_env, "PATH": f"{fake_bin}:{env['PATH']}", "EXPECTED_IMAGE": expected_image}
149
149
  launcher_env.pop("UNALTRAWEB_MCP_IMAGE", None)
150
150
  launcher_env.pop("MCP_RELEASE_IMAGE", None)
151
151
  prepared_image = run([str(docker_cli), "prepare"], cwd=temp, env=launcher_env).stdout.strip()
152
- if prepared_image != "sha256:fixture":
152
+ if prepared_image != "sha256:" + "1" * 64:
153
153
  raise RuntimeError("Installed preparation did not use the packaged bootstrap")
154
154
  run(["make", "--silent", "--no-print-directory", "-C", str(launcher), "mcp-build"], cwd=temp, env=launcher_env)
155
155
  run([str(docker_cli), "prepare", "--image", "explicit:override"], cwd=temp,
@@ -3,18 +3,28 @@ set -eu
3
3
 
4
4
  usage() {
5
5
  cat <<'USAGE'
6
- Usage: unaltraweb-mcp-bootstrap [--project PATH] [--image IMAGE] [--prepare|--check|--smoke]
6
+ Usage: unaltraweb-mcp-bootstrap [--project PATH] [--host-project PATH] [--image IMAGE]
7
+ [--expected-image-id ID] [--managed] [--offline] [--session-id ID] [--worker-images JSON]
8
+ [--prepare|--check|--smoke]
7
9
 
8
10
  Start one Dockerized unaltraweb MCP stdio server for the selected workspace.
9
11
  If --project is omitted, MCP_CONSUMER_WORKSPACE is used, then the current directory.
10
12
  --prepare inspects or pulls the selected image without mounting a consumer.
11
- --check and --smoke require that image locally; neither builds nor pulls.
13
+ Launch, --check and --smoke require that exact image locally; none builds or pulls.
14
+ Managed mode requires an immutable reference or an expected full image ID.
12
15
  USAGE
13
16
  }
14
17
 
15
- image="${UNALTRAWEB_MCP_IMAGE:-ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:908b4ce54c7bdf355e14ed55b31ed4b9baae319e211af90004a680d1d1cb8692}"
18
+ image="${UNALTRAWEB_MCP_IMAGE:-ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:736c4ddd0a543454e3edaeac2e9cfd97a1279ce472923ac54e326c1281b2ba05}"
16
19
  project="${MCP_CONSUMER_WORKSPACE:-${UNALTRAWEB_PROJECT:-}}"
17
20
  operation=serve
21
+ expected_image="${UNALTRAWEB_EXPECTED_IMAGE_ID:-}"
22
+ managed="${UNALTRAWEB_MANAGED_RUNTIME:-0}"
23
+ session_id=""
24
+ host_project=""
25
+ network=bridge
26
+ worker_images="${UNALTRAWEB_WORKER_IMAGES:-}"
27
+ [ -n "$worker_images" ] || worker_images='{}'
18
28
 
19
29
  while [ "$#" -gt 0 ]; do
20
30
  case "$1" in
@@ -28,6 +38,18 @@ while [ "$#" -gt 0 ]; do
28
38
  image="$2"
29
39
  shift 2
30
40
  ;;
41
+ --host-project|--expected-image-id|--session-id|--worker-images)
42
+ [ "$#" -ge 2 ] || { printf 'Missing value for %s\n' "$1" >&2; exit 2; }
43
+ case "$1" in
44
+ --host-project) host_project=$2 ;;
45
+ --expected-image-id) expected_image=$2 ;;
46
+ --session-id) session_id=$2 ;;
47
+ --worker-images) worker_images=$2 ;;
48
+ esac
49
+ shift 2
50
+ ;;
51
+ --managed) managed=1; shift ;;
52
+ --offline) network=none; shift ;;
31
53
  --prepare|--check|--smoke)
32
54
  [ "$operation" = serve ] || { printf '%s\n' 'Select only one operation.' >&2; exit 2; }
33
55
  operation="${1#--}"
@@ -45,10 +67,33 @@ while [ "$#" -gt 0 ]; do
45
67
  esac
46
68
  done
47
69
 
70
+ valid_image_id() {
71
+ case "$1" in sha256:*) hex=${1#sha256:} ;; *) return 1 ;; esac
72
+ [ "${#hex}" -eq 64 ] || return 1
73
+ case "$hex" in *[!0-9a-f]*) return 1 ;; esac
74
+ }
75
+ case "$image" in ''|*[!A-Za-z0-9._/:@-]*) printf '%s\n' 'Invalid image reference' >&2; exit 2 ;; esac
76
+ case "$image" in *://*) printf '%s\n' 'Docker references are not URLs' >&2; exit 2 ;; esac
77
+ case "$image" in
78
+ *@*)
79
+ valid_image_id "${image##*@}" || { printf '%s\n' 'Invalid digest reference' >&2; exit 2; }
80
+ case "${image%@*}" in *@*) printf '%s\n' 'Invalid digest reference' >&2; exit 2 ;; esac
81
+ ;;
82
+ esac
83
+ if [ -n "$expected_image" ]; then
84
+ valid_image_id "$expected_image" || { printf '%s\n' 'Expected image ID must be a full lowercase sha256' >&2; exit 2; }
85
+ managed=1
86
+ fi
87
+ case "$image" in
88
+ sha256:*) valid_image_id "$image" || exit 2; managed=1 ;;
89
+ *@sha256:*) valid_image_id "sha256:${image##*@sha256:}" || exit 2; managed=1 ;;
90
+ *) if [ "$managed" = 1 ] && [ -z "$expected_image" ]; then printf '%s\n' 'Managed mutable references require --expected-image-id' >&2; exit 2; fi ;;
91
+ esac
92
+
48
93
  resolve_image() {
49
94
  if ! resolved_image="$(docker image inspect --format '{{.Id}}' "$image" 2>/dev/null)"; then
50
95
  case "$operation" in
51
- check|smoke)
96
+ serve|check|smoke)
52
97
  printf 'Image is not prepared: %s. Run --prepare first.\n' "$image" >&2
53
98
  exit 1
54
99
  ;;
@@ -56,14 +101,19 @@ resolve_image() {
56
101
  docker pull "$image" >&2
57
102
  resolved_image="$(docker image inspect --format '{{.Id}}' "$image")"
58
103
  fi
104
+ valid_image_id "$resolved_image" || { printf '%s\n' 'Docker returned an invalid image identity' >&2; exit 2; }
105
+ case "$image" in sha256:*) [ "$image" = "$resolved_image" ] || { printf '%s\n' 'Selected image ID mismatch' >&2; exit 1; } ;; esac
106
+ if [ -n "$expected_image" ] && [ "$expected_image" != "$resolved_image" ]; then
107
+ printf '%s\n' 'Prepared image does not match the expected image ID; no fallback was attempted' >&2; exit 1
108
+ fi
59
109
  }
60
110
 
61
111
  if [ "$operation" != serve ]; then
62
112
  resolve_image
63
113
  case "$operation" in
64
114
  prepare) printf '%s\n' "$resolved_image"; exit 0 ;;
65
- check) exec docker run --rm --network none --user "$(id -u):$(id -g)" -e HOME=/tmp --entrypoint unaltraweb-mcp "$resolved_image" version ;;
66
- smoke) exec docker run --rm --network none --user "$(id -u):$(id -g)" -e HOME=/tmp --entrypoint python3 "$resolved_image" /opt/unaltraweb/test/mcp_smoke.py ;;
115
+ check) exec docker run --rm --pull never --network none --user "$(id -u):$(id -g)" -e HOME=/tmp --entrypoint unaltraweb-mcp "$resolved_image" version ;;
116
+ smoke) exec docker run --rm --pull never --network none --user "$(id -u):$(id -g)" -e HOME=/tmp --entrypoint python3 "$resolved_image" /opt/unaltraweb/test/mcp_smoke.py ;;
67
117
  esac
68
118
  fi
69
119
 
@@ -85,10 +135,28 @@ if [ ! -d "$project" ]; then
85
135
  fi
86
136
 
87
137
  project="$(CDPATH= cd -- "$project" && pwd -P)"
138
+ launcher_project="$project"
139
+ if [ -z "$host_project" ]; then
140
+ host_project="${UNALTRAWEB_DOCKER_ROOT:-}"
141
+ if [ -n "$host_project" ]; then
142
+ inherited_workspace="${MCP_CONSUMER_WORKSPACE:-}"
143
+ if [ -z "$inherited_workspace" ] || [ ! -d "$inherited_workspace" ] || [ "$(CDPATH= cd -- "$inherited_workspace" && pwd -P)" != "$launcher_project" ]; then
144
+ printf '%s\n' 'Inherited daemon mapping belongs to another or unknown workspace; pass --host-project explicitly' >&2; exit 2
145
+ fi
146
+ fi
147
+ fi
148
+ if [ -z "$host_project" ] && [ -f /.dockerenv ]; then
149
+ printf '%s\n' 'A containerized launcher needs --host-project with the explicit daemon-host mapping' >&2; exit 2
150
+ fi
151
+ host_project="${host_project:-$project}"
152
+ case "$host_project" in /*) ;; *) printf '%s\n' 'Host project must be absolute' >&2; exit 2 ;; esac
153
+ case "$host_project" in *"$newline"*|*"$carriage_return"*) exit 2 ;; esac
154
+ [ "$host_project" != / ] && [ "$(realpath -ms -- "$host_project")" = "$host_project" ] || { printf '%s\n' 'Host project must be normalized' >&2; exit 2; }
155
+ project="$host_project"
88
156
  owner="${UNALTRAWEB_PROJECT_USER:-$(id -u):$(id -g)}"
89
157
  docker_socket="${UNALTRAWEB_DOCKER_SOCKET:-/var/run/docker.sock}"
90
158
  script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)"
91
- project_id="$(/bin/sh "$script_dir/unaltraweb-mcp-project-id.sh" "$project")"
159
+ project_id="$(printf '%s' "$project" | sha256sum | cut -c 1-16)"
92
160
  workspace_mount="$(/bin/sh "$script_dir/unaltraweb-docker-mount.sh" "$project" /workspace)"
93
161
  mirror_mount="$(/bin/sh "$script_dir/unaltraweb-docker-mount.sh" "$project" "$project")"
94
162
 
@@ -103,7 +171,7 @@ if [ -S "$docker_socket" ]; then
103
171
  exit 1
104
172
  fi
105
173
  case "$docker_socket" in
106
- "$project"|"$project"/*)
174
+ "$project"|"$project"/*|"$launcher_project"|"$launcher_project"/*)
107
175
  printf '%s\n' 'UNALTRAWEB_DOCKER_SOCKET must be outside the consumer project so the persistent preview cannot inherit it.' >&2
108
176
  exit 1
109
177
  ;;
@@ -111,6 +179,9 @@ if [ -S "$docker_socket" ]; then
111
179
  fi
112
180
 
113
181
  resolve_image
182
+ if [ -z "$session_id" ]; then session_id=$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n'); fi
183
+ [ "${#session_id}" -eq 32 ] || { printf '%s\n' 'Session ID must contain 32 lowercase hex characters' >&2; exit 2; }
184
+ case "$session_id" in *[!0-9a-f]*) exit 2 ;; esac
114
185
  image_reference="$image"
115
186
  case "$image_reference" in
116
187
  ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:*) ;;
@@ -123,10 +194,13 @@ case "$image_reference" in
123
194
  ;;
124
195
  esac
125
196
 
126
- set -- docker run --rm -i \
197
+ set -- docker run --rm --pull never --init -i \
198
+ --network "$network" \
199
+ --name "unaltraweb-stdio-$session_id" --cpus 2 --memory 4g --pids-limit 512 \
127
200
  --label io.context.mcp-factory=unaltraweb \
128
201
  --label io.context.mcp-role=stdio \
129
202
  --label "io.context.mcp-project=$project_id" \
203
+ --label "io.context.mcp-session=$session_id" \
130
204
  --user "$owner" \
131
205
  -e HOME=/tmp \
132
206
  -e COMPUTE_CORE=/opt/unaltraweb \
@@ -136,6 +210,13 @@ set -- docker run --rm -i \
136
210
  -e UNALTRAWEB_FACTORY_DIR=/opt/unaltraweb \
137
211
  -e "UNALTRAWEB_MCP_IMAGE=$resolved_image" \
138
212
  -e "UNALTRAWEB_MCP_IMAGE_REFERENCE=$image_reference" \
213
+ -e "UNALTRAWEB_MCP_REQUESTED_IMAGE=$image" \
214
+ -e "UNALTRAWEB_EXPECTED_IMAGE_ID=$resolved_image" \
215
+ -e "UNALTRAWEB_RUNTIME_SESSION=$session_id" \
216
+ -e "UNALTRAWEB_MANAGED_RUNTIME=$managed" \
217
+ -e "UNALTRAWEB_RUNTIME_NETWORK=$network" \
218
+ -e "UNALTRAWEB_LAUNCHER_PROJECT=$launcher_project" \
219
+ -e "UNALTRAWEB_WORKER_IMAGES=$worker_images" \
139
220
  -e "UNALTRAWEB_PROJECT_USER=$owner" \
140
221
  --mount "$workspace_mount" \
141
222
  --mount "$mirror_mount" \
@@ -58,16 +58,48 @@ case "$project_id" in
58
58
  ;;
59
59
  esac
60
60
 
61
- containers=$(docker ps -aq \
61
+ # Explicit workspace-wide shutdown. Managed per-client detach uses live drain,
62
+ # stdio EOF and the separate session-status/reap-session native adapter instead.
63
+ containers=$(docker ps -aq --no-trunc \
62
64
  --filter "label=io.context.mcp-factory=unaltraweb" \
63
65
  --filter "label=io.context.mcp-project=$project_id")
64
- if [ -n "$containers" ]; then
65
- docker rm -f $containers >/dev/null
66
- fi
67
-
68
- networks=$(docker network ls -q \
66
+ networks=$(docker network ls -q --no-trunc \
69
67
  --filter "label=io.context.mcp-factory=unaltraweb" \
70
68
  --filter "label=io.context.mcp-project=$project_id")
71
- if [ -n "$networks" ]; then
72
- docker network rm $networks >/dev/null
73
- fi
69
+
70
+ valid_id() {
71
+ [ "${#1}" -eq 64 ] || return 1
72
+ case "$1" in *[!0-9a-f]*) return 1 ;; esac
73
+ }
74
+ count=0
75
+ for cid in $containers; do
76
+ count=$((count + 1))
77
+ [ "$count" -le 64 ] && valid_id "$cid" || { printf '%s\n' 'Container inventory is invalid or exceeds bounds' >&2; exit 2; }
78
+ labels=$(docker container inspect --format '{{index .Config.Labels "io.context.mcp-factory"}}|{{index .Config.Labels "io.context.mcp-role"}}|{{index .Config.Labels "io.context.mcp-project"}}' "$cid")
79
+ case "$labels" in
80
+ "unaltraweb|stdio|$project_id"|"unaltraweb|preview|$project_id"|"unaltraweb|computation|$project_id"|"unaltraweb|manual-pdf|$project_id"|"unaltraweb|web-capture|$project_id"|"unaltraweb|web-capture-site|$project_id") ;;
81
+ *) printf '%s\n' 'Refusing unknown container ownership' >&2; exit 2 ;;
82
+ esac
83
+ done
84
+ for nid in $networks; do
85
+ count=$((count + 1))
86
+ [ "$count" -le 64 ] && valid_id "$nid" || { printf '%s\n' 'Network inventory is invalid or exceeds bounds' >&2; exit 2; }
87
+ labels=$(docker network inspect --format '{{index .Labels "io.context.mcp-factory"}}|{{index .Labels "io.context.mcp-role"}}|{{index .Labels "io.context.mcp-project"}}' "$nid")
88
+ [ "$labels" = "unaltraweb|web-capture|$project_id" ] || { printf '%s\n' 'Refusing unknown network ownership' >&2; exit 2; }
89
+ done
90
+ if [ -n "$containers" ]; then docker rm -f $containers >/dev/null; fi
91
+ if [ -n "$networks" ]; then docker network rm $networks >/dev/null; fi
92
+ for cid in $containers; do
93
+ if observed=$(docker container inspect --format '{{.Id}}' "$cid" 2>&1); then
94
+ printf '%s\n' 'Container termination is not confirmed' >&2; exit 1
95
+ else
96
+ case "$observed" in *"No such container"*|*"no such container"*|*"No such object"*) ;; *) printf '%s\n' 'Container absence is unknown' >&2; exit 1 ;; esac
97
+ fi
98
+ done
99
+ for nid in $networks; do
100
+ if observed=$(docker network inspect --format '{{.Id}}' "$nid" 2>&1); then
101
+ printf '%s\n' 'Network removal is not confirmed' >&2; exit 1
102
+ else
103
+ case "$observed" in *"No such network"*|*"no such network"*|*"No such object"*) ;; *) printf '%s\n' 'Network absence is unknown' >&2; exit 1 ;; esac
104
+ fi
105
+ done
@@ -588,6 +588,8 @@ def internal_network(network: str) -> bool:
588
588
 
589
589
  def build_image(image: str) -> None:
590
590
  available = inspect_image(image)["available"] == "true"
591
+ if not available and os.environ.get("UNALTRAWEB_MANAGED_RUNTIME") == "1":
592
+ raise WebCaptureError("Selected capture image is not prepared; managed execution never pulls")
591
593
  if available:
592
594
  return
593
595
  completed = subprocess.run(["docker", "pull", image], check=False)
@@ -722,6 +724,9 @@ def run_worker(project: Path, image: str, config_path: Path) -> None:
722
724
  "project": os.environ.get("UNALTRAWEB_WORKER_PROJECT", "").strip(),
723
725
  "token": os.environ.get("UNALTRAWEB_WORKER_TOKEN", "").strip(),
724
726
  }
727
+ session = os.environ.get("UNALTRAWEB_RUNTIME_SESSION", "")
728
+ if session and not re.fullmatch(r"[0-9a-f]{32}", session):
729
+ raise WebCaptureError("Invalid runtime session identity")
725
730
  worker_args: list[str] = []
726
731
  cidfile: Path | None = None
727
732
  if any(worker_values.values()):
@@ -739,7 +744,9 @@ def run_worker(project: Path, image: str, config_path: Path) -> None:
739
744
  "--label", f"io.context.mcp-worker-token={worker_values['token']}",
740
745
  ]
741
746
  command = [
742
- "docker", "run", "--rm", *worker_args, "--user", f"{os.getuid()}:{os.getgid()}", "--network", network, "--ipc=host",
747
+ "docker", "run", "--rm", "--pull", "never", *worker_args,
748
+ *(["--label", f"io.context.mcp-session={session}"] if session else []),
749
+ "--user", f"{os.getuid()}:{os.getgid()}", "--network", network, "--ipc=host",
743
750
  "--read-only", "--cap-drop", "ALL", "--security-opt", "no-new-privileges", "--pids-limit", "256", "--cpus", "2", "--memory", "2g",
744
751
  "--tmpfs", "/tmp:rw,noexec,nosuid,size=512m", "-e", "HOME=/tmp",
745
752
  "--mount", docker_bind_mount(project, "/project"), "-w", "/project", image,
@@ -2,16 +2,16 @@
2
2
  "$schema": "component-contract.schema.json",
3
3
  "schema_version": 1,
4
4
  "release": {
5
- "version": "0.6.0",
6
- "tag": "v0.6.0"
5
+ "version": "0.7.0",
6
+ "tag": "v0.7.0"
7
7
  },
8
8
  "consumer_integration": {
9
9
  "schema_version": 1,
10
10
  "core_repository": "https://github.com/dosquartsdedocs/unaltraweb.git",
11
- "core_sha": "5cf9817489c8dc47cee726bfe42fe3071cd32b85",
11
+ "core_sha": "216ab2e8b6b8b45a5bb7da0a54bffd10edee895d",
12
12
  "site_deploy_workflow": "dosquartsdedocs/unaltraweb/.github/workflows/site-deploy.yml",
13
13
  "manual_pdf_image": "ghcr.io/dosquartsdedocs/unaltraweb-manual-pdf@sha256:0ba267cb87f53ebaca4e31805fe00610cd61fdf97a8d2c3692f4655700dceaed",
14
- "vegavisuals_sha": "68c0b231402ae9485cc34ce530dc5239cb0ec194"
14
+ "vegavisuals_sha": "a448cc50a5f511743a6fcfc213baeb9be80f4a27"
15
15
  },
16
16
  "receipt_contract": {
17
17
  "schema_version": 1,
@@ -36,10 +36,10 @@
36
36
  "gem": {
37
37
  "kind": "gem",
38
38
  "name": "unaltraweb",
39
- "version": "0.6.0",
40
- "release": "v0.6.0",
39
+ "version": "0.7.0",
40
+ "release": "v0.7.0",
41
41
  "release_status": "ready",
42
- "reference": "unaltraweb (= 0.6.0)",
42
+ "reference": "unaltraweb (= 0.7.0)",
43
43
  "repository": "https://github.com/dosquartsdedocs/unaltraweb",
44
44
  "included_in_wheel": false,
45
45
  "features": ["jekyll-core", "site-build"]
@@ -47,10 +47,10 @@
47
47
  "wheel": {
48
48
  "kind": "python-wheel",
49
49
  "name": "unaltraweb-mcp",
50
- "version": "0.6.0",
51
- "release": "v0.6.0",
50
+ "version": "0.7.0",
51
+ "release": "v0.7.0",
52
52
  "release_status": "ready",
53
- "reference": "unaltraweb-mcp==0.6.0",
53
+ "reference": "unaltraweb-mcp==0.7.0",
54
54
  "repository": "https://github.com/dosquartsdedocs/unaltraweb",
55
55
  "included_in_wheel": true,
56
56
  "features": ["calibre-import", "doctor", "new-web", "offline-inspection", "mcp-control-plane"]
@@ -58,10 +58,10 @@
58
58
  "runtime": {
59
59
  "kind": "container",
60
60
  "name": "unaltraweb runtime",
61
- "version": "0.6.0",
62
- "release": "v0.6.0",
61
+ "version": "0.7.0",
62
+ "release": "v0.7.0",
63
63
  "release_status": "ready",
64
- "reference": "ghcr.io/dosquartsdedocs/unaltraweb:0.6.0",
64
+ "reference": "ghcr.io/dosquartsdedocs/unaltraweb:0.7.0",
65
65
  "image_repository": "ghcr.io/dosquartsdedocs/unaltraweb",
66
66
  "repository": "https://github.com/dosquartsdedocs/unaltraweb",
67
67
  "included_in_wheel": false,
@@ -70,10 +70,10 @@
70
70
  "mcp": {
71
71
  "kind": "container",
72
72
  "name": "unaltraweb MCP runtime",
73
- "version": "0.6.0",
74
- "release": "v0.6.0",
73
+ "version": "0.7.0",
74
+ "release": "v0.7.0",
75
75
  "release_status": "ready",
76
- "reference": "ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.6.0",
76
+ "reference": "ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.7.0",
77
77
  "image_repository": "ghcr.io/dosquartsdedocs/unaltraweb-mcp",
78
78
  "repository": "https://github.com/dosquartsdedocs/unaltraweb",
79
79
  "included_in_wheel": false,
@@ -130,10 +130,10 @@
130
130
  "diavisuals": {
131
131
  "kind": "companion",
132
132
  "name": "diavisuals",
133
- "version": "0.4.0",
134
- "release": "v0.4.0",
133
+ "version": "0.5.0",
134
+ "release": "v0.5.0",
135
135
  "release_status": "released",
136
- "reference": "https://github.com/dosquartsdedocs/diavisuals/releases/download/v0.4.0/diavisuals-0.4.0-py3-none-any.whl#sha256=bfedcc9e2554f25ce4a1c33e556f352210848fccb8da800d1c3900dd86c48c93",
136
+ "reference": "https://github.com/dosquartsdedocs/diavisuals/releases/download/v0.5.0/diavisuals-0.5.0-py3-none-any.whl#sha256=f52e20f20f1fb3a2418f07adb8d6a68a5567d0c321fb7c0ccc0f4db04917b96b",
137
137
  "repository": "https://github.com/dosquartsdedocs/diavisuals",
138
138
  "included_in_wheel": false,
139
139
  "features": ["mermaid", "plantuml"]
@@ -141,10 +141,10 @@
141
141
  "vegavisuals": {
142
142
  "kind": "companion",
143
143
  "name": "vegavisuals",
144
- "version": "0.4.0",
145
- "release": "v0.4.0",
144
+ "version": "0.5.1",
145
+ "release": "v0.5.1",
146
146
  "release_status": "released",
147
- "reference": "https://github.com/dosquartsdedocs/vegavisuals/releases/download/v0.4.0/vegavisuals-0.4.0-py3-none-linux_x86_64.whl#sha256=b52ffa743643dd6b5e0320e7a9aa0cd500ea06262b7d5098c0c3f94a379bc0ea",
147
+ "reference": "https://github.com/dosquartsdedocs/vegavisuals/releases/download/v0.5.1/vegavisuals-0.5.1-py3-none-linux_x86_64.whl#sha256=5aba4f267a37179c520b3cd164e1cec62ac5561872aa33b069cce73442dc52b1",
148
148
  "repository": "https://github.com/dosquartsdedocs/vegavisuals",
149
149
  "included_in_wheel": false,
150
150
  "features": ["vega", "vega-lite"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unaltraweb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dosquartsdedocs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-02 00:00:00.000000000 Z
11
+ date: 2026-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: classifier-reborn
@@ -782,6 +782,7 @@ files:
782
782
  - docs/_documentation/en/43-workspace-path-policies.md
783
783
  - docs/_documentation/en/44-editorial-review.md
784
784
  - docs/_documentation/en/45-retained-documents.md
785
+ - docs/_documentation/en/46-live-runtime.md
785
786
  - docs/_documentation/en/50-faq.md
786
787
  - docs/_pages/ca/index.md
787
788
  - docs/_pages/en/index.md
@@ -799,9 +800,11 @@ files:
799
800
  - docs/agents/action-prompts/50-bibliography-entry.txt
800
801
  - docs/agents/action-prompts/60-bibliometrics-refresh.txt
801
802
  - docs/agents/action-prompts/70-build-and-review.txt
803
+ - docs/agents/d0-runtime-identity.md
802
804
  - docs/agents/manual-authoring-components.md
803
805
  - docs/agents/mcp-contract.md
804
806
  - docs/agents/owner-closeout-76.md
807
+ - docs/agents/owner-closeout-80.md
805
808
  - docs/agents/owner-followup-76.md
806
809
  - docs/agents/owner-preparation-2026-09-29.md
807
810
  - docs/agents/quarto-computation-adoption.md