unaltraweb 0.3.0 → 0.5.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 +47 -14
- data/_plugins/figure_captions.rb +47 -10
- data/_sass/_documentation.scss +7 -5
- data/_sass/_manual.scss +7 -0
- data/docs/_documentation/en/02-tools.md +4 -4
- data/docs/_documentation/en/03-usage.md +62 -1
- data/docs/_documentation/en/06-github-web-editing.md +1 -1
- data/docs/_documentation/en/13-unaltremanual.md +12 -5
- data/docs/_documentation/en/20-syntax.md +14 -0
- data/docs/_documentation/en/25-caption-credits.md +120 -0
- data/docs/_documentation/en/26-image-backgrounds.md +103 -0
- 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 +60 -23
- data/docs/_documentation/en/42-docker-image.md +21 -11
- data/docs/_documentation/en/43-workspace-path-policies.md +232 -0
- data/docs/_documentation/en/44-editorial-review.md +237 -0
- data/docs/agents/action-prompts/00-start-site-session.txt +14 -7
- data/docs/agents/action-prompts/22-manual-style-audit.txt +3 -1
- data/docs/agents/manual-authoring-components.md +38 -0
- data/docs/agents/mcp-contract.md +112 -14
- data/docs/agents/visual-companions-0.4.0.md +74 -0
- data/docs/assets/img/caption-credits-demo.svg +19 -0
- data/scripts/editorial_check.py +12 -0
- data/scripts/image_background_check.py +12 -0
- data/scripts/manual/build_pdf.py +94 -16
- data/scripts/manual/filters/figure-captions.lua +65 -10
- data/scripts/manual/templates/manual.tex +6 -1
- data/scripts/test_gem_build.py +32 -2
- data/scripts/test_reproducible_jekyll_build.py +1 -1
- data/scripts/test_wheel_install.py +75 -5
- data/scripts/unaltraweb-mcp-bootstrap.sh +19 -1
- data/scripts/validate_distribution.py +19 -4
- 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 +37 -37
- data/src/unaltraweb_mcp/editorial.py +495 -0
- data/src/unaltraweb_mcp/editorial_sources.py +504 -0
- data/src/unaltraweb_mcp/image_backgrounds.py +334 -0
- data/src/unaltraweb_mcp/image_probe.py +149 -0
- data/src/unaltraweb_mcp/processes.py +146 -0
- metadata +16 -2
|
@@ -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.
|
|
@@ -88,6 +88,67 @@ The generated repository contains one profile. Create a separate temporary site
|
|
|
88
88
|
- Reusable workflow updates change optional GitHub build and deploy behavior.
|
|
89
89
|
- Package scaffold changes affect newly generated sites. Existing generated repositories can review `scaffold_sync`, which manages exactly `.gitignore`, `.unaltraweb/docker-mount.sh`, `.github/CONTRIBUTING.md`, `.github/dependabot.yml`, `Makefile`, `Gemfile`, `Gemfile.lock`, `.github/pull_request_template.md`, and `.github/workflows/deploy.yml` from `.unaltraweb/scaffold.json`. It can adopt bytes that already equal the current package payload without rewriting the file. A conflict aborts the entire apply, and retired entries are removed from the baseline without deleting project files. Synchronization never changes site-owned README/agent guidance, config, or content.
|
|
90
90
|
|
|
91
|
+
### Guided Consumer Updates
|
|
92
|
+
|
|
93
|
+
At the start of a site session, the agent inspects `site_context.update_status`.
|
|
94
|
+
The MCP compares the consumer's literal MCP/gem pins and managed baseline with
|
|
95
|
+
the package active in this session. It reports the target version and integration
|
|
96
|
+
tuple, planned paths, preserved customizations, conflicts and a `plan_sha256`.
|
|
97
|
+
This is an offline check, not a query for the newest version on GitHub or PyPI.
|
|
98
|
+
A change to documentation or the factory discovery manifest alone does not imply
|
|
99
|
+
that the consumer needs updating.
|
|
100
|
+
|
|
101
|
+
When changes are available, the agent explains them and asks whether to proceed.
|
|
102
|
+
After acceptance and the normal issue, branch and path reservation, it calls:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
scaffold_sync(dry_run=false, confirm_sync=true, expected_plan_sha256=<reviewed plan_sha256>)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The equivalent native CLI flow is:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
unaltraweb-mcp --project . mcp site-context
|
|
112
|
+
unaltraweb-mcp --project . mcp scaffold-sync
|
|
113
|
+
unaltraweb-mcp --project . mcp scaffold-sync --apply --confirm-sync --expected-plan-sha256 <reviewed-sha256>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The confirmation is bound to that consumer, package proposal, configuration and
|
|
117
|
+
managed-file snapshot. If the plan changes, inspect it again before confirming.
|
|
118
|
+
After applying, inspect context again, run `site_check`, then `build_site` or the
|
|
119
|
+
appropriate local build. Manual PDF review still uses managed preparation and
|
|
120
|
+
cleanup. The update does not commit, publish or regenerate editorial artefacts.
|
|
121
|
+
Declining the offer leaves the repository untouched and lets content work continue.
|
|
122
|
+
|
|
123
|
+
An extra local ignore rule need not block a runtime-pin update: if the incoming
|
|
124
|
+
`.gitignore` still equals its recorded baseline, synchronization preserves the
|
|
125
|
+
customized file and its original package baseline. The same rule applies to other
|
|
126
|
+
managed files. If both the local file and the incoming package changed, there is
|
|
127
|
+
a genuine conflict; no file is overwritten and the entire apply is blocked.
|
|
128
|
+
New-path collisions and missing/unsafe baselines also need review. Known newer
|
|
129
|
+
consumer versions are never silently downgraded by an older MCP. Custom image
|
|
130
|
+
digests and non-literal version expressions are not guessed or resolved online.
|
|
131
|
+
|
|
132
|
+
The planner and transaction are deterministic Python code. For the same consumer
|
|
133
|
+
path, package/BOM, configuration, baseline and managed bytes, they produce the same
|
|
134
|
+
plan digest and intended control-file bytes. No model generates the replacement
|
|
135
|
+
files. Diagnostic timestamps such as `site_context.generated_at`, temporary names
|
|
136
|
+
and filesystem timestamps are not reproducibility guarantees of the plan.
|
|
137
|
+
|
|
138
|
+
The agent initiates the check, presents its result, asks for acceptance and
|
|
139
|
+
coordinates the normal Git/review workflow. An automatic offer therefore depends
|
|
140
|
+
on the agent following the session instructions; the MCP does not force a popup
|
|
141
|
+
or run a background update. The CLI commands above use the same engine without
|
|
142
|
+
an agent. Reconciliation of a genuine conflict is a separate reviewed change,
|
|
143
|
+
after which the engine computes a new plan.
|
|
144
|
+
|
|
145
|
+
Existing consumers receive this flow
|
|
146
|
+
when they reconnect to an MCP release that includes it; their site-owned AGENTS
|
|
147
|
+
and README do not need to be replaced. Publishing that new MCP/package release
|
|
148
|
+
and advancing its reviewed runtime pin remain maintainer actions. An already
|
|
149
|
+
running v0.4.0 image will not gain the feature simply because the factory checkout
|
|
150
|
+
was updated.
|
|
151
|
+
|
|
91
152
|
## Safe MCP Editing
|
|
92
153
|
|
|
93
154
|
`site_source_read`, `site_source_write`, and `site_source_delete` are deliberately restricted to `_config.yml`, Markdown/HTML content collections, YAML/JSON/CSV below `_data/`, and Markdown below `context/`. They do not expose generic filesystem access and cannot mutate workflows, runtime files, core overrides, bibliography, assets, or generated output. Writes default to dry-run and use SHA-256 optimistic concurrency; destructive deletes additionally require explicit confirmation and can never remove `_config.yml`.
|
|
@@ -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
|
|
|
@@ -166,7 +166,7 @@ Deleting or disabling a source leaves its lock record and generated artifacts as
|
|
|
166
166
|
|
|
167
167
|
Static Vega-Lite and Vega figures use `*.vl.json` and `*.vg.json` sources declared in `.vegavisuals.yml`. A chapter references the specification as a captioned Markdown image; Jekyll and the PDF builder resolve it to the same single manifest output without changing the caption or figure attributes. Prefer SVG when the figure must work identically on the web and in print.
|
|
168
168
|
|
|
169
|
-
Use the required companion `vegavisuals` MCP and its `initialize_project`, `visualization_status`, `render_visualizations`, and `visualization_check` tools for the source-to-output lifecycle.
|
|
169
|
+
Use the required companion `vegavisuals` MCP and its `initialize_project`, `visualization_status`, `render_visualizations`, and `visualization_check` tools for the source-to-output lifecycle. gContExt registers the companion separately rather than copying it into the site. Run `visualization_check` before PDF status, build, or publication when a manifest exists; the unaltraweb PDF tool does not proxy the separate MCP. Commit the manifest, lock, provider receipt, source data, and generated outputs together.
|
|
170
170
|
|
|
171
171
|
## PDF Edition
|
|
172
172
|
|
|
@@ -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.
|
|
@@ -102,6 +102,16 @@ For pages with figure captions enabled, use the Markdown image title as the capt
|
|
|
102
102
|

|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
+
Separate attribution from the description with `data-caption-source`:
|
|
106
|
+
|
|
107
|
+
```markdown
|
|
108
|
+
{: data-caption-source="Source: verified dataset. Credits: map creator."}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The label/number, description and credits have separate web elements. The source
|
|
112
|
+
continues inline with differentiated styling; PDF lists include the description
|
|
113
|
+
without its credits. See the [rendered caption and credit examples]({{ '/caption-credits/' | relative_url }}).
|
|
114
|
+
|
|
105
115
|
To narrow the space assigned to one figure without changing its height, set a
|
|
106
116
|
CSS width with `data-figure-width`. The figure remains centred and cannot exceed
|
|
107
117
|
the available width. The PDF builder maps `rem` values against its 60-rem
|
|
@@ -137,6 +147,10 @@ Manual chapters can number teaching tables with localized labels:
|
|
|
137
147
|
:::
|
|
138
148
|
```
|
|
139
149
|
|
|
150
|
+
Tables accept the same field on the opening line:
|
|
151
|
+
`::: table "Weekly work rhythm" {: data-caption-source="Source: course plan."}`.
|
|
152
|
+
Existing captions remain valid and are not automatically split.
|
|
153
|
+
|
|
140
154
|
Cells may contain inline code spans such as `` `=SUM(A2:A10)` ``. The manual renderer preserves these as inline `<code>` elements on the web; if a consumer project still shows literal backticks inside numbered tables, update its `unaltraweb` lock file or use explicit HTML `<code>` as a temporary compatibility fallback.
|
|
141
155
|
|
|
142
156
|
## Diagram Sources
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Figure And Table Captions With Credits
|
|
3
|
+
description: Separate automatic numbering, descriptive captions and source credits, with concise PDF indexes.
|
|
4
|
+
lang: en
|
|
5
|
+
ref: caption_credits
|
|
6
|
+
profiles: [unaltredocs]
|
|
7
|
+
documentation_profiles: [github-publishers, local-authors, site-designers, contributors]
|
|
8
|
+
section: Design And Customize
|
|
9
|
+
weight: 315
|
|
10
|
+
permalink: "/caption-credits/"
|
|
11
|
+
nav_title: Captions And Credits
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
A caption has three distinct parts: its automatic label and number, the
|
|
15
|
+
description of the figure or table, and optional source or creator credits.
|
|
16
|
+
Credits remain part of the visible caption but use a smaller italic style. In a
|
|
17
|
+
manual PDF, only the number and description enter the list of figures or tables.
|
|
18
|
+
|
|
19
|
+
## Figure
|
|
20
|
+
|
|
21
|
+
Keep the descriptive caption in the Markdown image title and add
|
|
22
|
+
`data-caption-source` to its attribute block. Alternative text describes the
|
|
23
|
+
image independently of its caption and credits.
|
|
24
|
+
|
|
25
|
+
```markdown
|
|
26
|
+
{: data-figure-width-web="34rem" data-figure-width-pdf="78%" data-caption-source="Source: original schematic for this example. Credits: unaltraweb."}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
{: data-figure-width-web="34rem" data-figure-width-pdf="78%" data-caption-source="Source: original schematic for this example. Credits: unaltraweb."}
|
|
30
|
+
|
|
31
|
+
The label receives the numbering style, the description is normal caption text,
|
|
32
|
+
and the source continues inline in its own styled span. Normal wrapping can move
|
|
33
|
+
the source onto the next line; it is not a separate numbered item.
|
|
34
|
+
|
|
35
|
+
## Table
|
|
36
|
+
|
|
37
|
+
Use the same attribute after the opening caption. This example contains
|
|
38
|
+
synthetic values solely to illustrate the presentation.
|
|
39
|
+
|
|
40
|
+
```markdown
|
|
41
|
+
::: table "Example values" {: data-caption-source="Source: synthetic demonstration data. Credits: unaltraweb."}
|
|
42
|
+
| Element | Value |
|
|
43
|
+
| --- | ---: |
|
|
44
|
+
| A | 10 |
|
|
45
|
+
| B | 25 |
|
|
46
|
+
| C | 40 |
|
|
47
|
+
:::
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
::: table "Example values" {: data-caption-source="Source: synthetic demonstration data. Credits: unaltraweb."}
|
|
51
|
+
| Element | Value |
|
|
52
|
+
| --- | ---: |
|
|
53
|
+
| A | 10 |
|
|
54
|
+
| B | 25 |
|
|
55
|
+
| C | 40 |
|
|
56
|
+
:::
|
|
57
|
+
|
|
58
|
+
## Links And Attribution
|
|
59
|
+
|
|
60
|
+
The credit field accepts inline Markdown, including links and emphasis, and the
|
|
61
|
+
existing bibliography citation syntax. Supply the label appropriate to the
|
|
62
|
+
content language, such as “Source”, “Font”, “Fuente” or “Credits”. Attribution
|
|
63
|
+
must describe the actual origin and licence of the material.
|
|
64
|
+
|
|
65
|
+
::: table "Caption components" {: data-caption-source="Credits: [unaltraweb](https://github.com/dosquartsdedocs/unaltraweb), documentation example."}
|
|
66
|
+
| Component | Purpose |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| Label and number | Automatically identify the figure or table |
|
|
69
|
+
| Description | Explain what the reader should observe |
|
|
70
|
+
| Source or credits | Identify origin, authorship or licence |
|
|
71
|
+
:::
|
|
72
|
+
|
|
73
|
+
Use single quotes around the attribute if the credit text contains literal double
|
|
74
|
+
quotes. Liquid citations such as `{% raw %}{% cite verifiedKey %}{% endraw %}`
|
|
75
|
+
follow the usual verified-bibliography workflow. Keep credits in the attribute,
|
|
76
|
+
rather than concatenating them into the descriptive title.
|
|
77
|
+
|
|
78
|
+
## Multi-Panel Figures
|
|
79
|
+
|
|
80
|
+
The attribute can credit the whole group or an individual panel:
|
|
81
|
+
|
|
82
|
+
```markdown
|
|
83
|
+
::: subfigures a+b "Two views of the same schematic" {: data-caption-source="Source: original demonstration schematic."}
|
|
84
|
+
{: data-caption-source="Credits: unaltraweb."}
|
|
85
|
+

|
|
86
|
+
:::
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
::: subfigures a+b "Two views of the same schematic" {: data-caption-source="Source: original demonstration schematic."}
|
|
90
|
+
{: data-caption-source="Credits: unaltraweb."}
|
|
91
|
+

|
|
92
|
+
:::
|
|
93
|
+
|
|
94
|
+
## Existing Captions
|
|
95
|
+
|
|
96
|
+
Existing captions remain valid. The renderer does not guess where a source starts
|
|
97
|
+
in an old caption, since words such as “source” can be part of the description.
|
|
98
|
+
Move attribution into `data-caption-source` explicitly to obtain separate styling
|
|
99
|
+
and shorter PDF index entries.
|
|
100
|
+
|
|
101
|
+
::: table "A caption without separate credits"
|
|
102
|
+
| Element | Category |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| A | Initial |
|
|
105
|
+
| B | Intermediate |
|
|
106
|
+
:::
|
|
107
|
+
|
|
108
|
+
## PDF Index Entries
|
|
109
|
+
|
|
110
|
+
For the figure above, the list of figures contains **Three linked stages**. Its
|
|
111
|
+
source and creator attribution remain beside the full caption in the chapter.
|
|
112
|
+
For the first table, the list of tables contains **Example values**, without the
|
|
113
|
+
synthetic-data attribution. Figure/table numbering continues to follow the
|
|
114
|
+
manual's normal chapter numbering.
|
|
115
|
+
|
|
116
|
+
The web exposes `.figlabel`, `.md-caption-text` and `.md-caption-source` for these
|
|
117
|
+
roles. The PDF renderer preserves inline formatting and links in the full caption
|
|
118
|
+
and supplies a description-only short caption to LaTeX. These semantics apply to
|
|
119
|
+
normal images, diagrams and generated figures referenced through the same image
|
|
120
|
+
syntax; source generation and freshness remain owned by their respective tools.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Check Image Backgrounds
|
|
3
|
+
description: Read-only transparency warnings for raster images and self-contained SVG figures.
|
|
4
|
+
lang: en
|
|
5
|
+
ref: image_backgrounds
|
|
6
|
+
profiles: [unaltredocs]
|
|
7
|
+
documentation_profiles: [local-authors, site-designers, contributors, core-developers]
|
|
8
|
+
section: Design And Customize
|
|
9
|
+
weight: 318
|
|
10
|
+
permalink: "/image-backgrounds/"
|
|
11
|
+
nav_title: Image Backgrounds
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Publication images should have an opaque background chosen for their content.
|
|
15
|
+
Any opaque colour is valid. A transparent PNG can be just as difficult to read
|
|
16
|
+
as a transparent SVG when enlarged over the website, so the check examines image
|
|
17
|
+
content rather than assuming that one filename extension is safe.
|
|
18
|
+
|
|
19
|
+
`image_background_check` is an **advisory**. It reports transparent and
|
|
20
|
+
unverifiable images, without modifying files, painting them white, changing
|
|
21
|
+
their dimensions or turning a style warning into a publication failure.
|
|
22
|
+
|
|
23
|
+
## What Is Checked
|
|
24
|
+
|
|
25
|
+
- PNG, including palette and RGB `tRNS` transparency, and supported JPEG, GIF,
|
|
26
|
+
WebP, BMP, TIFF and AVIF images are decoded with Pillow. A PNG with an alpha
|
|
27
|
+
channel whose values are all fully opaque passes the check.
|
|
28
|
+
- Animated raster images are inspected frame by frame within a fixed budget.
|
|
29
|
+
A transparent frame is reported even if the first frame is opaque.
|
|
30
|
+
- Self-contained SVG and SVGZ images are rasterised with CairoSVG into a bounded
|
|
31
|
+
viewport, with no supplied background colour. This samples the asset's own
|
|
32
|
+
background; a CSS background behind the image in the page does not count.
|
|
33
|
+
- SVG embedded PNG/JPEG captures are supported. External resource loading is
|
|
34
|
+
disabled. Dynamic SVG, masks, filters and unsupported resources are reported
|
|
35
|
+
as unverifiable rather than assumed opaque.
|
|
36
|
+
|
|
37
|
+
SVG inspection is a raster sample at a maximum side of 1536 pixels, not proof of
|
|
38
|
+
opacity at every possible scale or under every SVG renderer. Raster inspection
|
|
39
|
+
allows up to 16 megapixels per frame, 32 frames and 64 million frame-pixels in
|
|
40
|
+
total. Files are limited to 32 MiB (8 MiB of SVG text), with a bounded project
|
|
41
|
+
batch, decoder CPU/memory limits and timeouts. Oversized, malformed, missing or
|
|
42
|
+
unsupported images produce explicit diagnostics.
|
|
43
|
+
|
|
44
|
+
## Source And Rendered Views
|
|
45
|
+
|
|
46
|
+
The default check inspects image references in published Markdown/HTML source,
|
|
47
|
+
public image metadata and configured content collections. Code examples and
|
|
48
|
+
comments are skipped. The source resolver follows the declared output of
|
|
49
|
+
computation, capture, Mermaid/PlantUML and Vega references, prefers the supported
|
|
50
|
+
author-edited override, and selects maintained language variants using the
|
|
51
|
+
normal default-language fallback.
|
|
52
|
+
|
|
53
|
+
The rendered-output check inspects HTML image references, including `srcset`,
|
|
54
|
+
posters and icon links. It catches assets contributed by layouts or metadata
|
|
55
|
+
that a static source projection cannot resolve. Remote, data-URL and
|
|
56
|
+
fragment-selected references are not fetched or silently certified; inspect a
|
|
57
|
+
local, self-contained output or review the exact rendered view.
|
|
58
|
+
|
|
59
|
+
Findings identify the image path and referring document, include the inspected
|
|
60
|
+
file hash when available, and name the authoritative source for generated
|
|
61
|
+
figures. Repeated references to one asset are grouped.
|
|
62
|
+
|
|
63
|
+
## Commands And MCP
|
|
64
|
+
|
|
65
|
+
These consumer commands are available from the modular wheel:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
unaltraweb-mcp --project /path/to/site mcp image-background-check
|
|
69
|
+
unaltraweb-mcp --project /path/to/site mcp image-background-check --source assets/img/map.svg
|
|
70
|
+
unaltraweb-mcp --project /path/to/site mcp image-background-check --source _chapters/en/maps.md
|
|
71
|
+
unaltraweb-mcp --project /path/to/site mcp image-background-check --output-folder _site
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The MCP tool is `image_background_check`, and the source report is also available
|
|
75
|
+
at `web://image-backgrounds`. `site_check` includes the source check;
|
|
76
|
+
`html_audit` adds the rendered check; the MCP PDF build returns source-image
|
|
77
|
+
advisories. CLI checks print image warnings to stderr, so the package scaffold's
|
|
78
|
+
normal build still surfaces them when its JSON output is redirected.
|
|
79
|
+
|
|
80
|
+
The reusable deployment workflow runs the packaged native checker before build
|
|
81
|
+
and against the output folder before upload. A native gem installation can use:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
python /path/to/unaltraweb/scripts/image_background_check.py --project /path/to/site --output-folder _site
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The checker requires Pillow, CairoSVG and the system Cairo library. The MCP image
|
|
88
|
+
and native deployment workflow install them. A missing decoder is reported as
|
|
89
|
+
unverifiable. A completed check with transparency warnings exits successfully;
|
|
90
|
+
an invalid inspection request or source inventory returns a nonzero exit status.
|
|
91
|
+
|
|
92
|
+
## Fixing A Warning
|
|
93
|
+
|
|
94
|
+
Choose a background colour in the authoritative source or export settings. For
|
|
95
|
+
example, Matplotlib exports can set `facecolor` and `transparent=False`, and
|
|
96
|
+
`ggsave` can set `bg`. An SVG can use an opaque full-viewport background shape
|
|
97
|
+
behind its artwork. Check the chosen text/background contrast on web and PDF.
|
|
98
|
+
|
|
99
|
+
Keep the decision with the author: opaque white is one choice, not a forced
|
|
100
|
+
default. Regenerate renderer-owned assets through their factory. Review an
|
|
101
|
+
existing `.edited.svg` or unmanaged image before changing it, and preserve
|
|
102
|
+
original captures and source material. The background check does not establish
|
|
103
|
+
generation freshness, attribution or permission to replace an asset.
|
|
@@ -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`; coordinated `v0.4.0` is now public, so the fixture can align in a separate reviewed change, while 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.4.0`. Published generated consumers select the higher-level `ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.4.0`, which adds the reviewed core and Python control plane. Their Make targets use `/opt/unaltraweb` as a path gem; the RubyGems package remains the optional native Bundler channel. gContExt 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
|
|
|
@@ -25,13 +25,49 @@ The template is the better place to validate gem consumption, centralized styles
|
|
|
25
25
|
|
|
26
26
|
## Component Contract
|
|
27
27
|
|
|
28
|
+
### 0.5.0 release preparation
|
|
29
|
+
|
|
30
|
+
The next coordinated core release is **0.5.0**. It groups the integrated editorial
|
|
31
|
+
review, guided scaffold updates, caption-credit/image behavior and published
|
|
32
|
+
Diavisuals/Vegavisuals 0.4.0 acceptance. Package, core/MCP and manual PDF candidates
|
|
33
|
+
are verified through the existing source-bound workflows before publication.
|
|
34
|
+
Unchanged already-published computation and web-capture workers retain their own
|
|
35
|
+
0.4.0 versions and digests. New/pending worker candidates must still match the
|
|
36
|
+
coordinated release; an old mutable alias cannot qualify for reuse. The selected
|
|
37
|
+
consumer tuple must use a reviewed 0.5.0 core revision and tested PDF worker.
|
|
38
|
+
The PDF worker passed the
|
|
39
|
+
[signed candidate workflow](https://github.com/dosquartsdedocs/unaltraweb/actions/runs/36272762057)
|
|
40
|
+
at source `d857f8c9f5fea90cf450c0b30b4e77a37b541275` and is selected by digest
|
|
41
|
+
`sha256:9e0b3a45753c170b795e9a9d6df61580085c113436beac5bf6c8de69b6562097` in
|
|
42
|
+
both the factory and the consumer tuple. It is already published and tested, so
|
|
43
|
+
final candidate receipts cover the remaining ready package/core components.
|
|
44
|
+
The factory launcher remains on its previous published image until the normal
|
|
45
|
+
post-release pin update. Follow [issue 69](https://github.com/dosquartsdedocs/unaltraweb/issues/69)
|
|
46
|
+
for the candidate, receipt, tag and package evidence.
|
|
47
|
+
|
|
28
48
|
`src/unaltraweb_mcp/component-contract.json` is the canonical versioned bill of materials. Its `consumer_integration` object is the sole source for the reviewed core Git revision, reusable deploy workflow, manual PDF image digest, and Vega renderer revision. Scaffold templates render that tuple atomically into consumer `Gemfile`, `Gemfile.lock`, and deploy workflow files. `component-contract.schema.json` defines schema version 1. Runtime loading and `scripts/validate_distribution.py` validate the complete document against that schema, then enforce semantic parity between versions, release tags, repositories, references, wheel contents, CLI availability, and consumer integration pins.
|
|
29
49
|
|
|
30
50
|
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
51
|
|
|
32
|
-
|
|
52
|
+
The selected public core release is `0.4.0`; `v0.3.0` remains the immutable previous distribution. The next-release source BOM reuses immutable 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. These source changes require a new coordinated core release; they do not alter the already published `0.4.0` artifacts or the factory's `MCP_RELEASE_IMAGE` digest. `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`.
|
|
53
|
+
|
|
54
|
+
## Docker-First Hybrid Policy
|
|
55
|
+
|
|
56
|
+
GHCR is the canonical delivery channel for normal local use. The released package scaffold selects `ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.4.0`; existing `v0.3.0` 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.
|
|
57
|
+
|
|
58
|
+
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.
|
|
59
|
+
|
|
60
|
+
The distribution keeps native channels for interoperability rather than making them Docker prerequisites:
|
|
61
|
+
|
|
62
|
+
- `ghcr.io/dosquartsdedocs/unaltraweb` is the lower-level Ruby/Jekyll runtime used to build the MCP image.
|
|
63
|
+
- `ghcr.io/dosquartsdedocs/unaltraweb-mcp` is the self-contained normal site and MCP runtime.
|
|
64
|
+
- RubyGems publishes the same Jekyll core as a native Bundler adapter for consumers that do not use the Docker path.
|
|
65
|
+
- PyPI publishes the small scaffold, control and inspection plane for native Python, `pip` or `uv` use.
|
|
66
|
+
- Dedicated GHCR workers keep Chromium, TeX and computation environments out of the normal site image.
|
|
67
|
+
|
|
68
|
+
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.
|
|
33
69
|
|
|
34
|
-
## Wheel And Doctor
|
|
70
|
+
## Optional Wheel And Doctor
|
|
35
71
|
|
|
36
72
|
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
73
|
|
|
@@ -49,7 +85,7 @@ Factory-backed MCP serving, site preflight/build, computations, captures, PDF op
|
|
|
49
85
|
|
|
50
86
|
### GitHub-only editing
|
|
51
87
|
|
|
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
|
|
88
|
+
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
89
|
|
|
54
90
|
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
91
|
|
|
@@ -68,6 +104,8 @@ make down
|
|
|
68
104
|
|
|
69
105
|
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
106
|
|
|
107
|
+
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.
|
|
108
|
+
|
|
71
109
|
Theme development can happen side by side by pointing the template at a local core checkout:
|
|
72
110
|
|
|
73
111
|
```bash
|
|
@@ -94,35 +132,24 @@ Repositories created from a GitHub template are not linked to the template as fo
|
|
|
94
132
|
|
|
95
133
|
For that reason:
|
|
96
134
|
|
|
97
|
-
- normal improvements should ship through
|
|
135
|
+
- 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
136
|
- site repositories can enable Dependabot for Bundler and GitHub Actions, but deploy workflows should remain manual;
|
|
99
137
|
- breaking changes should be released with migration notes;
|
|
100
|
-
- scaffold changes should be rare; generated sites can explicitly dry-run `scaffold_sync`, which updates
|
|
138
|
+
- scaffold changes should be rare; generated sites can explicitly dry-run `scaffold_sync`, which updates unchanged baseline runtime files (including the pull-request template), creates newly managed missing files, and preserves local edits when the upstream file still equals its original baseline. Conflicting local/upstream edits remain blocked. The transaction never deletes paths, stages every output, rechecks adopted, unchanged and preserved files around the manifest write, rolls back on failure, and commits its manifest last. `site_context.update_status` guides the agent's version/update offer; `expected_plan_sha256` binds confirmation to the reviewed proposal. Generated README prose is site-owned and is not overwritten by synchronization.
|
|
101
139
|
|
|
102
140
|
## Docker Runtime
|
|
103
141
|
|
|
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.
|
|
142
|
+
Release `0.4.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
143
|
|
|
108
|
-
The
|
|
144
|
+
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
145
|
|
|
110
|
-
|
|
146
|
+
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
147
|
|
|
112
|
-
-
|
|
113
|
-
- Docker image updates change the local build/runtime environment.
|
|
148
|
+
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
149
|
|
|
115
|
-
|
|
150
|
+
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
151
|
|
|
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`.
|
|
152
|
+
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
153
|
|
|
127
154
|
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
155
|
|
|
@@ -143,7 +170,7 @@ The receipt uses a full source commit and different evidence by component kind.
|
|
|
143
170
|
}
|
|
144
171
|
```
|
|
145
172
|
|
|
146
|
-
|
|
173
|
+
The receipt example describes future publication verification; changing the contract does not publish, tag, or release any artifact.
|
|
147
174
|
|
|
148
175
|
## CI And Release Gates
|
|
149
176
|
|
|
@@ -153,6 +180,16 @@ Automatic `.github/workflows/ci.yml` uses `distribution-check`. It validates sch
|
|
|
153
180
|
|
|
154
181
|
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
182
|
|
|
183
|
+
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`:
|
|
184
|
+
|
|
185
|
+
- 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`.
|
|
186
|
+
- GitHub environment `rubygems`, allowed only from the default branch; RubyGems Trusted Publisher gem `unaltraweb`, owner `dosquartsdedocs`, repository `unaltraweb`, workflow `package-publish.yml`, environment `rubygems`.
|
|
187
|
+
- 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.
|
|
188
|
+
|
|
189
|
+
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.
|
|
190
|
+
|
|
191
|
+
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.
|
|
192
|
+
|
|
156
193
|
## Verification
|
|
157
194
|
|
|
158
195
|
Core changes should be validated in two layers:
|