Skip to content

Deliver a page's figures, embeds, diagrams and videos to the site - #313

Merged
egeozin merged 6 commits into
mainfrom
feat/page-bundles-delivery
Oct 5, 2026
Merged

egeozin merged 6 commits into
mainfrom
feat/page-bundles-delivery

Conversation

@egeozin

@egeozin egeozin commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

What and why

Page bundles, part 2: delivery. It builds on #312, which builds on #311, so merge those first. Plan: https://with.md/s/6bxvmfrh

With #311 and #312, a draft carries figures, interactive pieces, diagrams, videos and callouts, and the founder reviews them in Tin. This PR puts them on the founder's site in the site's own form, through content.deliver (the approval's "Open a pull request") and website.change.

What changed

  • Delivery prompts (content.deliver 1.7.0 and website.change 1.6.0, prompts and skills). The approved page's assets list each file's project path. Codex copies each asset from /home/user/state/<path> into the place the site keeps such files, and shows it the way the site shows media:

    • an SVG becomes an image or the site's figure component, with its alt text and caption;
    • an interactive piece becomes an iframe or an inline component, with its script and data unchanged;
    • each mermaid block becomes SVG with the sandbox's offline renderer, unless the site has its own diagram support: node /opt/tin-lite/diagram/scripts/check_diagram.mjs check <file>.mmd --out <dir> --no-previews writes light.svg and dark.svg;
    • videos and callouts use the site's own components or the provider's embed, and a video file is linked rather than copied.
  • The approved revision. procedure_project_revision checks out the approved revision for a delivery run whose page has assets, so the files Codex copies are exactly the approved ones. Pages without assets keep the canonical head as before.

  • figure_check, beside copy_check:

    • every approved file must arrive byte for byte (the same checksum);
    • every video by its address or provider ID;
    • every diagram as a new SVG, not a copied figure, or the site's own Mermaid support.

    When something isn't confirmed, the PR still opens and Tin leaves the merge to the founder: "Tin couldn't confirm every approved figure, interactive piece, diagram and video is on the page, so it waits for your review."

  • Limits. A page with a dozen figures plus its route and sitemap needs more room:

    • GitHub writes: pull requests and commits go from 10 files and 512 KB to 30 files and 2 MB (PULL_REQUEST_MAX_*). The merge goes from 20 files to 30.
    • A latent bug fixed along the way: the 10-file cap also refused technical batches that site-fix-v5 allows at 20 files, after the paid run.
    • Procedure ceilings: 30 files and 2 MB. The default stays 512 KB, so procedures that declare nothing keep their exact contract.
    • Saved patches: they read back up to 4 MB, twice the new ceiling, like the existing 2× manifest bound.

No migration and no Temporal change. Runs keep the versions they pinned.

Rollout

  1. Merge and deploy Let article drafts carry figures and embeds, approved with the article #311.
  2. Rebuild the Codex sandbox templates.
  3. Merge and deploy Show a draft's figures, embeds and videos, and let drafts use them #312, then this PR.

The diagram renderer is already in the isolated template, so this PR needs no extra rebuild.

Verification

  • test_delivery_confirms_every_figure_embed_diagram_and_video_arrived:
    • A static page with the copied SVG, the embed, a YouTube embed and a rendered diagram is wording_preserved and confirmed, and merges under the chosen route.
    • Dropping the embed, the figure, the video or the diagram makes it not_confirmed, and the merge waits.
    • A page without figures has no figure check.
  • test_a_delivery_with_assets_reads_them_where_they_were_approved: the checkout pin.
  • test_github_writes_fit_a_page_with_its_figures: the new caps, the unchanged default, and content.deliver's 30 files and 2 MB.
  • Pin updated: content.deliver 1.7.0's digest.
  • Python suite: 5,286 passed, 22 skipped against a disposable Postgres. The 2 macOS-only localtime tests fail as on main. ruff, format, lint-imports and dump_catalog.py --check are clean.

After the rollout, a real run: a bundle draft approved with "Open a pull request" on a test site, with the figures checked on the live page.

🤖 Generated with Claude Code

ege-em and others added 3 commits October 5, 2026 12:42
Page bundles, part 1a (the contract). An article draft whose definition declares
output.assets may write SVG figures and self-contained HTML embeds into its
assets folder (<article>.assets/) and refer to them from the Markdown.

- The article declares its assets: Tin keeps exactly the files it refers to,
  records their checksums with the checkpoint and publishes them with the
  article. A missing, oversized or unsafe file (an SVG with script, event
  handlers or outside references) is left out with its reason; it never fails
  the run. Files nothing refers to are never published.
- The sandbox runner allows files directly in the folder, discards anything
  else there, applies runaway limits and the identity/card leak scan, and
  commits them; --check-assets lets the host refuse an old image before any
  model call.
- The review token binds the asset list with the article; approved-page
  consumers re-check each file; a revision copies the files it keeps into its
  own folder; compare lists each version's assets.
- The delivery wording check skips embed, video and diagram blocks.

No workflow turns this on yet: definitions without assets keep their exact
contract and tokens. The sandbox templates need a rebuild before part 1b
enables it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Page bundles, part 1b (the experience), on top of #311's contract.

- Reader: render_markdown takes the article's assets folder. Figures render
  without a URL and load with the member's session (SVG as an inert data:
  image, as in Files); tin-embed blocks run in an allow-scripts-only sandboxed
  frame with an injected policy that blocks the network; tin-video blocks play
  YouTube (privacy mode), Vimeo, Loom or Mux, a video file loads only on play,
  and anything else stays a link; `> [!NOTE]`-style quotes become callouts. A
  note under the title names any file Tin left out and why.
- The run and Files document endpoints return the kept assets; the app passes
  a session-bound loader to the viewer.
- Decisions: the explanation says what the draft carries ("With 2 figures and
  1 interactive piece.").
- Drafts: content.public_article 1.9.0 and content.generate 1.10.0 (articles
  only) declare an assets folder and use a new page-figures skill: figures only
  where they show what words can't, Mermaid for diagrams, self-contained embeds
  with no network, existing videos only, and kept figures from an existing page.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Page bundles, part 2, on top of #312.

- content.deliver 1.7.0 and website.change 1.6.0 copy each approved asset
  from the project checkout, now pinned to the approved revision when a page
  has assets, and show it the way the site shows media; Mermaid blocks become
  SVG with the sandbox's offline renderer unless the site has its own diagram
  support; videos and callouts use the site's own components.
- figure_check beside copy_check: approved files byte for byte, videos by
  address or ID, diagrams as new SVG or the site's Mermaid support. Unconfirmed
  holds only Tin's own merge; the PR still opens.
- Limits: one pull request or commit may hold 30 files and 2 MB (it held 10
  files and 512 KB, which also refused technical batches the procedure allowed
  at 20 files); a saved patch reads back up to 4 MB. Procedures that declare
  nothing keep their 512 KB contract.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@egeozin
egeozin requested a review from a team as a code owner October 5, 2026 20:25
egeozin and others added 2 commits October 5, 2026 14:03
)

Page bundles, part 1b (the experience), on top of #311's contract.

- Reader: render_markdown takes the article's assets folder. Figures render
  without a URL and load with the member's session (SVG as an inert data:
  image, as in Files); tin-embed blocks run in an allow-scripts-only sandboxed
  frame with an injected policy that blocks the network; tin-video blocks play
  YouTube (privacy mode), Vimeo, Loom or Mux, a video file loads only on play,
  and anything else stays a link; `> [!NOTE]`-style quotes become callouts. A
  note under the title names any file Tin left out and why.
- The run and Files document endpoints return the kept assets; the app passes
  a session-bound loader to the viewer.
- Decisions: the explanation says what the draft carries ("With 2 figures and
  1 interactive piece.").
- Drafts: content.public_article 1.9.0 and content.generate 1.10.0 (articles
  only) declare an assets folder and use a new page-figures skill: figures only
  where they show what words can't, Mermaid for diagrams, self-contained embeds
  with no network, existing videos only, and kept figures from an existing page.

Co-authored-by: egeozin <ege@emotionmachine.ai>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	tests/test_page_assets.py
@egeozin
egeozin changed the base branch from feat/page-bundles-reader to feat/page-bundles-contract October 5, 2026 21:05
# Conflicts:
#	src/tin_lite/content_repository_delivery.py
#	tests/test_page_assets.py
@egeozin
egeozin changed the base branch from feat/page-bundles-contract to main October 5, 2026 21:11
@egeozin
egeozin merged commit 2fcbd08 into main Oct 5, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants