unaltraweb 0.7.4 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66c0a140c30b1c7d9f1353efc62b28eb3c379649160ba68ad6b262d1f91ec90a
4
- data.tar.gz: f6d0c69a53d4b1a5de92777335c6cd1705f4946fb6390cac260fcbeac7db3361
3
+ metadata.gz: 4da9160082bd9c84936be79042fb12dcd6b08de75353196d34079d77e2abc55c
4
+ data.tar.gz: fb9d3fb95a69e51d6c430f612baf9cf426a694c4b9c986de30e60ff3e652b765
5
5
  SHA512:
6
- metadata.gz: d3dff0655f66a91f8a56c301c2da3fd395ef4b2ef7cd33e80a2c858a86cb3a734058480d62bdfc7753663d93b44f2906076c8ce74a2504de1295856ae2b1cf04
7
- data.tar.gz: 9e8ffdb6047bde3d209f47fe339dcb97fe3d60ca365468cc7a056860b041c7e1823b9c47e7974da3f20e7be0e0c0719f986a617318876f64556e551c169a71e6
6
+ metadata.gz: 8949f58e89b5ade9255639ff6d2b43df2bce76401b989662059922c948bca7be1e63f6da6b9e623ef9587e5f8820d82492c06c3c7e226c6ee1055d17f783f243
7
+ data.tar.gz: 2999667915dfbfb58da97468780057a1a0a78d29054894fd70bc4d7d8e4ab096f662cee9f40043fc92111df3e9171b50b4884a7dde1916a1bd8fcc798493295a
data/Makefile CHANGED
@@ -1,9 +1,9 @@
1
1
  PYTHON ?= python3
2
2
  override PROJECT := $${MCP_CONSUMER_WORKSPACE:?MCP_CONSUMER_WORKSPACE is required}
3
3
  override PROJECT_ROOT := $(PROJECT)
4
- MCP_RUNTIME_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.7.4
5
- MCP_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.7.4
6
- MCP_RELEASE_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:8183e7117f5902fc0ad4d930b757d874b2da1f93d5ef4db102ae4c1b03e357a3
4
+ MCP_RUNTIME_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.8.0
5
+ MCP_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.8.0
6
+ MCP_RELEASE_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:10bcaf1411f27a518969283a77e23dcc99119014b216270b3089ba85ba0b6c7e
7
7
  MCP_DOCKER_BUILD_NETWORK ?= default
8
8
  INIT_SITE_PROFILE ?= unaltreselfie
9
9
  NEW_WEB_PROFILE ?= unaltreselfie
@@ -40,7 +40,7 @@ WEB_CAPTURE_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-web-capture@sha256:0bf1b
40
40
  WEB_CAPTURE_DEV_IMAGE ?= unaltraweb-web-capture:dev
41
41
  WEB_CAPTURE_DOCKER_BUILD_NETWORK ?= default
42
42
  VEGAVISUALS_CLI ?=
43
- DOCKER_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.7.4
43
+ DOCKER_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb:0.8.0
44
44
  MANUAL_PDF_IMAGE ?= ghcr.io/dosquartsdedocs/unaltraweb-manual-pdf@sha256:1bb3f2dafd741e96c28639ec1cc8ccaeb81cbaa73e649f787c0b4ef59997bd4b
45
45
  MANUAL_PDF_DEV_IMAGE ?= unaltraweb-manual-pdf:dev
46
46
  MCP_SMOKE_MANUAL_PDF_IMAGE ?= $(MANUAL_PDF_IMAGE)
@@ -0,0 +1,121 @@
1
+ ---
2
+ title: Export Manual Context for Itemarium
3
+ description: Portable, producer-resolved manual text and figures with immutable provenance.
4
+ lang: en
5
+ ref: manual_context_export
6
+ profiles:
7
+ - unaltredocs
8
+ documentation_profiles:
9
+ - site-authors
10
+ - core-developers
11
+ section: Manuals
12
+ weight: 470
13
+ permalink: /manual-context/
14
+ ---
15
+
16
+ Version **0.8.0** introduces `manual_context_export`. Version 0.7.4 and
17
+ already-running MCP sessions do not acquire it from a checkout edit.
18
+ Implementation and interoperability are tracked in
19
+ [issue #121](https://github.com/dosquartsdedocs/unaltraweb/issues/121).
20
+
21
+ ## Select the manual
22
+
23
+ Use a connection bound to the manual workspace. Set a stable, author-owned
24
+ `unaltraweb.manual.id` in `_config.yml`; retain it across revisions and relocation.
25
+ The title is `unaltraweb.manual.title`, falling back to the site title. Each
26
+ chapter needs a unique `ref`, a title, and its configured language. Explicit
27
+ heading IDs such as `## Method {#method}` remain stable across title edits.
28
+
29
+ ```python
30
+ manual_context_export(name="itemarium-review-01", language="ca", chapters=["first"])
31
+ ```
32
+
33
+ The equivalent factory-backed CLI is:
34
+
35
+ ```bash
36
+ unaltraweb-mcp --project /absolute/manual mcp manual-context-export \
37
+ --name itemarium-review-01 --language ca --chapter first
38
+ ```
39
+
40
+ `--chapter` is repeatable and preserves manual order. `--section first#method`
41
+ selects one **exact block**, not its descendants; repeat it for additional blocks.
42
+ The MCP arguments are `chapters` and `sections` arrays. Omitting them selects all
43
+ chapters/blocks in the requested language. Unknown or duplicate selections fail.
44
+
45
+ ## Delivery and retention
46
+
47
+ Exports are retained under `tmp/manual-context/<name>/`: `content/`, the flat
48
+ `manual-context.tar.gz` and a receipt sealed last. This directory must be ignored
49
+ by Git. An existing name is never replaced, even after a failed or interrupted
50
+ export. The result returns the workspace-relative `path`, actual `sha256`, receipt
51
+ path/hash, producer/manual identities and counts. Only the tar and its SHA are
52
+ needed by Itemarium; no source checkout access is necessary.
53
+
54
+ The archive contains exactly `manual-context.json`, `sections/...md`, and
55
+ `figures/...`. Image references use `/figures/...`. The schema is available at
56
+ `web://manual-context-schema`. Each heading's file ends before the next heading,
57
+ so parent blocks do not duplicate descendant prose. Order is explicit rather
58
+ than inferred from filenames. Numbered chapters precede reference chapters, as
59
+ in the native manual resolver; public `pdf: false` chapters remain available.
60
+
61
+ The exporter resolves native figures, localized variants/fallback and the
62
+ existing output precedence on a bounded no-follow snapshot. It reuses native
63
+ computation status (including generated chapters), complete companion receipts
64
+ for diagrams/Vega, and web-capture status. These project-wide completeness checks
65
+ also reject unrelated stale/orphaned records; selecting a chapter cannot bypass
66
+ a required producer gate. Unmanaged generated outputs are not freshness evidence.
67
+
68
+ Literal Markdown/HTML includes without parameters, manual bibliography includes,
69
+ `cite` tags, tables, subfigures, listings, callouts and definition lists are
70
+ projected to portable prose. Captions, credits and bibliography fields/links are
71
+ retained; the exporter does not emulate a CSL citation style. Code examples stay
72
+ literal. HTML comments are not reader-facing context. Arbitrary Liquid, dynamic
73
+ includes and active/interactive HTML need an explicitly prepared portable source
74
+ and fail with a diagnostic rather than disappearing.
75
+
76
+ No renderer or download runs implicitly. Standalone PNG/JPEG/GIF/WebP/SVG images
77
+ are accepted. The current receiver rejects SVG `<image>`, even embedded PNG data
78
+ in capture SVGs, and excludes PDF. A current native web-capture SVG can pass only
79
+ if its effective bytes meet this standalone boundary. QGIS capture provenance
80
+ has no native context-v1 adapter in this increment: recognizable QGIS captures
81
+ are refused with a diagnostic, not relabelled as static. Its profile/receipt
82
+ integration and receiver support for raster-bearing SVG require coordinated
83
+ follow-up. Edited captures are never silently substituted with original rasters.
84
+
85
+ Diagrams use the provider's exact effective-artifact receipt. Vega uses exactly
86
+ the output declared in `.vegavisuals.yml` (including an edited output only when
87
+ that is the declared, verified output). Computation SVG overrides must postdate
88
+ their current source and generated base. Web captures use their native embedded
89
+ PNG/fingerprint checks. The exporter records fresh resolution evidence binding
90
+ the native check **and the actual effective bytes**; `receipt_sha256` refers to
91
+ that evidence in the retained export receipt, not an older renderer receipt
92
+ misattributed to new edited bytes.
93
+
94
+ `manual.revision` is the actual Git HEAD and `dirty` is explicit. Clean source
95
+ dates use the commit date; dirty snapshots use the latest consumed file mtime,
96
+ with an explicit timezone. The source fingerprint is SHA256 of canonical UTF-8
97
+ JSON (sorted keys, compact separators, no trailing newline) mapping consumed
98
+ project-relative paths to their hashes: configuration, manual collection inputs,
99
+ referenced includes/bibliography, original and effective visuals, dependency
100
+ files, native locks and complete provider check inventories. Only resolved text
101
+ and registered images enter the transport archive; executable/data/check inputs
102
+ are represented by fingerprints in the producer receipt. Absent variant probes are retained in
103
+ the external producer receipt and rechecked before sealing. The package's direct
104
+ file hashes independently bind every delivered byte. Producer revision is a
105
+ code fingerprint of the package and native resolver/check scripts, not a claim
106
+ that a development build is a published release.
107
+
108
+ ## Interoperability test
109
+
110
+ Core maintainers can produce retained synthetic manuals through the actual MCP:
111
+
112
+ ```bash
113
+ python scripts/test_manual_context_export.py --output /absolute/unaltraweb/tmp/context-run-01
114
+ ```
115
+
116
+ The script emits first/next package paths and sender hashes. Pass those exact
117
+ files to Itemarium's `tools/desktop/manual_package_conformance.py`, using an
118
+ Itemarium-capable Python and a new output directory under unaltraweb's `tmp/`.
119
+ The producer receipt proves exporter invocation; the independent consumer receipt
120
+ proves schema/closure, import, preservation and backup/reopening. Neither test
121
+ creates questions or certifies a teaching bank's quality.
@@ -0,0 +1,134 @@
1
+ # Manual context producer — #121
2
+
3
+ Workspace: `/home/benizar/git/unaltraweb`; branch `feat/121-manual-context-export`.
4
+ [Draft PR122](https://github.com/dosquartsdedocs/unaltraweb/pull/122) contains the
5
+ post-release pin (`59bd62b`), first bound exporter (`cbf7260`) and resolver/check
6
+ continuation. No merge or public release is part of this delivery.
7
+
8
+ ## Interface and contract
9
+
10
+ - MCP: `manual_context_export(name, language="", chapters=None, sections=None)`.
11
+ - CLI: `unaltraweb-mcp --project /absolute/manual mcp manual-context-export
12
+ --name RUN [--language ca] [--chapter REF ...] [--section REF#ANCHOR ...]`.
13
+ - Resource: `web://manual-context-schema`.
14
+ - The export uses its connection's workspace; the MCP has no root argument.
15
+ - `unaltraweb.manual.id` is an explicit stable logical identity. Text selection
16
+ preserves refs, native order and exact non-overlapping blocks; section selection
17
+ is exact, not an implicit subtree expansion.
18
+ - The schema bytes match Itemarium's supplied v1 exactly:
19
+ `e099d6f01af87a9f95ae828a8abc5ef69fb61f8d3fdfb8e5886d478cb60bd1c8`.
20
+ - Production has no Itemarium dependency. Native factory helpers are required,
21
+ as declared by the wheel/CLI contract. A bounded snapshot, native statuses,
22
+ complete provider receipts and final source/identity rechecks precede sealing.
23
+
24
+ See the [author guide](../_documentation/en/47-manual-context.md) for supported
25
+ components, fingerprint closure, retention, limits and refusal remediation.
26
+
27
+ ## Acceptance evidence
28
+
29
+ First actual exporter invocation (one chapter, three blocks, one static PNG):
30
+
31
+ - `tmp/manual-context-121-first/manual-fixture/tmp/manual-context/v1/manual-context.tar.gz`
32
+ - SHA256 `414327f240684aa3e66f609d00017d9e1b8d6ced5a0ec7de4d0d0dd53e0bb256`.
33
+ - `tmp/manual-context-121-first-conformance/receipt.json`: consumer check, import
34
+ and backup/reopening passed. This early artifact predates the additional guards
35
+ and generated-resource adapter; it is retained separately from final evidence.
36
+
37
+ Final producer test used the installed package in an explicitly built, network-
38
+ disabled, read-only-root Docker MCP, with only its synthetic manual mounted:
39
+
40
+ - Image ID `sha256:f01c5e220ad635588d0e545eb3294e8550da71c90a59eaab9a91ad7d334c8873`.
41
+ - Producer version **0.8.0 development**; code revision
42
+ `sha256:322eb2f79d7c275c0dc9e6ce554a7d4fa82f63cb8a259cdd4b3413ecc5b3ca56`.
43
+ The host source fingerprint matches this observed installed producer exactly.
44
+ - Both stdio instances expose the bound tool, refuse a second use of the export
45
+ name, drain, close and leave their exact fixture container absent.
46
+ - The real CLI also exported one selected block with its figure successfully.
47
+ - Producer receipt: `tmp/manual-context-121-final/producer-receipt.json`, SHA256
48
+ `8482b2c44dc5885a683a94314f59c0077f104e58c0f6312f2c2454c8512b41a0`.
49
+
50
+ Packages below `tmp/manual-context-121-final/manual-fixture/`:
51
+
52
+ | Version | Path | SHA256 |
53
+ | --- | --- | --- |
54
+ | First: 1 chapter / 3 blocks / 1 figure | `tmp/manual-context/v1/manual-context.tar.gz` | `4ecb3014dc57c66962bead52c8ab42a1233e23c8ddc29c3bb5cecf82cc6801b3` |
55
+ | Next: 2 chapters / 4 blocks / 1 figure | `tmp/manual-context/v2/manual-context.tar.gz` | `b60909760352c74b921d077924a05f873d3cf144ae6aabf906e100b9b3a824d4` |
56
+
57
+ Both have manual ID `fixture-manual-121`, language `ca`, `dirty:false`; source
58
+ revisions are `4f507b56ba2fcdb3fc59f727066427639776841d` and
59
+ `5165919651d1140b7382d5f148e84fa54feb67fc`. Fingerprints change with the sources;
60
+ refs and the prior bank remain stable.
61
+
62
+ Itemarium's independent runner consumed **these exact two exporter archives**:
63
+
64
+ - Engine 0.6.2, source SHA256
65
+ `1a8983100c4b0c72bac74912fdde7a4d8ad14ce9970e188f13aae611dbeee813`.
66
+ - Receipt: `tmp/manual-context-121-final-conformance/receipt.json`, SHA256
67
+ `f5a5579377f3513f6b1d2e796cb8c99723dabe530f9de6425da517f36e13aad3`.
68
+ - PASS: schema/integrity/resource closure; isolated import; preservation of all
69
+ existing bank state when the second version adds one bank; backup reopening in
70
+ a second app after removal of the first fixture app.
71
+ - Backup SHA256 `ed8062db688001911bcdca68c23d33135208bfdc89375e1f3a7c1df843016bd0`.
72
+ - The consumer's `producer_export_invoked:false` correctly describes its own
73
+ runner; the separate producer receipt proves actual MCP export invocation.
74
+ No manual checkout was supplied to Itemarium. No questions or real teacher data
75
+ were used; functional conformance is not teaching-bank quality certification.
76
+
77
+ ## Checks and observations
78
+
79
+ - Full suite: **763 cases, 735 passed, 28 expected skips** with the prepared Python
80
+ environment (`PATH=.../public-env/bin:$PATH PYTHONPATH=src python3 -m unittest
81
+ discover -s test -p 'test_*.py'`). The bare system Python lacks existing pypdf /
82
+ tinycss2 dependencies; that failed attempt is not a passing suite.
83
+ - 21 exporter cases cover static/HTML figures, language fallback and translated
84
+ diagrams, components/includes/citations, generated chapter/computation checks,
85
+ edited byte evidence, Vega local-data receipts, web captures, stale/modified/
86
+ unmanaged/missing outputs, unsafe resources, concurrent changes and retention.
87
+ Generated-resource unit fixtures carry synthetic native records; they do not
88
+ claim fresh renderer execution.
89
+ - `distribution-check`, `workflow-check`, clean installed `wheel-check`: PASS.
90
+ - `make mcp-check`, `make mcp-smoke`: PASS.
91
+ - `make docs-build DOCKER_IMAGE=unaltraweb:dev`: PASS; prose check of the new guide:
92
+ no findings. The initial default-image docs command refused unpublished 0.8.0.
93
+ - Gem build/package inspection: PASS inside the prepared MCP image with its Ruby,
94
+ network disabled and source mounted read-only. The host-only default attempt
95
+ refused the unpublished 0.8.0 runtime rather than pulling it.
96
+ - `git diff --check`: PASS. Initial CI's old hardcoded 0.7.3 launcher expectation
97
+ was updated to the verified 0.7.4 digest; no release gate was relaxed.
98
+
99
+ ## Remaining integration boundaries
100
+
101
+ The receiver refuses all SVG `<image>` nodes, including embedded PNGs, and PDF.
102
+ Native capture freshness is checked, but such effective assets are refused rather
103
+ than rasterized or substituted. QGIS context-v1 provenance has no adapter in this
104
+ increment; recognizable QGIS captures get an explicit unsupported diagnostic.
105
+ Interactive/arbitrary Liquid and undeclared outputs also fail closed. These
106
+ limitations are recorded on #121 for producer/receiver coordination.
107
+
108
+ The source version is 0.8.0 with publication status **pending**. The checkout
109
+ launcher now selects published 0.7.4; observation of this conversation's existing
110
+ MCP still showed installed **0.7.3**, instance `ecac06618bd3436794e89c31746b8914`,
111
+ bound to this checkout. It was not hot-reloaded or rebound. Hub admission belongs
112
+ to gaContExt #52, and real-manual selection/author review remain separate.
113
+
114
+ The #116 checkpoint remains on `feat/116-common-practice-sdk`; it is not included
115
+ in this branch or attributed to these exports.
116
+
117
+ ## Authorized publication follow-up — 2026-11-03
118
+
119
+ After the source delivery above, the user authorized continuing toward hub
120
+ integration. PR122's final source and both CI runs remain green. Itemarium also
121
+ independently rechecked the exact exporter archives in network-disabled isolation
122
+ with no manual checkout: all four conformance checks passed, receiver receipt
123
+ `15b8561e1e55899b57d8e45b8d19b7a8eeaa669808cf729d29eac3589a3231fb`
124
+ ([receiver return](https://github.com/dosquartsdedocs/unaltraweb/issues/121#issuecomment-6597815786)).
125
+
126
+ The four new Web components are marked release-ready for the normal signed
127
+ candidate, receipt-only merge/tag and trusted publication workflow. Independent
128
+ core/gem, workflow, PDF and QGIS selections retain their previously verified
129
+ identities. This source readiness is not a claim of published bytes or admission.
130
+
131
+ GaContExt #52 specifically concerns admitting newer **Itemarium** native builds.
132
+ The new Web tuple must also be delivered to the hub owner after publication;
133
+ neither publication alone nor the existing consumer conformance receipt proves
134
+ hub admission, selected-client activation or an export of a real teaching manual.
@@ -38,10 +38,10 @@ it to an image ID before execution. The source checkout's digest pin is separate
38
38
  it advances only after a new receipt exists. Explicit `--image` (or the console
39
39
  adapter's `UNALTRAWEB_MCP_IMAGE`) permits a reviewed immutable selection.
40
40
 
41
- The post-release checkout pin selects published 0.7.1 MCP digest
42
- `sha256:6aa20cff86c3891a876e37ba0548fa36f3c5633c9f22ba672e9f50123d677cf6`.
43
- The [0.7.1 delivery report](owner-closeout-93.md) records the consolidated deployment
44
- contract, exact hotfix migration and matching PDF verifier closure.
41
+ The post-release checkout pin selects published 0.7.4 MCP digest
42
+ `sha256:10bcaf1411f27a518969283a77e23dcc99119014b216270b3089ba85ba0b6c7e`.
43
+ The [0.7.4 delivery report](scaffold-closeout-118.md) records the coherent independent
44
+ core/gem tuple, reviewed scaffold migration and verified publication.
45
45
  Existing processes keep their running image until clients reconnect. Site build
46
46
  defaults and customized Makefiles remain separate, explicitly reviewed selections.
47
47
 
@@ -89,6 +89,7 @@ baseline format nor runtime cleanup changes.
89
89
 
90
90
  | Resource | Description |
91
91
  | --- | --- |
92
+ | `web://manual-context-schema` | Portable manual context v1 schema, byte-identical to the reviewed Itemarium consumer contract. |
92
93
  | `web://practice-edition-schema` | Versioned Web recipient/edition configuration, separate from the producer-owned GIS descriptor. |
93
94
  | `web://practice-verifier-runtime` | Static private controller/package/profile identities and distinct release metadata; acquisition belongs to the hub. |
94
95
  | `web://runtime-identity` | Bounded identity of this serving process: startup/current code, instance/PID namespaces, binding, runtime selections and cooperative lifecycle. |
@@ -179,6 +180,7 @@ without a portable edition context; HTTPS and internal anchors remain intact.
179
180
 
180
181
  | Tool | Notes |
181
182
  | --- | --- |
183
+ | `manual_context_export` | Create-only export of the bound manual's selected language/chapter refs/exact section IDs; returns a retained tar.gz and real SHA256. No arbitrary root, renderer, publication or Itemarium runtime dependency. See [context export](../_documentation/en/47-manual-context.md). |
182
184
  | `manual_practice_compose` | One versioned consumer configuration selects GIS roots, two approved reading hashes and optional declared editable-SVG resources; delivers an independent PDF and ZIP per recipient with complete closure and create-only receipts. |
183
185
  | `runtime_identity` | Observe the actual serving instance, startup/current package evidence and selected/observed images; never prepare a runtime or inspect site content. |
184
186
  | `runtime_drain` | With `confirm=true`, stop admission on this connection; then close stdio and verify exact container/session resource termination. It is not workspace-wide down. |
@@ -135,6 +135,33 @@ unsafe-path refusal, symlinks, rollback and concurrent-edit recovery. Wheel test
135
135
  compare the complete top-level module inventory against source, excluding stale
136
136
  build-cache modules and the separate #116 checkpoint.
137
137
 
138
- Publication receipts, verified immutable images and final cold-download evidence
139
- will be added after their actual gates complete. Source preparation or a passing
140
- local acceptance is not a claim of release or manual activation.
138
+ ## Published delivery and post-release selection
139
+
140
+ [Release v0.7.4](https://github.com/dosquartsdedocs/unaltraweb/releases/tag/v0.7.4)
141
+ is public. PR119 supplies artifact source `676caa695d8e8f84a8a11800bb5a4a17599af539`;
142
+ PR120 supplies receipt/tag target `a0b82eadceac608a7e3ce97fb6d2fd2885dd541c`.
143
+ Annotated tag object: `7ef4525c33fd6ee4b9592deec23f1d87910a4581`.
144
+
145
+ - Controller: `ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:10bcaf1411f27a518969283a77e23dcc99119014b216270b3089ba85ba0b6c7e`.
146
+ - Runtime: `ghcr.io/dosquartsdedocs/unaltraweb@sha256:5d82d15358f2785e1a548f8d3d2a0e932602e3d9d7b4d1efa330fb774c65f5cb`.
147
+ - Wheel SHA256: `0202ff0982fea276a7ba775245faa7bafd75acc5cfad3e1f549055e87cd336ec`.
148
+ - Handoff: [release-handoff.json](https://github.com/dosquartsdedocs/unaltraweb/releases/download/v0.7.4/release-handoff.json),
149
+ SHA256 `aaf6256f438cdb4500b3d96081a7a6edd013c44b3411744aa6738b18decbffb2`.
150
+
151
+ Trusted Publishing `40388093852`, OCI promotion `40388094024` and tag CI
152
+ `40388002315` passed. All fourteen original assets were downloaded anonymously
153
+ and matched their exact sizes/hashes, including both Docker archives. PyPI and
154
+ RubyGems bytes and OCI aliases matched. The public
155
+ [verification receipt](https://github.com/dosquartsdedocs/unaltraweb/releases/download/v0.7.4/verification.json)
156
+ has SHA256 `c19b46279100d48483ff544d87a3f88fae08d2bfc9b20f302628767ecaae8d09`.
157
+
158
+ The public wheel was installed in a new environment and passed the complete
159
+ synthetic installed-MCP, reviewed-conflict, stale-CAS and frozen Git/Bundler/Jekyll
160
+ acceptance: [acceptance.json](https://github.com/dosquartsdedocs/unaltraweb/releases/download/v0.7.4/acceptance.json).
161
+ The first invocation refused relative paths before running; the completed run
162
+ uses the required absolute paths. #115/#118 are closed with the
163
+ [full delivery](https://github.com/dosquartsdedocs/unaltraweb/issues/118#issuecomment-6426018149).
164
+
165
+ The post-release checkout launcher now selects the published controller digest
166
+ above. Existing sessions keep their loaded image; consumer/hub selection and
167
+ author review remain independent of this source pin and technical delivery.
@@ -451,7 +451,7 @@ def artifact_plan(project: Path, config: dict[str, Any], languages: list[str]) -
451
451
  return plan
452
452
 
453
453
 
454
- def manual_sources(project: Path, config: dict[str, Any], lang: str) -> tuple[Path | None, list[tuple[Path, dict[str, Any], str]], str]:
454
+ def manual_sources(project: Path, config: dict[str, Any], lang: str, *, for_pdf: bool = True) -> tuple[Path | None, list[tuple[Path, dict[str, Any], str]], str]:
455
455
  manual = nested(config, "unaltraweb", "manual")
456
456
  collection = str(manual.get("collection") or "chapters").strip()
457
457
  collection_root = safe_relative(project, f"_{collection}", label="manual collection", must_exist=True)
@@ -462,7 +462,10 @@ def manual_sources(project: Path, config: dict[str, Any], lang: str) -> tuple[Pa
462
462
  if path.suffix.lower() not in {".md", ".markdown"} or not path.is_file():
463
463
  continue
464
464
  front, body = read_source(path)
465
- if front.get("pdf") is False:
465
+ if for_pdf and front.get("pdf") is False:
466
+ continue
467
+ if not for_pdf and (front.get("published") is False or front.get("draft") is True
468
+ or (front.get("profiles") is not None and "unaltremanual" not in front["profiles"])):
466
469
  continue
467
470
  pages.append((path, front, body))
468
471
 
@@ -0,0 +1,115 @@
1
+ """Retained fixture acceptance through the real producer MCP, never a fabricated tar.
2
+
3
+ Run Itemarium's independent conformance command on the returned paths/hashes.
4
+ This script itself has no Itemarium dependency and uses no teaching workspace.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ import asyncio
10
+ import hashlib
11
+ import json
12
+ import os
13
+ from pathlib import Path
14
+ import sys
15
+ import subprocess
16
+ import uuid
17
+
18
+ ROOT = Path(__file__).resolve().parents[1]
19
+ sys.path.insert(0, str(ROOT / "src"))
20
+ sys.path.insert(0, str(ROOT / "test"))
21
+ from test_manual_context import fixture, write, commit # noqa: E402
22
+
23
+
24
+ def save(path, value):
25
+ with path.open("x") as stream:
26
+ json.dump(value, stream, ensure_ascii=False, sort_keys=True, indent=2)
27
+ stream.write("\n")
28
+
29
+
30
+ async def export(project, name, image=""):
31
+ from mcp import ClientSession, StdioServerParameters
32
+ from mcp.client.stdio import stdio_client
33
+ def payload(result):
34
+ return result.structuredContent or json.loads("\n".join(row.text for row in result.content if hasattr(row, "text")))
35
+ parameters = StdioServerParameters(command=sys.executable,
36
+ args=["-B", "-m", "unaltraweb_mcp.cli", "mcp", "serve"],
37
+ env={"PATH": os.environ["PATH"], "HOME": str(project), "PYTHONPATH": str(ROOT / "src"),
38
+ "MCP_CONSUMER_WORKSPACE": str(project), "UNALTRAWEB_FACTORY_DIR": str(ROOT)})
39
+ container = ""
40
+ image_id = ""
41
+ if image:
42
+ from unaltraweb_mcp.docker_mount import docker_bind_mount
43
+ image_id = subprocess.run(["docker", "image", "inspect", image, "--format", "{{.Id}}"], check=True,
44
+ capture_output=True, text=True, timeout=20).stdout.strip()
45
+ container = "unaltraweb-context-121-" + uuid.uuid4().hex
46
+ parameters = StdioServerParameters(command="docker", args=["run", "--rm", "-i", "--pull", "never",
47
+ "--name", container, "--network", "none", "--read-only", "--user", f"{os.getuid()}:{os.getgid()}",
48
+ "--cpus", "2", "--memory", "2g", "--pids-limit", "256", "--tmpfs", "/tmp:rw,nosuid,nodev,size=268435456",
49
+ "--label", "io.context.mcp-factory=unaltraweb", "--label", "io.context.mcp-role=context-acceptance",
50
+ "--mount", docker_bind_mount(project, "/workspace"), "-e", "HOME=/tmp",
51
+ "-e", "UNALTRAWEB_DOCKER_ROOT=" + str(project), "-e", "UNALTRAWEB_MCP_IMAGE=" + image_id,
52
+ "--entrypoint", "unaltraweb-mcp", image_id, "--project", "/workspace", "mcp", "serve"])
53
+ async with stdio_client(parameters) as (incoming, outgoing):
54
+ async with ClientSession(incoming, outgoing) as client:
55
+ await client.initialize()
56
+ tools = await client.list_tools()
57
+ tool = next(row for row in tools.tools if row.name == "manual_context_export")
58
+ assert not {"root", "project", "workspace"} & tool.inputSchema["properties"].keys()
59
+ identity = payload(await client.call_tool("runtime_identity", {}))
60
+ result = payload(await client.call_tool("manual_context_export", {"name": name, "language": "ca"}))
61
+ assert result["ok"], result
62
+ again = payload(await client.call_tool("manual_context_export", {"name": name, "language": "ca"}))
63
+ assert not again["ok"] and "already exists" in again["error"], again
64
+ await client.call_tool("runtime_drain", {"confirm": True})
65
+ result["absolute_path"] = str(project / result["path"])
66
+ assert hashlib.sha256(Path(result["absolute_path"]).read_bytes()).hexdigest() == result["sha256"]
67
+ observed = {"identity": identity, "export": result, "create_only_refusal": again}
68
+ if image:
69
+ assert identity["package"]["mode"] == "installed" and not identity["package"]["drift"]
70
+ absent = subprocess.run(["docker", "container", "inspect", container], capture_output=True, timeout=20)
71
+ assert absent.returncode != 0, "Fixture MCP container remains after stdio close"
72
+ observed.update(image_id=image_id, exact_container_absent=True)
73
+ return observed
74
+
75
+
76
+ def main():
77
+ parser = argparse.ArgumentParser(description=__doc__)
78
+ parser.add_argument("--output", type=Path, required=True)
79
+ parser.add_argument("--single", action="store_true")
80
+ parser.add_argument("--image", default="", help="Already-built MCP image; pull-never, network-disabled fixture stdio")
81
+ args = parser.parse_args()
82
+ output = args.output.resolve()
83
+ if output != args.output or not output.is_relative_to(ROOT / "tmp") or not output.parent.is_dir():
84
+ raise ValueError("Use a new absolute output directory below the factory tmp with an existing parent")
85
+ output.mkdir(mode=0o700)
86
+ project = output / "manual-fixture"
87
+ project.mkdir()
88
+ fixture(project)
89
+ first = asyncio.run(export(project, "v1", args.image))
90
+ save(output / "producer-v1.json", first)
91
+ command = [sys.executable, "-B", "-m", "unaltraweb_mcp.cli", "--project", str(project), "mcp", "manual-context-export",
92
+ "--name", "cli-block", "--language", "ca", "--chapter", "first", "--section", "first#method"]
93
+ completed = subprocess.run(command, env={**os.environ, "PYTHONPATH": str(ROOT / "src"), "UNALTRAWEB_FACTORY_DIR": str(ROOT)},
94
+ check=True, capture_output=True, text=True, timeout=120)
95
+ cli = json.loads(completed.stdout)
96
+ assert cli["ok"] and cli["sections"] == 1 and cli["figures"] == 1, cli
97
+ save(output / "producer-cli.json", {"command": command, "result": cli})
98
+ result = {"ok": True, "synthetic_manual": True, "producer_export_invoked": True, "transport": "mcp-stdio",
99
+ "first": first["export"], "cli_verified": True, "questions_generated": 0}
100
+ if not args.single:
101
+ original = (project / "_chapters/ca/first.md").read_text()
102
+ write(project, "_chapters/ca/first.md", original + "\nSegona versió: text afegit.\n")
103
+ write(project, "_chapters/ca/second.md", "---\ntitle: Segon\nref: second\nlang: ca\nweight: 20\n---\nCapítol afegit en la segona versió.\n")
104
+ commit(project, ["_chapters/ca/first.md", "_chapters/ca/second.md"], "Second fixture revision")
105
+ second = asyncio.run(export(project, "v2", args.image))
106
+ save(output / "producer-v2.json", second)
107
+ result["next"] = second["export"]
108
+ assert first["export"]["manual"]["id"] == second["export"]["manual"]["id"]
109
+ assert first["export"]["manual"]["revision"] != second["export"]["manual"]["revision"]
110
+ save(output / "producer-receipt.json", result)
111
+ print(json.dumps(result, indent=2, ensure_ascii=False))
112
+
113
+
114
+ if __name__ == "__main__":
115
+ main()
@@ -15,7 +15,7 @@ Managed mode requires an immutable reference or an expected full image ID.
15
15
  USAGE
16
16
  }
17
17
 
18
- image="${UNALTRAWEB_MCP_IMAGE:-ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:8183e7117f5902fc0ad4d930b757d874b2da1f93d5ef4db102ae4c1b03e357a3}"
18
+ image="${UNALTRAWEB_MCP_IMAGE:-ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:10bcaf1411f27a518969283a77e23dcc99119014b216270b3089ba85ba0b6c7e}"
19
19
  project="${MCP_CONSUMER_WORKSPACE:-${UNALTRAWEB_PROJECT:-}}"
20
20
  operation=serve
21
21
  expected_image="${UNALTRAWEB_EXPECTED_IMAGE_ID:-}"
@@ -2,8 +2,8 @@
2
2
  "$schema": "component-contract.schema.json",
3
3
  "schema_version": 1,
4
4
  "release": {
5
- "version": "0.7.4",
6
- "tag": "v0.7.4"
5
+ "version": "0.8.0",
6
+ "tag": "v0.8.0"
7
7
  },
8
8
  "consumer_integration": {
9
9
  "schema_version": 1,
@@ -55,17 +55,17 @@
55
55
  "package_only_commands": ["doctor", "import-calibre", "new-web", "version"],
56
56
  "factory_required_commands": ["factory-dir"],
57
57
  "package_only_mcp": ["artifact-import-check", "import-artifact-bundle", "bibliography-add-entry", "bibliography-inventory", "build-health", "content-approval-inventory", "content-freshness-check", "content-inventory", "detect-site", "editorial-policy", "editorial-publication-check", "editorial-review-prepare", "editorial-review-record", "editorial-review-resolve", "editorial-status", "html-audit", "http-check", "image-background-check", "initialize-site", "language-policy", "list-tools", "manual-authoring-capabilities", "manual-editorial-quality-check", "manual-pdf-preview-clean", "manual-source-quality-check", "new-web", "preview-start", "preview-status", "preview-stop", "profile-check", "profile-prune", "profile-prune-plan", "prose-check", "scaffold-sync", "site-context", "site-doctor", "site-source-delete", "site-source-read", "site-source-write", "starter-templates", "translation-plan"],
58
- "factory_required_mcp": ["bibliometrics-check", "bibliometrics-fetch-scimago", "bibliometrics-update", "build-site", "manual-computation-check", "manual-computation-render", "manual-computation-render-figures", "manual-computation-status", "manual-pdf-build", "manual-pdf-preview-prepare", "manual-pdf-publish", "manual-pdf-status", "manual-practice-compose", "manual-practice-pdf-build", "manual-release-check", "manual-release-prepare", "manual-release-status", "prompts", "serve", "site-check", "web-capture-check", "web-capture-render", "web-capture-status"],
58
+ "factory_required_mcp": ["bibliometrics-check", "bibliometrics-fetch-scimago", "bibliometrics-update", "build-site", "manual-computation-check", "manual-computation-render", "manual-computation-render-figures", "manual-computation-status", "manual-context-export", "manual-pdf-build", "manual-pdf-preview-prepare", "manual-pdf-publish", "manual-pdf-status", "manual-practice-compose", "manual-practice-pdf-build", "manual-release-check", "manual-release-prepare", "manual-release-status", "prompts", "serve", "site-check", "web-capture-check", "web-capture-render", "web-capture-status"],
59
59
  "not_bundled": ["gem", "runtime", "mcp", "compute_python", "compute_r", "web_capture", "manual_pdf", "diavisuals", "vegavisuals", "qgis_practice_verifier"]
60
60
  },
61
61
  "components": {
62
62
  "gem": {
63
63
  "kind": "gem",
64
64
  "name": "unaltraweb",
65
- "version": "0.7.4",
66
- "release": "v0.7.4",
65
+ "version": "0.8.0",
66
+ "release": "v0.8.0",
67
67
  "release_status": "ready",
68
- "reference": "unaltraweb (= 0.7.4)",
68
+ "reference": "unaltraweb (= 0.8.0)",
69
69
  "repository": "https://github.com/dosquartsdedocs/unaltraweb",
70
70
  "included_in_wheel": false,
71
71
  "features": ["jekyll-core", "site-build"]
@@ -73,10 +73,10 @@
73
73
  "wheel": {
74
74
  "kind": "python-wheel",
75
75
  "name": "unaltraweb-mcp",
76
- "version": "0.7.4",
77
- "release": "v0.7.4",
76
+ "version": "0.8.0",
77
+ "release": "v0.8.0",
78
78
  "release_status": "ready",
79
- "reference": "unaltraweb-mcp==0.7.4",
79
+ "reference": "unaltraweb-mcp==0.8.0",
80
80
  "repository": "https://github.com/dosquartsdedocs/unaltraweb",
81
81
  "included_in_wheel": true,
82
82
  "features": ["calibre-import", "doctor", "new-web", "offline-inspection", "mcp-control-plane", "source-ci"]
@@ -84,10 +84,10 @@
84
84
  "runtime": {
85
85
  "kind": "container",
86
86
  "name": "unaltraweb runtime",
87
- "version": "0.7.4",
88
- "release": "v0.7.4",
87
+ "version": "0.8.0",
88
+ "release": "v0.8.0",
89
89
  "release_status": "ready",
90
- "reference": "ghcr.io/dosquartsdedocs/unaltraweb:0.7.4",
90
+ "reference": "ghcr.io/dosquartsdedocs/unaltraweb:0.8.0",
91
91
  "image_repository": "ghcr.io/dosquartsdedocs/unaltraweb",
92
92
  "repository": "https://github.com/dosquartsdedocs/unaltraweb",
93
93
  "included_in_wheel": false,
@@ -96,10 +96,10 @@
96
96
  "mcp": {
97
97
  "kind": "container",
98
98
  "name": "unaltraweb MCP runtime",
99
- "version": "0.7.4",
100
- "release": "v0.7.4",
99
+ "version": "0.8.0",
100
+ "release": "v0.8.0",
101
101
  "release_status": "ready",
102
- "reference": "ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.7.4",
102
+ "reference": "ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.8.0",
103
103
  "image_repository": "ghcr.io/dosquartsdedocs/unaltraweb-mcp",
104
104
  "repository": "https://github.com/dosquartsdedocs/unaltraweb",
105
105
  "included_in_wheel": false,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unaltraweb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dosquartsdedocs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-26 00:00:00.000000000 Z
11
+ date: 2026-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: classifier-reborn
@@ -783,6 +783,7 @@ files:
783
783
  - docs/_documentation/en/44-editorial-review.md
784
784
  - docs/_documentation/en/45-retained-documents.md
785
785
  - docs/_documentation/en/46-live-runtime.md
786
+ - docs/_documentation/en/47-manual-context.md
786
787
  - docs/_documentation/en/47-private-practice-pdfs.md
787
788
  - docs/_documentation/en/50-faq.md
788
789
  - docs/_pages/ca/index.md
@@ -806,6 +807,7 @@ files:
806
807
  - docs/agents/deploy-provenance-86.md
807
808
  - docs/agents/deployment-integration-0.7.1.md
808
809
  - docs/agents/manual-authoring-components.md
810
+ - docs/agents/manual-context-121.md
809
811
  - docs/agents/mcp-contract.md
810
812
  - docs/agents/owner-closeout-76.md
811
813
  - docs/agents/owner-closeout-80.md
@@ -858,6 +860,7 @@ files:
858
860
  - scripts/prepare_practice_candidate.py
859
861
  - scripts/prepare_practice_editions_candidate.py
860
862
  - scripts/test_gem_build.py
863
+ - scripts/test_manual_context_export.py
861
864
  - scripts/test_reproducible_jekyll_build.py
862
865
  - scripts/test_scaffold_release.py
863
866
  - scripts/test_wheel_install.py