Metadata-Version: 2.4
Name: unaltraweb-mcp
Version: 0.6.0
Summary: MCP control plane for unaltraweb website workspaces
Author: dosquartsdedocs
Keywords: jekyll,mcp,websites,bibliometrics
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: bibtexparser<2,>=1.4
Requires-Dist: PyYAML<7,>=6
Requires-Dist: requests<3,>=2.31
Requires-Dist: Pillow<13,>=10.4
Requires-Dist: CairoSVG<3,>=2.7
Requires-Dist: pypdf<7,>=6
Requires-Dist: markdown-it-py<5,>=3
Requires-Dist: tomli<3,>=2; python_version < "3.11"
Provides-Extra: mcp
Requires-Dist: mcp<2,>=1.0.0; extra == "mcp"
Dynamic: license-file

# unaltraweb

`unaltraweb` is a reusable Jekyll core for academic, research project, software and documentation websites maintained by `dosquartsdedocs`.

## Content Editors: Start Here

If you have been invited to edit a generated site, start with that repository's `README.md`. It identifies the selected profile, lists the content paths you may edit, and puts the browser-only workflow before local technical setup.

For every GitHub Web edit:

1. Work from an assigned issue or an explicit file reservation accepted by the maintainer. Only one active editor may work on a file.
2. Create one branch per task and never edit `main` directly.
3. Open a small Draft pull request early. If a conflict appears, stop and ask the maintainer instead of overwriting work.
4. The maintainer runs local checks and required renders, reviews the result, merges the pull request, and only then starts deployment manually.

See [Edit Safely In GitHub Web](docs/_documentation/en/06-github-web-editing.md) for the full coordination, image-upload, file-safety, and publication protocol.

## Four Site Profiles

| Profile | Use it for | Main editor-owned paths |
|---|---|---|
| `unaltreselfie` | A personal academic or professional site | `_pages/`, `_posts/`, `_news/`, `_projects/`, `_books/`, `_bibliography/`, approved profile assets |
| `unaltreprojecte` | A research project, group, infrastructure, or output site | `_pages/`, `_news/`, `_projects/`, `_outputs/`, `_books/`, `_data/team.yml`, `_data/repositories.yml`, approved project assets |
| `unaltremanual` | A manual, course, handbook, or book-like publication | `_pages/`, `_chapters/`, `_bibliography/`, `context/writing-profile.md`, approved source images |
| `unaltredocs` | Technical or operational documentation | `_pages/`, `_documentation/`, public `_data/` files, approved screenshots |

## Technical Overview

The core packages shared layouts, includes, Sass, assets, Jekyll plugins, bibliometric tooling, multilingual behaviour, theme modes and reusable GitHub Actions workflows. Child sites stay thin: they keep content, configuration and local assets while reusable implementation remains in this repository.

The default distribution is Docker-first. Generated sites run their normal build, serve and test commands in the published `unaltraweb-mcp` image, which contains the Python control plane and the reviewed core at `/opt/unaltraweb`. The `unaltraweb` gem on RubyGems and the `unaltraweb-mcp` wheel on PyPI remain supported native interoperability channels, not additional requirements for the Docker path.

`unaltremanual` sites can also build language-specific PDF editions and matching web-cover images in an isolated Pandoc/XeLaTeX container. PDF status is offline; build and local review are available through Make and the MCP control plane. When PDF output is enabled, default generated PDF and cover outputs are not versioned. The selected `latest` or stable selector is rendered into both the website and PDF metadata.

## Current Status

- Public release [`v0.5.0`](https://github.com/dosquartsdedocs/unaltraweb/releases/tag/v0.5.0) provides the Docker images, Ruby gem and Python wheel as one receipt-bound distribution.
- `ghcr.io/dosquartsdedocs/unaltraweb-mcp` is the canonical normal local runtime for generated sites; `ghcr.io/dosquartsdedocs/unaltraweb` is its lower-level Jekyll runtime base.
- The Ruby gem supports native Bundler/Jekyll consumers. The modular MCP wheel supports native creation and inspection, and does not bundle the gem, factory checkout, worker images or companion renderers.
- PDF, browser-capture and computation environments remain separate images so the normal site image does not carry every heavy toolchain.
- The companion `../unaltraweb-template` repository remains the full-profile integration fixture and visual demo.
- The project is in its early `0.x` release series. Some inherited `al-folio` implementation details remain while the core is being generalized.

## Repository Roles

- `unaltraweb`: reusable code, theme defaults, plugins, styles, scripts, documentation, reusable workflows and the Docker runtime image.
- `unaltraweb-template`: full-profile demo and Playwright integration fixture for the gem consumer path.
- `docs/`: the public reference site for `unaltraweb`.

The template is the better place to validate gem consumption, centralized styles and shared logic because it exercises `unaltraweb` as an external dependency instead of relying on the core checkout itself.

## Profile Configuration

Prepared site families are called site profiles. The four profiles and their editor-owned paths are summarized near the top of this README.

Select a profile in a child site's `_config.yml`:

```yaml
unaltraweb:
  site_profile: unaltreselfie
  features:
    blog: true
    cv: true
    projects: true
    publications: true
    metrics: true
```

## Quick Start

Create a child site directly with the public Docker image:

```bash
mkdir my-site && \
docker run --rm --network none --user "$(id -u):$(id -g)" -e HOME=/tmp \
  --mount "type=bind,src=${PWD}/my-site,dst=/workspace" \
  ghcr.io/dosquartsdedocs/unaltraweb-mcp@sha256:36d17edbade77edb40a687f6a744203c6329acb33fbc2eb255e88d9ff1a42c98 \
  --project /workspace new-web --site-profile unaltreselfie --title "My site" --default-lang en
```

The chained `mkdir` requires a new destination, the digest binds creation to the reviewed `v0.5.0` receipt, and `--network none` keeps scaffold generation offline.

If Python package tooling is already available, the PyPI adapter exposes the equivalent native command:

```bash
unaltraweb-mcp --project ./my-site new-web --site-profile unaltreselfie --title "My site" --default-lang en
```

From this factory checkout, the equivalent command is:

```bash
MCP_CONSUMER_WORKSPACE=./my-site make mcp-new-web NEW_WEB_PROFILE=unaltreselfie SITE_TITLE="My site" DEFAULT_LANG=en
```

All three creation paths use only assets shipped in `unaltraweb_mcp`, either in the image, wheel or factory checkout. They preflight all managed paths, write `.unaltraweb/scaffold.json`, and never overwrite differing files. Later `scaffold_sync` calls can update unchanged baseline controls or adopt exact current package bytes, including the collaboration contract, Dependabot policy, pull-request template, dependency pins, and deploy caller. Local customizations are preserved when the package has not changed that file since the baseline; conflicting local/upstream changes remain blocked. Synchronization never touches config, README prose, agent guidance, or content. `dosquartsdedocs/unaltraweb-template` remains available when a full multi-profile demo with Playwright tests is more useful than a clean profile-specific site.

The guided update flow reports `site_context.update_status` at session start so the agent can explain available package/scaffold changes and ask whether to apply them. Accepted updates use the existing `scaffold_sync` transaction with the reviewed `plan_sha256` as `expected_plan_sha256`; an older MCP cannot downgrade known newer consumer pins. See [Guided Consumer Updates](docs/_documentation/en/03-usage.md#guided-consumer-updates). This functionality needs a reviewed MCP/package release containing it: updating a discovery checkout alone does not replace the already-running or digest-pinned public image.

After creation, there are two supported editing paths:

- Local Docker editing for previews, larger edits, screenshots, tests, and rendered-output review before publication.
- GitHub-only editing for small content changes, bibliography updates, page edits and simple configuration changes, followed by an explicit manual workflow run when GitHub Pages must publish the site.

Local editing is intended to require only Git, Docker and GNU Make. On Windows, use WSL2 with Docker Desktop and run `make` commands inside the WSL Linux shell.

## Documentation

- `docs/_pages/en/index.md`: public overview for the `unaltraweb` reference site.
- `docs/_documentation/en/`: reference pages for quick start, tools, usage, profiles, syntax, themes, customization, distribution and development.
- `docs/Gemfile`: local child-site Gemfile that consumes this checkout as the `unaltraweb` gem.
- `docs/_config.yml`: docs site config using `theme: unaltraweb` and `site_profile: unaltredocs`.

The core Jekyll build excludes `docs/`. Publish the reference site separately from the `docs/` folder or through a dedicated workflow.
- `TODO.md`: working state, decisions and next tasks.

## Development

Use the core Docker workflow when checking the core repository itself:

```bash
docker compose -f docker-compose.yml run --rm --entrypoint "bash -lc '(bundle check || bundle install) && bundle exec jekyll build --trace'" jekyll
docker compose -f docker-compose.yml down --remove-orphans
```

Use the `unaltraweb` reference-site workflow when checking `docs/` through the real `unaltredocs` profile:

```bash
make docs-serve DOCKER_IMAGE=unaltraweb:dev
make docs-build DOCKER_IMAGE=unaltraweb:dev
```

The public distribution contract selects `ghcr.io/dosquartsdedocs/unaltraweb-mcp:0.5.0` for normal generated-site commands. `MCP_RELEASE_IMAGE` pins the reviewed MCP digest from the same `v0.5.0` receipt for gContExt (formerly ContExt), the GNOME Shell extension. The mutable `:main` channel is reserved for explicit maintainer testing; locally built core images use the `:dev` name.

The lower-level `unaltraweb` image supplies Ruby, Jekyll and runtime dependencies. The MCP image layers the full reviewed factory and installed Python package on top; generated Make targets load the theme as a path gem from `/opt/unaltraweb`. Native Bundler consumers can instead resolve the independently published gem, and native Python users can install the wheel.

The independent manual PDF runtime is built from `scripts/manual/Dockerfile`; it is deliberately separate from the Jekyll image so normal site builds do not carry Pandoc and TeX Live.

Publish images only through the manual Docker workflows. A core-code change requires a new MCP image, while a base-runtime change also requires a new lower-level runtime image. Candidate builds receive package-write authority only after preflight and never execute their outputs; broad aliases move only after a separate read-only job tests the exact digests, and the final package-write job executes no candidate.

When running the core and the template profiles together, keep `unaltraweb` on port `4000` and the template profile servers on `4001` through `4004`.

Use the template when validating the gem consumer path:

```bash
cd ../unaltraweb-template
make build LOCAL_CORE=../unaltraweb
make test LOCAL_CORE=../unaltraweb SITE_PROFILE=unaltreselfie PORT=4018
make test LOCAL_CORE=../unaltraweb SITE_PROFILE=unaltreprojecte PORT=4019
make test LOCAL_CORE=../unaltraweb SITE_PROFILE=unaltremanual PORT=4020
make down
```

The template tests are intentionally heavier because they run browser smoke tests and screenshots. On constrained machines, prefer `make build` first and run Playwright only when needed.

## Modular Wheel And Doctor

The `unaltraweb-mcp` wheel is intentionally a small control and inspection package. A wheel-only install supports `version`, `new-web`, top-level `doctor`, the host-only `import-calibre` command, constrained SHA-256 source edits, baseline-aware `scaffold_sync`, `site-doctor`, `html-audit`, and package-only inspections such as `detect-site`, `profile-check`, `content-inventory`, and `build-health`. Commands that execute factory Make targets or serve MCP require a factory checkout and fail with an explicit `UNALTRAWEB_FACTORY_DIR` remediation when it is absent.

```bash
unaltraweb-mcp version
unaltraweb-mcp doctor
unaltraweb-mcp doctor --project /path/to/site
unaltraweb-mcp doctor --project /path/to/site --docker
```

Doctor is offline. The optional `--docker` mode only calls local Docker version/image inspection and never pulls. Its findings have stable `code`, `severity`, `expected`, `actual`, and `remediation` fields. `src/unaltraweb_mcp/component-contract.json` is the canonical release BOM; its `consumer_integration` object is the single source for the core Git revision, deploy workflow, PDF image digest, and Vega renderer revision emitted into consumer scaffolds. The adjacent versioned JSON Schema defines the machine-readable contract.

## Global Dockerized MCP

`unaltraweb` provides one global, on-demand stdio MCP whose containers are scoped to the current consumer workspace. Each client session gets an independent Docker-generated container name plus stable factory, role, and project labels, so concurrent processes for the same project do not collide. This runtime capability does not authorize overlapping editors: the collaboration control plane uses one primary mutable checkout and one active editing session per repository. gContExt runs `mcp-build` to prepare the exact public image selected by `MCP_RELEASE_IMAGE`, and `mcp-stdio` launches that same digest. To build and test a development image explicitly from this checkout instead:

```bash
make mcp-image
make mcp-smoke-prebuilt MCP_IMAGE=unaltraweb-mcp:dev
```

Source builds use the explicit local names `unaltraweb:dev` and `unaltraweb-mcp:dev` by default, avoiding shadowed public references. After each coordinated release, `MCP_RELEASE_IMAGE` advances to its recorded digest in a separate post-release change; candidate source never embeds its unknown future self-digest.

gContExt reads the canonical manifest transport `make -C ${factoryRoot} mcp-stdio` and supplies `MCP_CONSUMER_WORKSPACE=${workspaceFolder}` through the process environment. The manifest continues to use `make`, an allowed container host launcher, but no consumer path is parsed by Make or interpolated into shell source. The collaboration control plane requests one top-level MCP, selects its declared dependency closure, preserves unrelated user registrations, and runs a read-only checkout preflight before editing. When a process-held cooperative lease is required, it launches the editing command through its `exec` wrapper; it never manipulates Git worktrees implicitly. An equivalent direct MCP launch is:

```bash
MCP_CONSUMER_WORKSPACE="$PWD" make --silent --no-print-directory -C /path/to/unaltraweb mcp-stdio
```

Replace `/path/to/unaltraweb` with this checkout's absolute path and restart the client after changing its configuration. Each session canonicalizes the inherited workspace after launch, then mounts it at `/workspace` and at its canonical host path, so Docker-backed authoring tools pass valid bind paths to the host daemon. `build_site` runs Jekyll directly in that MCP runtime and returns the offline HTML audit. `preview_start`, `preview_status`, and `preview_stop` manage one labelled preview container per project. By default, Docker publishes container port `4000` on a free loopback host port, so previews from distinct workspaces can run concurrently; pass a nonzero `port` only when a fixed host port is required. A running preview created by the former fixed-port default remains idempotently usable until stopped, after which the dynamic default applies. `http_check` derives its origin only from that owned preview and never accepts an arbitrary URL.

Dependency preparation ensures the selected release image and prepares required companions only; it does not initialize a consumer website, and companion `init` aggregation is disabled. Create a site explicitly with the `new_web` MCP tool. To clean up one consumer project, pass the same canonical project path used at launch:

```bash
MCP_CONSUMER_WORKSPACE=/path/to/consumer make mcp-down
# If the path was moved or deleted:
MCP_CONSUMER_WORKSPACE=/old/absent/path MCP_PROJECT_ID=0123456789abcdef make mcp-down
# Or explicitly remove a stale inherited workspace binding:
env -u MCP_CONSUMER_WORKSPACE MCP_PROJECT_ID=0123456789abcdef make mcp-down
```

When the workspace is live, a supplied `MCP_PROJECT_ID` must match its canonical path. A retained ID is accepted only without a live workspace, making stale-resource cleanup explicit. Cleanup selects only resources carrying both `io.context.mcp-factory=unaltraweb` and that project's stable `io.context.mcp-project` label. Maintainers can deliberately clean every labelled unaltraweb MCP resource with `make mcp-down-all`; neither target deletes images or touches unlabelled containers and networks.
Replace the example retained ID with the 16-hex value from that project's `io.context.mcp-project` Docker label.

## Workspace Path Policies

For consumer filesystem ownership, the discovery manifest declares literal
`workspace_rule.path_policies`. The central manager's read-only `workspace-check`
checks the selected consumer plus the installable `diavisuals` and `vegavisuals`
dependency closure. It does not run provider commands or clean files. In
particular, ignored PDF recovery state and `tmp` require explicit review, not
blanket deletion. See [Workspace Path Policies And Consumer Updates](docs/_documentation/en/43-workspace-path-policies.md)
for the audited paths, package/scaffold compatibility and migration procedure.

## Bibliometrics

Normal Jekyll builds must stay static. External metrics are fetched only through explicit update commands and written back to local data files before build time.

```bash
make metrics-scimago-fetch
make metrics-update
make metrics-check
```

Use `METRICS_ARGS` and `SCIMAGO_INPUT` for local safety checks and local Scimago files:

```bash
make metrics-update METRICS_ARGS="--strict-external --require-scimago"
make metrics-scimago-fetch SCIMAGO_INPUT=path/to/scimagojr.csv
```

The manual metrics workflow keeps Scimago caches and temporary diagnostics out of pull requests. When PR creation is enabled, it includes only versionable generated data: `_bibliography/**/*.bib` and `_data/metrics.yml`.

## Formatting

`package.json` declares Prettier and the Liquid plugin. Regenerate `package-lock.json` with `npm install` on a machine with Node/npm available; do not hand-edit dependency integrity data.

`npm` is development tooling, not part of the docs deploy or normal Jekyll runtime. If we need a containerized workflow for it, prefer a small dedicated Node tooling image or GitHub Action over adding Node/npm to every Jekyll build path.

## Publishing Docs

The core repository can deploy the `unaltraweb` reference site from `docs/` through the manual `.github/workflows/deploy.yml` workflow. Configure GitHub Pages for GitHub Actions deployments when using that route.

It can also publish the reference site locally to `gh-pages`:

```bash
make docs-publish
```

Sites created by `new_web` include a manual GitHub Pages workflow that requires the full locally reviewed `main` commit SHA before delegating to the reusable core workflow. The current reusable publication contract also requires the manual PDF worker by full image digest. Local `make build` and `make test` validate the site without publishing or changing Git history.

## CI Scope

`.github/workflows/ci.yml` runs automatically for pushes and pull requests. It compiles and unit-tests supported Python versions, checks patch whitespace, validates workflow and distribution structure, builds/tests the wheel and gem, then uses cached Docker builds for the MCP smoke test and reference docs. This normal CI intentionally runs `distribution-check`, not the strict release gate, so truthful `pending` releases do not block ordinary development.

CodeQL separately analyzes JavaScript/TypeScript, Python and Ruby on pull requests, default-branch pushes and a weekly schedule. Docs deployment, link checks, publication metrics, package preparation and image publication remain manual.

Core artifact workflows run `distribution-check` while selected candidates are truthfully `pending`, validate the selected ref against the BOM version, and keep their preflight jobs credential-free. Once the final source and release intent are reviewed, mark its components `ready` and commit that state. Runtime, MCP, and manual PDF publication separates authority across a signing/package-write build job that never runs candidates, a read-only test job that verifies GitHub-signed digest/source provenance and removes GHCR credentials before execution, and a package-write promotion job that executes no candidate. Every image is built once under only its SHA tag; only the exact digests that pass all Ruby, PDF, reproducibility, MCP, and docs gates can reach verified `sha-*`, `main`, and `latest` aliases. GHCR does not make the initial absence lookup and later tag write atomic, so the signed tested digest, not a claim of compare-and-swap no-clobber, is the trust anchor. Record those digests and package checksums in a versioned `release-candidates.json` child commit; validation requires that receipt to be the only change. After `distribution-release-check`, tag the receipt commit. The tag-only job verifies SHA-tag equality, signed provenance and the revision label against the receipt's `source_commit`, then promotes only the receipt's manifests to checked semver aliases without rebuilding or executing them. `released` remains available for already-published components; released containers other than the self-describing MCP must be digest-pinned. Package preparation never uploads to RubyGems or PyPI, creates a GitHub release, tags the repository or publishes an image; those operations require separate explicit maintainer approval.

After tagging, maintainers can use the manual `Publish language packages` workflow with PyPI and RubyGems Trusted Publishing. Its read-only job binds a successful package-preparation run and immutable artifact to the release receipt, verifies the exact file inventory and SHA-256 values, and only then passes one wheel and one gem to separate environment-protected OIDC jobs. Those jobs do not checkout source, rebuild candidates, receive repository write authority, or use stored registry tokens. See [Distribution](docs/_documentation/en/40-distribution.md) for the one-time publisher setup and dispatch inputs.

## Attribution

`unaltraweb` started from the open-source `al-folio` Jekyll theme and is being refactored into a self-owned reusable core for `dosquartsdedocs` sites. Retain upstream attribution where inherited code remains relevant.
