synthesis-promotion-gate · v2.0.0 · 2026-10-06 · sha256 aaa8ce37c062efae

synthesis-promotion-gate v2.0.0A

Immutable. This exact content never changes and is served at /api/v1/blob/aaa8ce37c062efae.

---
name: synthesis-promotion-gate
description: "Scan a built site for configured internal markers (draft notes, placeholders, private comments) before an approved deploy, and refuse on any hit. Use for publication gates, rendered-output inspection, pre-deploy marker scans or build.sh deploy checks."
license: "Apache-2.0"
depends_on: ["synthesis-grounding-discipline", "synthesis-implementation-integrity"]
metadata:
  author: "Rajiv Pant"
  version: "2.0.0"
  source_repo: "github.com/synthesisengineering/synthesis-skills"
  source_type: "public"
  format: v5
---

# Synthesis Promotion Gate

Reads the built site the way a reader will see it, before the deploy command may publish it, and stops the deploy when a configured internal marker survived the build.

## Binding rules

1. **A successful build is not a publication-safety signal.** A build shows a renderer accepted its inputs; promotion needs a second judgment over the outgoing files.
2. **The gate is evidence, not permission.** The deploy still needs the principal's approval of that exact command (the v5 deploy guard, R3.2), and a clean scan covers only the markers configured.
3. **Refuse, never warn:** a hit (exit 1), an unusable config, a missing folder or a scan that found no files (exit 2) stops the deploy.
4. **Name the view judged:** `text` (what a reader sees), `headings`, `comments` or `source`. `text` is not all browser-visible text: attributes, code, scripts and styles are left out.
5. **The destination's parser is the arbiter of what a reader sees.** The gate's small HTML reader is held to a corpus that parser produced; when they disagree, add the case to the corpus and fix the reader. The `source` view catches a marker however the page parses.
6. **One canonical identity per marker,** whose pattern matches at least one positive example and no negative one; a pattern that matches nothing protects nothing.
7. **Keep patterns tied to observed pipeline scaffolding.** A pattern that matches ordinary language is repaired or removed: approval fatigue is not safety.
8. **The unverified remainder is never none.** The scan sees only built files matching its globs: not consumers served from elsewhere, remote bytes after the deploy, or client-side changes.
9. **A failing fixture comes before the repair** for a changed view or a new kind of marker.

## Contents

- [references/configuration.md](references/configuration.md): the marker config, the four views and what each leaves out, the marker policy, and wiring the scan into `build.sh`. Read it when writing or changing the config.
- [references/coverage-map.md](references/coverage-map.md): where each part of the 1.0.0 text and each script change now lives (ruling D8).
- [references/preserved.md](references/preserved.md): the 2.0.0 text for the retired engine (isolated build, routes, receipts, `check` and `enforce`), verbatim. Read only to review the change.
- Run it, Tests: below.

## Run it

In the site repository's `build.sh`, between the build and the deploy:

```bash
python3 <synthesis-promotion-gate-root>/scripts/promotion_gate.py dist --config .agents/promotion-markers.json || exit 1
```

It prints one line per hit, `<file>[:line]: <marker id> in <view> (<rationale>)`, then a summary such as `42 file(s) scanned, 3 marker(s), 0 hit(s): clean`. Exit 0 clean; 1 hits found, so do not deploy; 2 the config, the folder or an empty scan made the result meaningless. The matched text itself is not printed; open the file at the reported place.

## Tests

`python3 -m pytest -q skills/synthesis-promotion-gate/tests` runs the rendered-view corpus (inline adjacency, entities, repaired headings, attribute, code and hidden-container exclusions), the five round-two defects behind a successful build, and the policy refusals.