unaltraweb 0.3.0 → 0.4.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 +4 -4
- data/Makefile +57 -17
- data/README.md +35 -14
- data/docs/_documentation/en/02-tools.md +2 -2
- data/docs/_documentation/en/03-usage.md +1 -1
- data/docs/_documentation/en/06-github-web-editing.md +1 -1
- data/docs/_documentation/en/13-unaltremanual.md +11 -4
- data/docs/_documentation/en/31-template.md +1 -1
- data/docs/_documentation/en/32-development.md +1 -1
- data/docs/_documentation/en/40-distribution.md +39 -22
- data/docs/_documentation/en/42-docker-image.md +20 -10
- data/docs/agents/action-prompts/00-start-site-session.txt +9 -7
- data/docs/agents/mcp-contract.md +12 -6
- data/scripts/manual/templates/manual.tex +4 -0
- data/scripts/test_reproducible_jekyll_build.py +1 -1
- data/scripts/test_wheel_install.py +47 -5
- data/scripts/unaltraweb-mcp-bootstrap.sh +19 -1
- data/scripts/validate_distribution.py +1 -1
- data/scripts/validate_workflows.py +290 -5
- data/scripts/verify_package_publish.py +414 -0
- data/scripts/web_captures/render.py +1 -1
- data/src/unaltraweb_mcp/component-contract.json +29 -29
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f06f4a02759cbf9874ee57a4a15f26f4eb0f5247bad7778b04811b47f7d0cbce
|
|
4
|
+
data.tar.gz: 8ca81f6f199be8023e4651efe8886499ef67ecf49fbcac6f202a760d0dbfbbcc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e3950450a0339c08473ec1ff3dd1edcdfb5336982990a441c80c94928b9e606bb51a5f4247fa8e09ab89a7621e56debeac4e479b98cdadbb68081caac7253552
|
|
7
|
+
data.tar.gz: 7253948b9cb8adf42e43cdf45b92c67ed179b3c84d35ceccd280e883727369573e3e01e787b9df98eaf3970f5446b20be2bb17b3e0368f843da36106de9a43d1
|
data/Makefile
CHANGED
|
@@ -1,8 +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.
|
|
5
|
-
MCP_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.
|
|
4
|
+
MCP_RUNTIME_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.4.0
|
|
5
|
+
MCP_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.4.0
|
|
6
|
+
MCP_RELEASE_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:f3ab5542e6ece56487d4b8238a5e7abd89f36a0b8d87bf7bab19645e3ced1e58
|
|
6
7
|
MCP_DOCKER_BUILD_NETWORK ?= default
|
|
7
8
|
INIT_SITE_PROFILE ?= unaltreselfie
|
|
8
9
|
NEW_WEB_PROFILE ?= unaltreselfie
|
|
@@ -35,22 +36,35 @@ WEB_CAPTURE_DOCKER_NETWORK ?=
|
|
|
35
36
|
WEB_CAPTURE_SERVICE_HOST ?=
|
|
36
37
|
WEB_CAPTURE_CONFIRM_OVERWRITE ?= 0
|
|
37
38
|
WEB_CAPTURE_SCRIPT := $(CURDIR)/scripts/web_captures/render.py
|
|
38
|
-
WEB_CAPTURE_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-web-capture:
|
|
39
|
+
WEB_CAPTURE_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-web-capture@sha256:0bf1bc67fe63e1440bffe708a168beefa11c54441650a871ab99380d362f7c1e
|
|
39
40
|
WEB_CAPTURE_DEV_IMAGE ?= unaltraweb-web-capture:dev
|
|
40
41
|
WEB_CAPTURE_DOCKER_BUILD_NETWORK ?= default
|
|
41
42
|
VEGAVISUALS_CLI ?=
|
|
42
|
-
DOCKER_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.
|
|
43
|
-
MANUAL_PDF_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-manual-pdf:0.
|
|
43
|
+
DOCKER_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.4.0
|
|
44
|
+
MANUAL_PDF_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-manual-pdf:0.4.0
|
|
44
45
|
MANUAL_PDF_DEV_IMAGE ?= unaltraweb-manual-pdf:dev
|
|
46
|
+
MCP_SMOKE_MANUAL_PDF_IMAGE ?= $(MANUAL_PDF_IMAGE)
|
|
45
47
|
MANUAL_PDF_LANG ?=
|
|
46
48
|
MANUAL_PDF_PUBLISH_DRY_RUN ?= 1
|
|
49
|
+
MANUAL_PDF_CONFIRM_PUBLISH ?= 0
|
|
50
|
+
MANUAL_PDF_PUBLICATION_INTENT_SHA256 ?=
|
|
51
|
+
MANUAL_PDF_PREVIEW_CLEAN_DRY_RUN ?= 1
|
|
52
|
+
MANUAL_PDF_PREVIEW_CONFIRM_CLEAN ?= 0
|
|
53
|
+
MANUAL_PDF_PREVIEW_RECEIPT_SHA256 ?=
|
|
47
54
|
MANUAL_RELEASE_SELECTOR ?= latest
|
|
48
55
|
MANUAL_RELEASE_DRY_RUN ?= 1
|
|
49
56
|
MANUAL_RELEASE_CONFIRM_PREPARE ?= 0
|
|
50
57
|
override MANUAL_RELEASE_SELECTOR := $(value MANUAL_RELEASE_SELECTOR)
|
|
51
58
|
override MANUAL_RELEASE_DRY_RUN := $(value MANUAL_RELEASE_DRY_RUN)
|
|
52
59
|
override MANUAL_RELEASE_CONFIRM_PREPARE := $(value MANUAL_RELEASE_CONFIRM_PREPARE)
|
|
53
|
-
|
|
60
|
+
override MANUAL_PDF_PREVIEW_CLEAN_DRY_RUN := $(value MANUAL_PDF_PREVIEW_CLEAN_DRY_RUN)
|
|
61
|
+
override MANUAL_PDF_PREVIEW_CONFIRM_CLEAN := $(value MANUAL_PDF_PREVIEW_CONFIRM_CLEAN)
|
|
62
|
+
override MANUAL_PDF_PREVIEW_RECEIPT_SHA256 := $(value MANUAL_PDF_PREVIEW_RECEIPT_SHA256)
|
|
63
|
+
override MANUAL_PDF_LANG := $(value MANUAL_PDF_LANG)
|
|
64
|
+
override MANUAL_PDF_PUBLISH_DRY_RUN := $(value MANUAL_PDF_PUBLISH_DRY_RUN)
|
|
65
|
+
override MANUAL_PDF_CONFIRM_PUBLISH := $(value MANUAL_PDF_CONFIRM_PUBLISH)
|
|
66
|
+
override MANUAL_PDF_PUBLICATION_INTENT_SHA256 := $(value MANUAL_PDF_PUBLICATION_INTENT_SHA256)
|
|
67
|
+
export MANUAL_RELEASE_SELECTOR MANUAL_PDF_LANG MANUAL_PDF_PUBLISH_DRY_RUN MANUAL_PDF_CONFIRM_PUBLISH MANUAL_PDF_PUBLICATION_INTENT_SHA256 MANUAL_PDF_PREVIEW_CLEAN_DRY_RUN MANUAL_PDF_PREVIEW_CONFIRM_CLEAN MANUAL_PDF_PREVIEW_RECEIPT_SHA256
|
|
54
68
|
UNALTRAWEB_WORKER_ROLE ?=
|
|
55
69
|
UNALTRAWEB_WORKER_PROJECT ?=
|
|
56
70
|
UNALTRAWEB_WORKER_TOKEN ?=
|
|
@@ -79,8 +93,8 @@ ifneq ($(strip $(SCIMAGO_INPUT)),)
|
|
|
79
93
|
SCIMAGO_ARGS += --input "$(SCIMAGO_INPUT)"
|
|
80
94
|
endif
|
|
81
95
|
|
|
82
|
-
.PHONY: distribution-check distribution-release-check distribution-doctor workflow-check wheel-check gem-check reproducible-site-check docs-build docs-serve docs-publish docs-down metrics-scimago-fetch metrics-update metrics-update-all metrics-check manual-pdf-image manual-pdf-image-dev manual-pdf-preflight manual-pdf-status manual-pdf-check manual-pdf-build manual-pdf-publish manual-pdf-sync manual-release-status manual-release-check manual-release-prepare manual-compute-status manual-compute-check manual-compute-render manual-compute-render-figures manual-compute-image-python manual-compute-image-r manual-compute-images manual-compute-rstudio compute-base-image-python compute-base-image-r web-capture-status web-capture-check web-capture-render web-capture-image visualization-status visualization-check visualization-render
|
|
83
|
-
.PHONY: mcp-runtime-image mcp-image mcp-build mcp-check mcp-smoke mcp-smoke-prebuilt mcp-stdio mcp-down mcp-down-all mcp-list-tools mcp-starter-templates mcp-new-web mcp-initialize-site mcp-site-context mcp-profile-check mcp-manual-source-quality-check mcp-manual-editorial-quality-check mcp-manual-authoring-capabilities mcp-manual-computation-status mcp-manual-computation-check mcp-manual-computation-render mcp-manual-computation-render-figures mcp-web-capture-status mcp-web-capture-check mcp-web-capture-render mcp-manual-pdf-status mcp-manual-pdf-build mcp-manual-pdf-publish mcp-manual-release-status mcp-manual-release-check mcp-manual-release-prepare mcp-profile-prune-plan mcp-profile-prune mcp-content-inventory mcp-language-policy mcp-content-approval-inventory mcp-translation-plan mcp-bibliography-inventory mcp-bibliometrics-check mcp-build-health
|
|
96
|
+
.PHONY: distribution-check distribution-release-check distribution-doctor workflow-check wheel-check gem-check reproducible-site-check docs-build docs-serve docs-publish docs-down metrics-scimago-fetch metrics-update metrics-update-all metrics-check manual-pdf-image manual-pdf-image-dev manual-pdf-preflight manual-pdf-status manual-pdf-check manual-pdf-build manual-pdf-publish manual-pdf-publish-worker manual-pdf-sync manual-release-status manual-release-check manual-release-prepare manual-compute-status manual-compute-check manual-compute-render manual-compute-render-figures manual-compute-image-python manual-compute-image-r manual-compute-images manual-compute-rstudio compute-base-image-python compute-base-image-r web-capture-status web-capture-check web-capture-render web-capture-image visualization-status visualization-check visualization-render
|
|
97
|
+
.PHONY: mcp-runtime-image mcp-image mcp-build mcp-check mcp-smoke mcp-smoke-prebuilt mcp-stdio mcp-down mcp-down-all mcp-list-tools mcp-starter-templates mcp-new-web mcp-initialize-site mcp-site-context mcp-profile-check mcp-manual-source-quality-check mcp-manual-editorial-quality-check mcp-manual-authoring-capabilities mcp-manual-computation-status mcp-manual-computation-check mcp-manual-computation-render mcp-manual-computation-render-figures mcp-web-capture-status mcp-web-capture-check mcp-web-capture-render mcp-manual-pdf-status mcp-manual-pdf-build mcp-manual-pdf-preview-prepare mcp-manual-pdf-preview-clean mcp-manual-pdf-publish mcp-manual-release-status mcp-manual-release-check mcp-manual-release-prepare mcp-profile-prune-plan mcp-profile-prune mcp-content-inventory mcp-language-policy mcp-content-approval-inventory mcp-translation-plan mcp-bibliography-inventory mcp-bibliometrics-check mcp-build-health
|
|
84
98
|
|
|
85
99
|
REPOSITORY_CONTEXT_TARGETS := distribution-doctor manual-compute-status manual-compute-check manual-compute-render manual-compute-render-figures manual-compute-image-python manual-compute-image-r manual-compute-images manual-compute-rstudio web-capture-status web-capture-check web-capture-render visualization-status visualization-check visualization-render manual-pdf-preflight manual-pdf-status manual-pdf-check manual-pdf-build manual-pdf-publish manual-pdf-sync manual-release-status manual-release-check manual-release-prepare metrics-scimago-fetch metrics-update metrics-update-all metrics-check
|
|
86
100
|
$(REPOSITORY_CONTEXT_TARGETS): override PROJECT = $${MCP_CONSUMER_WORKSPACE:-$$PWD}
|
|
@@ -107,19 +121,24 @@ gem-check: ## Build the gem and verify its package-owned contract files
|
|
|
107
121
|
reproducible-site-check: ## Build the same fixed-epoch Jekyll fixture twice and compare every output
|
|
108
122
|
@DOCKER_IMAGE="$(DOCKER_IMAGE)" $(PYTHON) scripts/test_reproducible_jekyll_build.py
|
|
109
123
|
|
|
110
|
-
mcp-runtime-image
|
|
124
|
+
mcp-runtime-image mcp-image mcp-check mcp-smoke: MCP_RUNTIME_IMAGE = unaltraweb:dev
|
|
125
|
+
mcp-runtime-image mcp-image mcp-check mcp-smoke: MCP_IMAGE = unaltraweb-mcp:dev
|
|
126
|
+
mcp-smoke: MCP_SMOKE_MANUAL_PDF_IMAGE = $(MANUAL_PDF_DEV_IMAGE)
|
|
127
|
+
|
|
128
|
+
mcp-runtime-image: ## Build the local development Jekyll runtime used by the MCP
|
|
111
129
|
docker build --network "$(MCP_DOCKER_BUILD_NETWORK)" -t "$(MCP_RUNTIME_IMAGE)" .
|
|
112
130
|
|
|
113
|
-
mcp-image: mcp-runtime-image ## Build the
|
|
131
|
+
mcp-image: mcp-runtime-image ## Build the local development FastMCP control plane
|
|
114
132
|
docker build --network "$(MCP_DOCKER_BUILD_NETWORK)" --build-arg "UNALTRAWEB_RUNTIME_IMAGE=$(MCP_RUNTIME_IMAGE)" -t "$(MCP_IMAGE)" -f Dockerfile.mcp .
|
|
115
133
|
|
|
116
|
-
mcp-build:
|
|
134
|
+
mcp-build: ## Prepare the reviewed release image used by MCP sessions, builds, and previews
|
|
135
|
+
@docker image inspect "$(MCP_RELEASE_IMAGE)" >/dev/null 2>&1 || docker pull "$(MCP_RELEASE_IMAGE)" >/dev/null
|
|
117
136
|
|
|
118
137
|
mcp-check: mcp-image ## Verify the Dockerized MCP CLI contract
|
|
119
138
|
docker run --rm --entrypoint unaltraweb-mcp "$(MCP_IMAGE)" version
|
|
120
139
|
|
|
121
|
-
mcp-smoke: mcp-
|
|
122
|
-
@$(MAKE) --silent --no-print-directory mcp-smoke-prebuilt MCP_IMAGE="$(MCP_IMAGE)"
|
|
140
|
+
mcp-smoke: mcp-image manual-pdf-image-dev ## Build and prove a real MCP stdio connection
|
|
141
|
+
@$(MAKE) --silent --no-print-directory mcp-smoke-prebuilt MCP_IMAGE="$(MCP_IMAGE)" MCP_SMOKE_MANUAL_PDF_IMAGE="$(MCP_SMOKE_MANUAL_PDF_IMAGE)"
|
|
123
142
|
|
|
124
143
|
mcp-smoke-prebuilt: ## Prove a real MCP stdio connection using the selected prebuilt MCP image
|
|
125
144
|
docker run --rm --user "$(LOCAL_UID):$(LOCAL_GID)" -e HOME=/tmp --entrypoint python3 "$(MCP_IMAGE)" /opt/unaltraweb/test/mcp_smoke.py
|
|
@@ -128,16 +147,18 @@ mcp-smoke-prebuilt: ## Prove a real MCP stdio connection using the selected preb
|
|
|
128
147
|
image_id=$$(docker image inspect --format '{{.Id}}' "$(MCP_IMAGE)"); \
|
|
129
148
|
socket_mount=$$(/bin/sh "$(DOCKER_MOUNT_SCRIPT)" "$$docker_socket" /var/run/docker.sock); \
|
|
130
149
|
project_mount=$$(/bin/sh "$(DOCKER_MOUNT_SCRIPT)" "$(CURDIR)/tmp/mcp-preview-smoke" /workspace); \
|
|
150
|
+
mirror_mount=$$(/bin/sh "$(DOCKER_MOUNT_SCRIPT)" "$(CURDIR)/tmp/mcp-preview-smoke" "$(CURDIR)/tmp/mcp-preview-smoke"); \
|
|
131
151
|
docker run --rm --user "$(LOCAL_UID):$(LOCAL_GID)" --group-add "$$socket_group" \
|
|
132
152
|
-e HOME=/tmp -e "UNALTRAWEB_DOCKER_ROOT=$(CURDIR)/tmp/mcp-preview-smoke" \
|
|
133
153
|
-e "UNALTRAWEB_PROJECT_USER=$(LOCAL_UID):$(LOCAL_GID)" -e "UNALTRAWEB_MCP_IMAGE=$$image_id" \
|
|
154
|
+
-e "MANUAL_PDF_IMAGE=$(MCP_SMOKE_MANUAL_PDF_IMAGE)" \
|
|
134
155
|
--mount "$$socket_mount" \
|
|
135
|
-
--mount "$$project_mount" -w /workspace \
|
|
156
|
+
--mount "$$project_mount" --mount "$$mirror_mount" -w /workspace \
|
|
136
157
|
--entrypoint python3 "$$image_id" /opt/unaltraweb/test/mcp_preview_smoke.py
|
|
137
158
|
|
|
138
159
|
mcp-stdio: ## Serve MCP_CONSUMER_WORKSPACE through the Dockerized stdio MCP
|
|
139
160
|
@test -n "$${MCP_CONSUMER_WORKSPACE:-}" || { printf '%s\n' 'MCP_CONSUMER_WORKSPACE is required' >&2; exit 2; }
|
|
140
|
-
@exec "$(CURDIR)/scripts/unaltraweb-mcp-bootstrap.sh" --image "$(
|
|
161
|
+
@exec "$(CURDIR)/scripts/unaltraweb-mcp-bootstrap.sh" --image "$(MCP_RELEASE_IMAGE)"
|
|
141
162
|
|
|
142
163
|
mcp-down: ## Remove MCP resources owned by MCP_CONSUMER_WORKSPACE or MCP_PROJECT_ID
|
|
143
164
|
@exec "$(CURDIR)/scripts/unaltraweb-mcp-cleanup.sh"
|
|
@@ -202,6 +223,12 @@ mcp-manual-pdf-status: ## Inspect manual PDF state for PROJECT
|
|
|
202
223
|
mcp-manual-pdf-build: ## Build the manual PDF for PROJECT
|
|
203
224
|
@PYTHONPATH="$(CURDIR)/src" $(PYTHON) -m unaltraweb_mcp.cli --project "$(PROJECT)" mcp manual-pdf-build --language "$(MANUAL_PDF_LANG)"
|
|
204
225
|
|
|
226
|
+
mcp-manual-pdf-preview-prepare: ## Stage ignored PDF and cover copies for local Jekyll review
|
|
227
|
+
@PYTHONPATH="$(CURDIR)/src" $(PYTHON) -m unaltraweb_mcp.cli --project "$(PROJECT)" mcp manual-pdf-preview-prepare
|
|
228
|
+
|
|
229
|
+
mcp-manual-pdf-preview-clean: ## Dry-run cleanup of receipt-owned PDF preview copies
|
|
230
|
+
@PYTHONPATH="$(CURDIR)/src" $(PYTHON) -m unaltraweb_mcp.cli --project "$(PROJECT)" mcp manual-pdf-preview-clean $(if $(filter 0 false FALSE no NO n N,$(MANUAL_PDF_PREVIEW_CLEAN_DRY_RUN)),--apply,) $(if $(filter 1 true TRUE yes YES y Y,$(MANUAL_PDF_PREVIEW_CONFIRM_CLEAN)),--confirm-clean,) --expected-receipt-sha256 "$${MANUAL_PDF_PREVIEW_RECEIPT_SHA256}"
|
|
231
|
+
|
|
205
232
|
mcp-manual-pdf-publish: ## Dry-run manual PDF publication for PROJECT
|
|
206
233
|
@PYTHONPATH="$(CURDIR)/src" $(PYTHON) -m unaltraweb_mcp.cli --project "$(PROJECT)" mcp manual-pdf-publish --language "$(MANUAL_PDF_LANG)"
|
|
207
234
|
|
|
@@ -365,10 +392,23 @@ manual-pdf-check: ## Reject stale or unpublished manual PDF artefacts without Do
|
|
|
365
392
|
manual-pdf-build: manual-pdf-preflight ## Build manual PDFs and cover previews under tmp
|
|
366
393
|
$(call run_manual_pdf_worker,build)
|
|
367
394
|
|
|
368
|
-
manual-pdf-publish:
|
|
395
|
+
manual-pdf-publish-worker: ## Internal worker target used by provenance-aware publication
|
|
396
|
+
@if test -z "$(filter 1 true TRUE yes YES y Y,$(MANUAL_PDF_PUBLISH_DRY_RUN))"; then \
|
|
397
|
+
test "$(MANUAL_PDF_CONFIRM_PUBLISH)" = 1 || { printf '%s\n' 'Real publication workers require MANUAL_PDF_CONFIRM_PUBLISH=1.' >&2; exit 2; }; \
|
|
398
|
+
test -n "$(UNALTRAWEB_WORKER_TOKEN)" || { printf '%s\n' 'Real publication workers must be launched by the provenance-aware controller.' >&2; exit 2; }; \
|
|
399
|
+
test -n "$(MANUAL_PDF_PUBLICATION_INTENT_SHA256)" || { printf '%s\n' 'Real publication workers require a controller publication intent.' >&2; exit 2; }; \
|
|
400
|
+
PYTHONPATH="$(CURDIR)/src" /usr/bin/python3 -c 'import sys; from pathlib import Path; from unaltraweb_mcp.manual_pdf_preview import validate_publication_worker; validate_publication_worker(Path(sys.argv[1]), sys.argv[2], sys.argv[3])' "$(PROJECT_ROOT)" "$(MANUAL_PDF_PUBLICATION_INTENT_SHA256)" "$(MANUAL_PDF_LANG)"; \
|
|
401
|
+
fi
|
|
402
|
+
@$(MAKE) --silent --no-print-directory manual-pdf-preflight
|
|
403
|
+
@if test -z "$(filter 1 true TRUE yes YES y Y,$(MANUAL_PDF_PUBLISH_DRY_RUN))"; then \
|
|
404
|
+
PYTHONPATH="$(CURDIR)/src" /usr/bin/python3 -c 'import sys; from pathlib import Path; from unaltraweb_mcp.manual_pdf_preview import validate_publication_worker; validate_publication_worker(Path(sys.argv[1]), sys.argv[2], sys.argv[3])' "$(PROJECT_ROOT)" "$(MANUAL_PDF_PUBLICATION_INTENT_SHA256)" "$(MANUAL_PDF_LANG)"; \
|
|
405
|
+
fi
|
|
369
406
|
$(call run_manual_pdf_worker,publish,$(if $(filter 1 true TRUE yes YES y Y,$(MANUAL_PDF_PUBLISH_DRY_RUN)),--dry-run,))
|
|
370
407
|
|
|
371
|
-
manual-pdf-
|
|
408
|
+
manual-pdf-publish: ## Copy built PDF artefacts through provenance-aware MCP orchestration
|
|
409
|
+
@PYTHONPATH="$(CURDIR)/src" $(PYTHON) -m unaltraweb_mcp.cli --project "$(PROJECT_ROOT)" mcp manual-pdf-publish --language "$${MANUAL_PDF_LANG}" --release-selector "$${MANUAL_RELEASE_SELECTOR}" $(if $(filter 0 false FALSE no NO n N,$(MANUAL_PDF_PUBLISH_DRY_RUN)),--apply,) $(if $(filter 1 true TRUE yes YES y Y,$(MANUAL_PDF_CONFIRM_PUBLISH)),--confirm-publish,)
|
|
410
|
+
|
|
411
|
+
manual-pdf-sync: manual-pdf-preflight ## Build and copy only changed manual PDFs; disabled configurations remain a no-op
|
|
372
412
|
$(call run_manual_pdf_worker,sync)
|
|
373
413
|
|
|
374
414
|
define run_manual_release_worker
|
data/README.md
CHANGED
|
@@ -26,17 +26,20 @@ See [Edit Safely In GitHub Web](docs/_documentation/en/06-github-web-editing.md)
|
|
|
26
26
|
|
|
27
27
|
## Technical Overview
|
|
28
28
|
|
|
29
|
-
The core packages shared layouts, includes, Sass, assets, Jekyll plugins, bibliometric tooling, multilingual behaviour, theme modes and reusable GitHub Actions workflows. Child sites
|
|
29
|
+
The core packages shared layouts, includes, Sass, assets, Jekyll plugins, bibliometric tooling, multilingual behaviour, theme modes and reusable GitHub Actions workflows. Child sites stay thin: they keep content, configuration and local assets while reusable implementation remains in this repository.
|
|
30
|
+
|
|
31
|
+
The default distribution is Docker-first. Generated sites run their normal build, serve and test commands in the published `unaltraweb-mcp` image, which contains the Python control plane and the reviewed core at `/opt/unaltraweb`. The `unaltraweb` gem on RubyGems and the `unaltraweb-mcp` wheel on PyPI remain supported native interoperability channels, not additional requirements for the Docker path.
|
|
30
32
|
|
|
31
33
|
`unaltremanual` sites can also build language-specific PDF editions and matching web-cover images in an isolated Pandoc/XeLaTeX container. PDF status is offline; build and local review are available through Make and the MCP control plane. When PDF output is enabled, default generated PDF and cover outputs are not versioned. The selected `latest` or stable selector is rendered into both the website and PDF metadata.
|
|
32
34
|
|
|
33
35
|
## Current Status
|
|
34
36
|
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
- The
|
|
37
|
+
- Public release `v0.3.0` provides the Docker images, Ruby gem and Python wheel as one receipt-bound distribution.
|
|
38
|
+
- `ghcr.io/dosquartsdedocs/unaltraweb-mcp` is the canonical normal local runtime for generated sites; `ghcr.io/dosquartsdedocs/unaltraweb` is its lower-level Jekyll runtime base.
|
|
39
|
+
- The Ruby gem supports native Bundler/Jekyll consumers. The modular MCP wheel supports native creation and inspection, and does not bundle the gem, factory checkout, worker images or companion renderers.
|
|
40
|
+
- PDF, browser-capture and computation environments remain separate images so the normal site image does not carry every heavy toolchain.
|
|
38
41
|
- The companion `../unaltraweb-template` repository remains the full-profile integration fixture and visual demo.
|
|
39
|
-
- The project is
|
|
42
|
+
- The project is in its early `0.x` release series. Some inherited `al-folio` implementation details remain while the core is being generalized.
|
|
40
43
|
|
|
41
44
|
## Repository Roles
|
|
42
45
|
|
|
@@ -65,7 +68,19 @@ unaltraweb:
|
|
|
65
68
|
|
|
66
69
|
## Quick Start
|
|
67
70
|
|
|
68
|
-
Create a child site with the
|
|
71
|
+
Create a child site directly with the public Docker image:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
mkdir my-site && \
|
|
75
|
+
docker run --rm --network none --user "$(id -u):$(id -g)" -e HOME=/tmp \
|
|
76
|
+
--mount "type=bind,src=${PWD}/my-site,dst=/workspace" \
|
|
77
|
+
ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:f3ab5542e6ece56487d4b8238a5e7abd89f36a0b8d87bf7bab19645e3ced1e58 \
|
|
78
|
+
--project /workspace new-web --site-profile unaltreselfie --title "My site" --default-lang en
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The chained `mkdir` requires a new destination, the digest binds creation to the reviewed `v0.3.0` receipt, and `--network none` keeps scaffold generation offline.
|
|
82
|
+
|
|
83
|
+
If Python package tooling is already available, the PyPI adapter exposes the equivalent native command:
|
|
69
84
|
|
|
70
85
|
```bash
|
|
71
86
|
unaltraweb-mcp --project ./my-site new-web --site-profile unaltreselfie --title "My site" --default-lang en
|
|
@@ -77,7 +92,7 @@ From this factory checkout, the equivalent command is:
|
|
|
77
92
|
MCP_CONSUMER_WORKSPACE=./my-site make mcp-new-web NEW_WEB_PROFILE=unaltreselfie SITE_TITLE="My site" DEFAULT_LANG=en
|
|
78
93
|
```
|
|
79
94
|
|
|
80
|
-
|
|
95
|
+
All three creation paths use only assets shipped in `unaltraweb_mcp`, either in the image, wheel or factory checkout. They preflight all managed paths, write `.unaltraweb/scaffold.json`, and never overwrite differing files. Later `scaffold_sync` calls can update unchanged baseline controls or adopt exact current package bytes, including the collaboration contract, Dependabot policy, pull-request template, dependency pins, and deploy caller. They never touch config, README prose, agent guidance, or content. `dosquartsdedocs/unaltraweb-template` remains available when a full multi-profile demo with Playwright tests is more useful than a clean profile-specific site.
|
|
81
96
|
|
|
82
97
|
After creation, there are two supported editing paths:
|
|
83
98
|
|
|
@@ -112,11 +127,13 @@ make docs-serve DOCKER_IMAGE=unaltraweb:dev
|
|
|
112
127
|
make docs-build DOCKER_IMAGE=unaltraweb:dev
|
|
113
128
|
```
|
|
114
129
|
|
|
115
|
-
The
|
|
130
|
+
The candidate distribution contract selects `ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.4.0` for normal generated-site commands after coordinated publication. Until that release completes, `v0.3.0` and the digest-pinned `MCP_RELEASE_IMAGE` remain the public runtime. The mutable `:main` channel is reserved for explicit maintainer testing; locally built core images use the `:dev` name.
|
|
131
|
+
|
|
132
|
+
The lower-level `unaltraweb` image supplies Ruby, Jekyll and runtime dependencies. The MCP image layers the full reviewed factory and installed Python package on top; generated Make targets load the theme as a path gem from `/opt/unaltraweb`. Native Bundler consumers can instead resolve the independently published gem, and native Python users can install the wheel.
|
|
116
133
|
|
|
117
134
|
The independent manual PDF runtime is built from `scripts/manual/Dockerfile`; it is deliberately separate from the Jekyll image so normal site builds do not carry Pandoc and TeX Live.
|
|
118
135
|
|
|
119
|
-
|
|
136
|
+
Publish images only through the manual Docker workflows. A core-code change requires a new MCP image, while a base-runtime change also requires a new lower-level runtime image. Candidate builds receive package-write authority only after preflight and never execute their outputs; broad aliases move only after a separate read-only job tests the exact digests, and the final package-write job executes no candidate.
|
|
120
137
|
|
|
121
138
|
When running the core and the template profiles together, keep `unaltraweb` on port `4000` and the template profile servers on `4001` through `4004`.
|
|
122
139
|
|
|
@@ -148,14 +165,16 @@ Doctor is offline. The optional `--docker` mode only calls local Docker version/
|
|
|
148
165
|
|
|
149
166
|
## Global Dockerized MCP
|
|
150
167
|
|
|
151
|
-
`unaltraweb` provides one global, on-demand stdio MCP whose containers are scoped to the current consumer workspace. Each client session gets an independent Docker-generated container name plus stable factory, role, and project labels, so concurrent
|
|
168
|
+
`unaltraweb` provides one global, on-demand stdio MCP whose containers are scoped to the current consumer workspace. Each client session gets an independent Docker-generated container name plus stable factory, role, and project labels, so concurrent processes for the same project do not collide. This runtime capability does not authorize overlapping editors: the collaboration control plane uses one primary mutable checkout and one active editing session per repository. ContExt runs `mcp-build` to prepare the exact public image selected by `MCP_RELEASE_IMAGE`, and `mcp-stdio` launches that same digest. To build and test a development image explicitly from this checkout instead:
|
|
152
169
|
|
|
153
170
|
```bash
|
|
154
|
-
make mcp-
|
|
155
|
-
make mcp-smoke
|
|
171
|
+
make mcp-image
|
|
172
|
+
make mcp-smoke-prebuilt MCP_IMAGE=unaltraweb-mcp:dev
|
|
156
173
|
```
|
|
157
174
|
|
|
158
|
-
|
|
175
|
+
Source builds use the explicit local names `unaltraweb:dev` and `unaltraweb-mcp:dev` by default, avoiding shadowed public references. After each coordinated release, `MCP_RELEASE_IMAGE` advances to its recorded digest in a separate post-release change; candidate source never embeds its unknown future self-digest.
|
|
176
|
+
|
|
177
|
+
ContExt reads the canonical manifest transport `make -C ${factoryRoot} mcp-stdio` and supplies `MCP_CONSUMER_WORKSPACE=${workspaceFolder}` through the process environment. The manifest continues to use `make`, an allowed container host launcher, but no consumer path is parsed by Make or interpolated into shell source. The collaboration control plane requests one top-level MCP, selects its declared dependency closure, preserves unrelated user registrations, and runs a read-only checkout preflight before editing. When a process-held cooperative lease is required, it launches the editing command through its `exec` wrapper; it never manipulates Git worktrees implicitly. An equivalent direct MCP launch is:
|
|
159
178
|
|
|
160
179
|
```bash
|
|
161
180
|
MCP_CONSUMER_WORKSPACE="$PWD" make --silent --no-print-directory -C /path/to/unaltraweb mcp-stdio
|
|
@@ -163,7 +182,7 @@ MCP_CONSUMER_WORKSPACE="$PWD" make --silent --no-print-directory -C /path/to/una
|
|
|
163
182
|
|
|
164
183
|
Replace `/path/to/unaltraweb` with this checkout's absolute path and restart the client after changing its configuration. Each session canonicalizes the inherited workspace after launch, then mounts it at `/workspace` and at its canonical host path, so Docker-backed authoring tools pass valid bind paths to the host daemon. `build_site` runs Jekyll directly in that MCP runtime and returns the offline HTML audit. `preview_start`, `preview_status`, and `preview_stop` manage one labelled preview container per project. By default, Docker publishes container port `4000` on a free loopback host port, so previews from distinct workspaces can run concurrently; pass a nonzero `port` only when a fixed host port is required. A running preview created by the former fixed-port default remains idempotently usable until stopped, after which the dynamic default applies. `http_check` derives its origin only from that owned preview and never accepts an arbitrary URL.
|
|
165
184
|
|
|
166
|
-
Dependency preparation
|
|
185
|
+
Dependency preparation ensures the selected release image and prepares required companions only; it does not initialize a consumer website, and companion `init` aggregation is disabled. Create a site explicitly with the `new_web` MCP tool. To clean up one consumer project, pass the same canonical project path used at launch:
|
|
167
186
|
|
|
168
187
|
```bash
|
|
169
188
|
MCP_CONSUMER_WORKSPACE=/path/to/consumer make mcp-down
|
|
@@ -221,6 +240,8 @@ CodeQL separately analyzes JavaScript/TypeScript, Python and Ruby on pull reques
|
|
|
221
240
|
|
|
222
241
|
Core artifact workflows run `distribution-check` while selected candidates are truthfully `pending`, validate the selected ref against the BOM version, and keep their preflight jobs credential-free. Once the final source and release intent are reviewed, mark its components `ready` and commit that state. Runtime, MCP, and manual PDF publication separates authority across a signing/package-write build job that never runs candidates, a read-only test job that verifies GitHub-signed digest/source provenance and removes GHCR credentials before execution, and a package-write promotion job that executes no candidate. Every image is built once under only its SHA tag; only the exact digests that pass all Ruby, PDF, reproducibility, MCP, and docs gates can reach verified `sha-*`, `main`, and `latest` aliases. GHCR does not make the initial absence lookup and later tag write atomic, so the signed tested digest, not a claim of compare-and-swap no-clobber, is the trust anchor. Record those digests and package checksums in a versioned `release-candidates.json` child commit; validation requires that receipt to be the only change. After `distribution-release-check`, tag the receipt commit. The tag-only job verifies SHA-tag equality, signed provenance and the revision label against the receipt's `source_commit`, then promotes only the receipt's manifests to checked semver aliases without rebuilding or executing them. `released` remains available for already-published components; released containers other than the self-describing MCP must be digest-pinned. Package preparation never uploads to RubyGems or PyPI, creates a GitHub release, tags the repository or publishes an image; those operations require separate explicit maintainer approval.
|
|
223
242
|
|
|
243
|
+
After tagging, maintainers can use the manual `Publish language packages` workflow with PyPI and RubyGems Trusted Publishing. Its read-only job binds a successful package-preparation run and immutable artifact to the release receipt, verifies the exact file inventory and SHA-256 values, and only then passes one wheel and one gem to separate environment-protected OIDC jobs. Those jobs do not checkout source, rebuild candidates, receive repository write authority, or use stored registry tokens. See [Distribution](docs/_documentation/en/40-distribution.md) for the one-time publisher setup and dispatch inputs.
|
|
244
|
+
|
|
224
245
|
## Attribution
|
|
225
246
|
|
|
226
247
|
`unaltraweb` started from the open-source `al-folio` Jekyll theme and is being refactored into a self-owned reusable core for `dosquartsdedocs` sites. Retain upstream attribution where inherited code remains relevant.
|
|
@@ -27,13 +27,13 @@ nav_title: Local Tools
|
|
|
27
27
|
|
|
28
28
|
## Local Runtime
|
|
29
29
|
|
|
30
|
-
The template uses the shared runtime image by default:
|
|
30
|
+
The currently published integration template uses the shared `v0.3.0` runtime image by default:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
ghcr.io/dosquartsdedocs/unaltraweb:0.3.0
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
That image provides Ruby, Bundler, Jekyll system dependencies, ImageMagick, Node for ExecJS and Python tooling needed by local commands. The GHCR package must be public before unauthenticated users can pull it.
|
|
36
|
+
That image provides Ruby, Bundler, Jekyll system dependencies, ImageMagick, Node for ExecJS and Python tooling needed by local commands. The `v0.4.0` candidate source selects matching `0.4.0` runtime and MCP images for package-generated sites, but consumers must not adopt those references until coordinated publication completes. The GHCR package must be public before unauthenticated users can pull it.
|
|
37
37
|
|
|
38
38
|
## Generated Site Commands
|
|
39
39
|
|
|
@@ -21,7 +21,7 @@ nav_title: Run And Preview
|
|
|
21
21
|
|
|
22
22
|
1. Call `new_web` with one `unaltraweb.site_profile` and the site identity/language settings.
|
|
23
23
|
2. Inspect the generated `_config.yml` and localized home page.
|
|
24
|
-
3. Reserve a focused task, create one branch, and open a small Draft pull request. Never edit `main` directly, and allow only one active
|
|
24
|
+
3. Reserve a focused task, create one branch, and open a small Draft pull request. Never edit `main` directly, and allow only one active editing session per repository; exact file reservations limit scope but do not permit overlap.
|
|
25
25
|
4. Edit content and data files. MCP agents should read a source hash, review the default `site_source_write` dry-run, then apply the exact CAS update.
|
|
26
26
|
5. Stop and ask the maintainer if another task overlaps or a conflict appears.
|
|
27
27
|
6. The maintainer runs `site_doctor`, `profile_check`, `site_check`, `build_site`, and required renderers, then reviews the returned HTML audit and rendered outputs.
|
|
@@ -21,7 +21,7 @@ nav_title: GitHub Web Editing
|
|
|
21
21
|
|
|
22
22
|
1. Open or choose an issue describing one focused task.
|
|
23
23
|
2. Be assigned to it, or post an explicit reservation naming every file you intend to edit and wait for maintainer acceptance.
|
|
24
|
-
3. Check the issue and open pull requests before starting. There must be only one active
|
|
24
|
+
3. Check the issue and open pull requests before starting. There must be only one active editing session per repository; exact file reservations limit scope but do not permit overlap.
|
|
25
25
|
4. Create one branch for the task. Never edit or commit directly to `main`.
|
|
26
26
|
5. Open a Draft pull request early, link the issue, and list the reserved files.
|
|
27
27
|
|
|
@@ -61,7 +61,7 @@ The current publishing contract separates an up-to-date reading site from durabl
|
|
|
61
61
|
- Stable editions use `vYYYY.MM(.N)`: `vYYYY.MM` for the first edition in a month and `vYYYY.MM.N` for an additional edition. They are deferred until a maintainer creates an explicit release, and an ordinary `latest` deployment never creates one.
|
|
62
62
|
- Release checks reject generated `legacy/` or `sandbox/` trees. Keep them outside the current manual content roots.
|
|
63
63
|
|
|
64
|
-
The generated site README and pull request template require one assigned issue or explicit file reservation, one task branch, one active
|
|
64
|
+
The generated site README and pull request template require one assigned issue or explicit file reservation, one task branch, one active editing session per repository, and a small Draft pull request. Reservations limit task scope but do not permit overlapping sessions. If edits overlap or conflict, stop and ask the maintainer rather than choosing a side or recreating files.
|
|
65
65
|
|
|
66
66
|
## Executable Chapters
|
|
67
67
|
|
|
@@ -220,10 +220,13 @@ The PDF workflow checks executable chapters before reading their generated Markd
|
|
|
220
220
|
|
|
221
221
|
```text
|
|
222
222
|
manual_pdf_status
|
|
223
|
-
|
|
223
|
+
manual_pdf_preview_prepare # builds stale latest artifacts and stages ignored copies
|
|
224
|
+
build_site(site_profile="unaltremanual", release_selector="latest")
|
|
225
|
+
preview_start # browser and PDF review
|
|
226
|
+
manual_pdf_preview_clean # dry-run by default
|
|
227
|
+
manual_pdf_preview_clean(dry_run=false, confirm_clean=true, expected_receipt_sha256="<dry-run receipt_sha256>")
|
|
224
228
|
manual_pdf_publish # dry-run by default
|
|
225
229
|
manual_pdf_publish(dry_run=false, confirm_publish=true)
|
|
226
|
-
build_site(site_profile="unaltremanual", release_selector="latest")
|
|
227
230
|
manual_release_status(selector="latest")
|
|
228
231
|
manual_release_prepare(selector="latest") # dry-run
|
|
229
232
|
manual_release_prepare(selector="latest", dry_run=false, confirm_prepare=true)
|
|
@@ -232,6 +235,10 @@ factory: MCP_CONSUMER_WORKSPACE=/path/to/site make manual-pdf-sync
|
|
|
232
235
|
factory: MCP_CONSUMER_WORKSPACE=/path/to/site make manual-pdf-check
|
|
233
236
|
```
|
|
234
237
|
|
|
235
|
-
Builds remain under `tmp/manual-pdf/<lang>/`. Each language build creates both the PDF and a PNG extracted from its first page. The release selector is part of the PDF fingerprint, so every PDF build, site build, and candidate operation in one sequence must use the same selector. The default template derives XeTeX's trailer ID from that fingerprint, and the builder canonicalizes the lossless PDF streams with qpdf, so identical inputs and selector produce byte-identical PDFs. A custom template must put `\special{pdf:trailerid [<$trailer-id$><$trailer-id$>]}` on its first output page to preserve that property.
|
|
238
|
+
Builds remain under `tmp/manual-pdf/<lang>/`. Each language build creates both the PDF and a PNG extracted from its first page. The release selector is part of the PDF fingerprint, so every PDF build, site build, and candidate operation in one sequence must use the same selector. The default template derives XeTeX's trailer ID from that fingerprint, and the builder canonicalizes the lossless PDF streams with qpdf, so identical inputs and selector produce byte-identical PDFs. A custom template must put `\special{pdf:trailerid [<$trailer-id$><$trailer-id$>]}` on its first output page to preserve that property.
|
|
239
|
+
|
|
240
|
+
`manual_pdf_preview_prepare` is a non-publishing `latest` operation. It builds only stale languages, rejects public destinations that are tracked or not ignored, refuses unmanaged existing files, stages each PDF and cover atomically, and records SHA-256 plus file identity under `.cache/unaltraweb/`. Its result always says `publishes: false`. Each public PDF or cover that already matches its generated artifact remains unchanged and unclaimed. Before confirmed publication mutates public files, `.cache/unaltraweb/manual-pdf-publication-intent.json` records expected hashes; successful publication then writes separate non-owning identity provenance to `.cache/unaltraweb/manual-pdf-publication.json`. A later cycle may replace only matching public files, even after `tmp/` was cleaned or publication was interrupted. Preview cleanup never uses publication intent or provenance as deletion authority. Jekyll sees staged or current published files through its normal static-file inventory, so chapter 0 can display the cover and download link. Managed `make build`, `make test`, and `make serve` run preparation automatically in a short-lived controller; that controller can run without a Docker socket when no nested PDF build is needed, and the persistent preview remains socket-free. After review, preview cleanup defaults to dry-run and deletes only unchanged receipt-owned files. Pass that dry-run's `receipt_sha256` as `expected_receipt_sha256` when confirming deletion, which prevents a newly prepared generation from being cleaned under an older review. Make-only cleanup accepts the equivalent `MANUAL_PDF_PREVIEW_CLEAN_DRY_RUN=0`, `MANUAL_PDF_PREVIEW_CONFIRM_CLEAN=1`, and `MANUAL_PDF_PREVIEW_RECEIPT_SHA256=<sha256>` variables. Edited, replaced, mode-changed, tracked, or newly unignored paths are preserved and reported as conflicts. Clean receipt-owned copies before disabling PDF output or real publication; preparation fails closed rather than serving stale files. `make clean` refuses to discard `tmp/` while a preview receipt remains.
|
|
241
|
+
|
|
242
|
+
The default public PDF and cover paths are ignored and remain unversioned; deployment rebuilds them from reviewed source. Preview preparation, cleanup, local publication, and local builds never commit, push, tag, release, or deploy. The download button appears when the generated PDF exists among the built site's static files. Chapters can opt out with `pdf: false`.
|
|
236
243
|
|
|
237
244
|
`manual_release_status` and `manual_release_check` are offline and read-only. `manual_release_prepare` writes only a local evidence bundle below `tmp/manual-release/<selector>/`; it never tags, pushes, deploys, or creates a GitHub release. Stable local candidates are no-clobber while their selector directory exists, but the durable object is the published GitHub tag/release after immutable releases are enabled for the repository, not the disposable `tmp/` directory. A stable sequence must start at the consumer repository root on the exact clean Git commit that will be reviewed on `main`; nested repositories, submodules, Git clean/smudge filters, and versioned PDF outputs are rejected. Start the MCP with the reviewed `ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:<digest>` image rather than a mutable tag. The commit timestamp fixes Jekyll's build time, source and site fingerprints use canonical modes, and the version-2 release manifest records the commit, epoch, and site-build image. Use the same `vYYYY.MM(.N)` selector for PDF build, site build, and candidate preparation, and additionally require approved default-language content, no draft PDF, and a clean editorial quality check. Record the reviewed candidate with `sha256sum tmp/manual-release/<selector>/release-manifest.json`; the stable workflow must reproduce that digest before publication can proceed.
|
|
@@ -21,7 +21,7 @@ nav_title: Core And Template
|
|
|
21
21
|
- It consumes `unaltraweb` as an external dependency.
|
|
22
22
|
- It contains realistic demo content for `unaltreselfie`, `unaltreprojecte`, `unaltremanual` and `unaltredocs` profiles.
|
|
23
23
|
- It exercises richer local Docker and browser-test orchestration than the clean package scaffolds.
|
|
24
|
-
-
|
|
24
|
+
- The currently published fixture uses `ghcr.io/dosquartsdedocs/unaltraweb:0.3.0`; it should align with the `0.4.0` candidate only after coordinated publication, and mutable `main` remains reserved for maintainer testing.
|
|
25
25
|
- It runs Playwright smoke tests and screenshots across profiles, themes and responsive layouts.
|
|
26
26
|
- It keeps rich demo content out of clean profile scaffolds.
|
|
27
27
|
|
|
@@ -65,7 +65,7 @@ docker compose -f docker-compose.yml down --remove-orphans
|
|
|
65
65
|
|
|
66
66
|
This can be resource-heavy because the inherited demo build minifies JavaScript and can generate many responsive WebP images.
|
|
67
67
|
|
|
68
|
-
The
|
|
68
|
+
The currently public base Dockerfile image is `ghcr.io/dosquartsdedocs/unaltraweb:0.3.0`. Published generated consumers select the higher-level `ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.3.0`, which adds the reviewed core and Python control plane. The `v0.4.0` candidate checkout selects matching `0.4.0` images, but downstream consumers must remain on `v0.3.0` until coordinated publication completes. Their Make targets use `/opt/unaltraweb` as a path gem; the RubyGems package remains the optional native Bundler channel. ContExt prepares the full post-release `MCP_RELEASE_IMAGE` digest with `mcp-build`, while `mcp-image`, `mcp-check` and `mcp-smoke` reserve local `:dev` names for source testing. Mutable `:main` channels remain explicit maintainer paths.
|
|
69
69
|
|
|
70
70
|
The root core build excludes `docs/`. The reference site is published from the `docs/` folder through a dedicated workflow so its root-relative permalinks do not collide with the inherited core demo build.
|
|
71
71
|
|
|
@@ -29,9 +29,25 @@ The template is the better place to validate gem consumption, centralized styles
|
|
|
29
29
|
|
|
30
30
|
The BOM is an interoperability contract, not a bundle. The wheel contains only its Python control/inspection modules, schema/BOM, and clean package-owned scaffolds. In particular it does not contain Ruby theme assets, Docker image layers, factory Make/scripts/docs, TeX, Chromium, computation environments, `diavisuals`, or `vegavisuals`.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
The selected candidate release is `0.4.0`; `v0.3.0` remains the public distribution until coordinated publication finishes. The BOM reuses immutable compute and web-capture worker digests and selects the published `diavisuals v0.3.1` and `vegavisuals v0.3.1` releases. Current checkouts can be used through `suggested_path`, while immutable release references remain the distribution contract. `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 the final same-commit candidates is `ready`, while an already-published component is `released`.
|
|
33
33
|
|
|
34
|
-
##
|
|
34
|
+
## Docker-First Hybrid Policy
|
|
35
|
+
|
|
36
|
+
GHCR is the canonical delivery channel for normal local use. The candidate package scaffold selects `ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.4.0`, which must not be adopted by consumers until coordinated publication completes; published `v0.3.0` sites remain supported during preparation. 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.
|
|
37
|
+
|
|
38
|
+
Factory registration uses a stricter pin. ContExt 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.
|
|
39
|
+
|
|
40
|
+
The distribution keeps native channels for interoperability rather than making them Docker prerequisites:
|
|
41
|
+
|
|
42
|
+
- `ghcr.io/dosquartsdedocs/unaltraweb` is the lower-level Ruby/Jekyll runtime used to build the MCP image.
|
|
43
|
+
- `ghcr.io/dosquartsdedocs/unaltraweb-mcp` is the self-contained normal site and MCP runtime.
|
|
44
|
+
- RubyGems publishes the same Jekyll core as a native Bundler adapter for consumers that do not use the Docker path.
|
|
45
|
+
- PyPI publishes the small scaffold, control and inspection plane for native Python, `pip` or `uv` use.
|
|
46
|
+
- Dedicated GHCR workers keep Chromium, TeX and computation environments out of the normal site image.
|
|
47
|
+
|
|
48
|
+
These remain real package boundaries: the MCP image installs the Python package and uses the core through Ruby's gem interface. The policy only makes their public registry installation optional for Docker users. It does not combine all toolchains into one image or duplicate worker layers in the wheel or gem.
|
|
49
|
+
|
|
50
|
+
## Optional Wheel And Doctor
|
|
35
51
|
|
|
36
52
|
A clean `unaltraweb-mcp` wheel works without a factory checkout for `version`, `new-web`, top-level `doctor`, constrained source management, scaffold synchronization, `site-doctor`, HTML audit, and pure inspection where feasible. Examples include `mcp list-tools`, `starter-templates`, `detect-site`, `site-context`, `profile-check`, content/language/bibliography inventories, and `build-health`.
|
|
37
53
|
|
|
@@ -49,7 +65,7 @@ Factory-backed MCP serving, site preflight/build, computations, captures, PDF op
|
|
|
49
65
|
|
|
50
66
|
### GitHub-only editing
|
|
51
67
|
|
|
52
|
-
Users can push a site created by `new_web` and edit small content changes in the GitHub web UI. The generated README requires an assigned issue or accepted file reservation, one task branch, one active
|
|
68
|
+
Users can push a site created by `new_web` and edit small content changes in the GitHub web UI. The generated README requires an assigned issue or accepted file reservation, one task branch, one active editing session per repository, and a small Draft pull request. A reservation limits scope rather than permitting overlapping sessions. Editors never change `main` directly; they stop and ask a maintainer when work overlaps or conflicts.
|
|
53
69
|
|
|
54
70
|
This path is intended for small content edits, bibliography updates, course/manual chapter edits and configuration changes. It does not require Docker, Make or a local development environment. A maintainer checks and renders the branch locally, reviews it, merges it, and only then starts deployment manually.
|
|
55
71
|
|
|
@@ -68,6 +84,8 @@ make down
|
|
|
68
84
|
|
|
69
85
|
Local editing requires Git, Docker and GNU Make. On Windows, use WSL2 with Docker Desktop and run the same commands inside the WSL Linux shell.
|
|
70
86
|
|
|
87
|
+
The normal targets use the selected MCP image, including its reviewed core at `/opt/unaltraweb`. The committed `Gemfile` and native package entry points remain available for GitHub deployment, integration testing and environments that deliberately choose a non-Docker path; local Docker editing does not need a sibling core checkout or registry package installation.
|
|
88
|
+
|
|
71
89
|
Theme development can happen side by side by pointing the template at a local core checkout:
|
|
72
90
|
|
|
73
91
|
```bash
|
|
@@ -94,35 +112,24 @@ Repositories created from a GitHub template are not linked to the template as fo
|
|
|
94
112
|
|
|
95
113
|
For that reason:
|
|
96
114
|
|
|
97
|
-
- normal improvements should ship through
|
|
115
|
+
- normal local-runtime improvements should ship through a versioned MCP image; native consumers receive corresponding gem or wheel releases when their package boundary changes;
|
|
98
116
|
- site repositories can enable Dependabot for Bundler and GitHub Actions, but deploy workflows should remain manual;
|
|
99
117
|
- breaking changes should be released with migration notes;
|
|
100
118
|
- scaffold changes should be rare; generated sites can explicitly dry-run `scaffold_sync`, which updates only unchanged baseline runtime files (including the pull-request template), creates newly managed missing files, reports conflicts, never deletes paths, stages every output, rechecks adopted and unchanged files around the manifest write, rolls the whole transaction back on failure, and commits its manifest last. Generated README prose is site-owned and is not overwritten by synchronization.
|
|
101
119
|
|
|
102
120
|
## Docker Runtime
|
|
103
121
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
The same candidate rule applies to workers. The Python and R computation workers are already released and selected by immutable digest; the gem, wheel, runtime, MCP, web-capture worker, and manual PDF worker remain truthfully `pending`. The final reviewed source commit marks only candidate components `ready` before default-branch publication. For runtime, MCP, and manual PDF, `build-candidates` builds each image once under only its SHA tag, builds MCP from the runtime digest, and creates a GitHub-signed build-provenance attestation for each exact digest while retaining BuildKit SBOM and maximum provenance. It never runs those images. `test-candidates` has only read permissions, verifies registry-stored attestations against the exact repository, workflow, and source commit, pulls and checks all revision labels, logs out of GHCR, and then runs every Ruby test, all manual PDF integrations, reproducibility, MCP smoke, and docs against digest references without exposing `GH_TOKEN`. Only its successful digest outputs reach `promote-candidates`, which has package write but executes no candidate and points `sha-*`, `main`, and `latest` to those digests without rebuilding. A failure can leave a reviewable SHA candidate but cannot move broad aliases.
|
|
122
|
+
Release `0.3.0` publishes the selected base runtime, MCP runtime and 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`.
|
|
107
123
|
|
|
108
|
-
The
|
|
124
|
+
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.
|
|
109
125
|
|
|
110
|
-
|
|
126
|
+
Candidate publication remains credential-separated. `build-candidates` builds each image once under only its SHA tag and creates GitHub-signed provenance. A read-only job verifies provenance and revision labels, removes GHCR credentials, and executes the test suite against exact digests. A final package-write job executes no candidate and promotes only tested manifests. The signed source-bound digest, rather than a mutable alias, remains the evidence recorded in `release-candidates.json`.
|
|
111
127
|
|
|
112
|
-
-
|
|
113
|
-
- Docker image updates change the local build/runtime environment.
|
|
128
|
+
The pre-build absence lookup is not an atomic no-clobber guarantee: GHCR exposes separate read and tag-write operations, not compare-and-swap. A package administrator could race the lookup or later retagging. Immediate equality checks and post-promotion verification bound that risk, while the signed, source-bound digest tested by the read-only job remains the evidence to record. An immediately following `release-candidates.json` child commit records immutable image digests and package checksums while changing no other path. Validation requires the parent to belong to the default branch, requires each image digest to use the component's declared GHCR repository, and checks exact package names. Before semver promotion, the tag-only job proves that each source commit's SHA tag still resolves to the recorded digest, verifies its GitHub-signed attestation with `receipt.source_commit` as the source digest, and requires the image revision label to equal that commit. It then uses `docker buildx imagetools create` to add and verify semver aliases without rebuilding, executing, or trusting a mutable source tag. Excluding the receipt from image and package contents avoids an impossible self-digest for the MCP. Publication workflows require the reviewed manual PDF image by full digest. Separate worker images keep Chromium, TeX, and computation environments out of ordinary site and wheel installs.
|
|
114
129
|
|
|
115
|
-
|
|
130
|
+
For the Docker-first path, reusable layouts, styles, plugins and scripts travel inside the reviewed MCP image at `/opt/unaltraweb`; generated Make targets expose that directory as a path gem. The independently published gem carries the same reusable core for native Bundler consumers. The wheel provides native bootstrap and inspection but does not carry the theme or worker images.
|
|
116
131
|
|
|
117
|
-
-
|
|
118
|
-
- Run the image and package-preparation workflows from that exact default-branch commit to publish `sha-<full-commit>` images and prepare the gem/wheel candidates without creating a release tag. For runtime, MCP, and manual PDF, preserve the workflow summary that pairs the source commit with all three signed and read-only-tested digests; `main` and `latest` move only from the dependent promotion job after those tests pass.
|
|
119
|
-
- Add only `release-candidates.json` in the next commit, recording the parent source SHA, immutable image references, package basenames and SHA-256 checksums.
|
|
120
|
-
- Run `make distribution-release-check`, create the exact release tag on the receipt commit, then run the image workflows from the tag to promote the recorded manifests to semver aliases.
|
|
121
|
-
- Open the `ghcr.io/dosquartsdedocs/unaltraweb` package settings in GitHub.
|
|
122
|
-
- Make the package public.
|
|
123
|
-
- Confirm that `docker pull ghcr.io/dosquartsdedocs/unaltraweb:0.3.0` works without `docker login`.
|
|
124
|
-
- Make the `ghcr.io/dosquartsdedocs/unaltraweb-mcp` package public after its first publication.
|
|
125
|
-
- Confirm that `docker pull ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.3.0` works without `docker login`.
|
|
132
|
+
The `v0.3.0` first-publish checklist is complete: all selected GHCR packages are public, anonymous pulls work, Trusted Publishing delivered the exact gem and wheel to RubyGems and PyPI, anonymous installations matched the receipt, and the GitHub Release archives both package files plus `SHA256SUMS`. Do not rebuild, republish or retag that version.
|
|
126
133
|
|
|
127
134
|
The receipt uses a full source commit and different evidence by component kind. This abbreviated example shows both forms; the real `components` object must contain exactly every component marked `ready`:
|
|
128
135
|
|
|
@@ -143,7 +150,7 @@ The receipt uses a full source commit and different evidence by component kind.
|
|
|
143
150
|
}
|
|
144
151
|
```
|
|
145
152
|
|
|
146
|
-
|
|
153
|
+
The receipt example describes future publication verification; changing the contract does not publish, tag, or release any artifact.
|
|
147
154
|
|
|
148
155
|
## CI And Release Gates
|
|
149
156
|
|
|
@@ -153,6 +160,16 @@ Automatic `.github/workflows/ci.yml` uses `distribution-check`. It validates sch
|
|
|
153
160
|
|
|
154
161
|
The manual `.github/workflows/package-prepare.yml` builds and checks the gem and wheel, writes `SHA256SUMS`, and uploads an immutable workflow artifact whose name includes the source commit SHA. It does not call RubyGems, PyPI or GitHub Releases. Passing CI, preparing candidates, and later passing the strict coordinated-release gate are evidence for a release, not authorization to publish: tagging, starting a credentialed image workflow, uploading language packages and creating a GitHub release remain separate explicit maintainer approvals.
|
|
155
162
|
|
|
163
|
+
After a coordinated tag exists, `.github/workflows/package-publish.yml` provides the separately approved optional language-package operation without long-lived registry secrets. The repository uses these one-time identities, first configured for `v0.3.0`:
|
|
164
|
+
|
|
165
|
+
- GitHub environment `pypi`, allowed only from the default branch; PyPI Trusted Publisher project `unaltraweb-mcp`, owner `dosquartsdedocs`, repository `unaltraweb`, workflow `package-publish.yml`, environment `pypi`.
|
|
166
|
+
- GitHub environment `rubygems`, allowed only from the default branch; RubyGems Trusted Publisher gem `unaltraweb`, owner `dosquartsdedocs`, repository `unaltraweb`, workflow `package-publish.yml`, environment `rubygems`.
|
|
167
|
+
- Add required environment reviewers and prevent self-review when the maintainer topology allows another person to approve. A sole maintainer should still require the exact reviewed `publisher_sha` input and inspect the run before allowing each environment deployment.
|
|
168
|
+
|
|
169
|
+
Dispatch the workflow only from the default branch. Supply its current reviewed full commit as `publisher_sha`, the exact `vX.Y.Z` receipt tag, the annotated tag object SHA, the SHA-256 of `release-candidates.json`, the successful `Prepare package candidates` run ID, and `all`. The unprivileged job checks that request before checkout and uses only the reviewed publisher verifier: it never installs or executes code from the release tag. That verifier requires the annotated tag object and receipt hash supplied by the maintainer, checks the receipt-only tag target against its first parent and default-branch ancestry, validates the release-ready component inventory, and binds the selected run to `.github/workflows/package-prepare.yml`, the default branch, the receipt source commit, a successful conclusion, and the one expected unexpired artifact. It then rejects extra files, verifies both package hashes and the exact `SHA256SUMS` contents, and uploads one wheel and one gem as separate same-run artefacts. Only the dependent environment jobs receive `id-token: write`; they download those verified files without checking out source and exchange GitHub's OIDC identity for short-lived registry credentials. They receive no repository write permission and never build or execute candidates. The PyPI job runs the reviewed official publisher image by immutable OCI digest rather than its mutable generated image tag; the RubyGems job uses the hosted runner's preinstalled `gem` command instead of downloading a toolchain after receiving OIDC authority.
|
|
170
|
+
|
|
171
|
+
If one registry succeeds and the other fails, rerun against `pypi` or `rubygems` only with the same tag object SHA, receipt SHA-256, and package run ID; immutable registries reject duplicate versions. After both package pages and anonymous installations match the receipt checksums, create the GitHub Release with the preserved gem, wheel, and `SHA256SUMS`. Package publication never moves the coordinated tag, creates a release, or deploys a consumer site.
|
|
172
|
+
|
|
156
173
|
## Verification
|
|
157
174
|
|
|
158
175
|
Core changes should be validated in two layers:
|