Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion codex_procedures/content.generate/PROMPT.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ Write exactly two UTF-8 Markdown files at the declared `output.path` and
`output.companion_path`. For `draft`, the primary file is public copy only: start with a `# Title`,
then the complete article/page draft with factual sources beside the supported claims.
Shape it with the search-and-answer-engines skill: answer first, question headings, a table
when comparing options, an FAQ where it fits and `## Sources` last.
when comparing options, an FAQ where it fits and `## Sources` last. For an article, use the
page-figures skill for any figure, diagram, interactive piece, video or callout it needs; an
answer page or a page refresh has none.
No Tin frontmatter, checklists, generation notes or approval commentary belongs in this file.
For any no-draft outcome, write a short assessment instead of an article. Its exact format is
`# Content assessment`, a blank line, the exact `rationale` string from the judgment below,
Expand Down
77 changes: 77 additions & 0 deletions codex_procedures/content.generate/skills/page-figures/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
name: page-figures
description: Add figures, diagrams, interactive pieces, videos and callouts to an article draft only where they show what the words can't, as files in its assets folder that the founder approves with the article.
---

# Figures, diagrams, interactive pieces and videos

Add one only where it shows something the words can't carry as well: a process, a comparison,
a structure, a measured result. Most articles need none, one or two. Never decorate.

Your output instructions name the article's assets folder. Write each file directly in it and
refer to it from the article with a path that starts `./<folder name>/`. Tin keeps only the files
the article refers to, and the founder approves them with the words.

## Diagram

A `mermaid` block for a flow, a sequence, a hierarchy or a timeline. Tin draws it in the
project's brand colours, so don't set colours or styles in it. Keep it to the few nodes the point
needs.

## Figure

An SVG file in the assets folder:

```markdown
![What the figure shows](./<folder name>/<file>.svg "What the reader should notice")
```

The alt text says what the figure shows; the caption says what to notice. Draw it plainly, with
real labels and the article's own numbers. The SVG must have no script, no event handlers, no
`foreignObject` and no links or images from elsewhere; Tin leaves out an SVG that has any.

## Interactive piece

One self-contained HTML file in the assets folder, only when interaction teaches something a still
figure can't, such as a simulation or a calculator:

````markdown
```tin-embed
src: ./<folder name>/<file>.html
height: 420
title: What the reader can do with it
```
````

Inline its CSS and JavaScript. It runs with no network access: no requests, no outside fonts,
libraries or images. Keep it small, give it a sensible default state, and make it work with a
mouse, a keyboard and a touch screen.

## Video

A video that already exists at a public address, on the founder's site or on YouTube, Vimeo, Loom
or Mux:

````markdown
```tin-video
url: https://www.youtube.com/watch?v=...
title: What it shows
```
````

Never invent a video or an address. A long video file is linked, never copied into the folder.

## Callout

`> [!NOTE]`, `> [!TIP]` or `> [!WARNING]` on the first line of a quote, for a short aside the
reader shouldn't miss. Use one or two at most.

## Existing pages

When the brief points to an existing page that has figures and you can read its source, keep
them: copy an inline SVG into the folder as its own file, or rebuild an interactive piece as an
embed with its script and data. Don't describe a figure in words instead of keeping it. If you
can't carry one over, say which and why in Generation notes.

List every figure, diagram, interactive piece and video in Generation notes with one line on what
each shows.
8 changes: 5 additions & 3 deletions codex_procedures/content.public_article/PROMPT.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ Plan before drafting, but publish only the finished article in the declared Mark
article should open in the reader's world, land one salient idea, explain the mechanism, use concrete
evidence, acknowledge material assumptions and limitations, and leave the reader with a useful
contribution. Shape it with the search-and-answer-engines skill: its search-listing frontmatter
comes first, then the `# ` title and an answer-first opening. Apply the public-article-edit pass
before finishing.
comes first, then the `# ` title and an answer-first opening. Use the page-figures skill for any
figure, diagram, interactive piece, video or callout the article needs. Apply the public-article-edit
pass before finishing.

This is a reviewable draft, not a publishing action. Do not change another project file, publish to
This is a reviewable draft, not a publishing action. Do not change another project file (the
article's assets folder is part of the draft), publish to
a website, send the article, contact anyone, or ask an interactive question. Put unresolved choices
or missing evidence into the separate Generation notes document, never the public copy.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
name: page-figures
description: Add figures, diagrams, interactive pieces, videos and callouts to an article draft only where they show what the words can't, as files in its assets folder that the founder approves with the article.
---

# Figures, diagrams, interactive pieces and videos

Add one only where it shows something the words can't carry as well: a process, a comparison,
a structure, a measured result. Most articles need none, one or two. Never decorate.

Your output instructions name the article's assets folder. Write each file directly in it and
refer to it from the article with a path that starts `./<folder name>/`. Tin keeps only the files
the article refers to, and the founder approves them with the words.

## Diagram

A `mermaid` block for a flow, a sequence, a hierarchy or a timeline. Tin draws it in the
project's brand colours, so don't set colours or styles in it. Keep it to the few nodes the point
needs.

## Figure

An SVG file in the assets folder:

```markdown
![What the figure shows](./<folder name>/<file>.svg "What the reader should notice")
```

The alt text says what the figure shows; the caption says what to notice. Draw it plainly, with
real labels and the article's own numbers. The SVG must have no script, no event handlers, no
`foreignObject` and no links or images from elsewhere; Tin leaves out an SVG that has any.

## Interactive piece

One self-contained HTML file in the assets folder, only when interaction teaches something a still
figure can't, such as a simulation or a calculator:

````markdown
```tin-embed
src: ./<folder name>/<file>.html
height: 420
title: What the reader can do with it
```
````

Inline its CSS and JavaScript. It runs with no network access: no requests, no outside fonts,
libraries or images. Keep it small, give it a sensible default state, and make it work with a
mouse, a keyboard and a touch screen.

## Video

A video that already exists at a public address, on the founder's site or on YouTube, Vimeo, Loom
or Mux:

````markdown
```tin-video
url: https://www.youtube.com/watch?v=...
title: What it shows
```
````

Never invent a video or an address. A long video file is linked, never copied into the folder.

## Callout

`> [!NOTE]`, `> [!TIP]` or `> [!WARNING]` on the first line of a quote, for a short aside the
reader shouldn't miss. Use one or two at most.

## Existing pages

When the brief points to an existing page that has figures and you can read its source, keep
them: copy an inline SVG into the folder as its own file, or rebuild an interactive piece as an
embed with its script and data. Don't describe a figure in words instead of keeping it. If you
can't carry one over, say which and why in Generation notes.

List every figure, diagram, interactive piece and video in Generation notes with one line on what
each shows.
14 changes: 14 additions & 0 deletions docs/content-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ the next roadmap item.
separate artifact and viewer action, never text or a link embedded in public copy.
- Approval is bound to the current reviewed revision. An old approval cannot approve
a newer draft. Only the final approved copy is eligible for configured delivery.
- An article (content.public_article 1.9.0, content.generate 1.10.0 articles) may carry
figures, diagrams, interactive pieces, videos and callouts. The reader shows them as
they'll ship: SVG figures as images, embeds in a sandboxed frame with no network or
access to Tin, videos from YouTube, Vimeo, Loom or Mux (a video file loads only on
play), and a note under the title for any file Tin left out. The Decisions card says
what the draft carries, for example "With 2 figures and 1 interactive piece."
- A failed revision needs explicit retry. Feedback is not discarded by generic retry,
and a stopped revision closes its waiting review ancestry.
- Discard turns down anything waiting in Decisions: the run ends as declined, nothing it
Expand All @@ -40,6 +46,14 @@ outbox bridges dispatch; Temporal carries command/run identifiers, not feedback
article contents. Preserve historical adapter/output contracts and exact-version
guards. Do not infer approval from a generated no-draft assessment.

An article draft whose definition declares `output.assets` may carry figures and embeds in
its assets folder (`<article>.assets/`, `src/tin_lite/page_assets.py`). The article declares
them: Tin keeps only the files it refers to, leaves out a missing, oversized or unsafe one
with its reason (never a failed run), and records each kept file's checksum with the
checkpoint. The review token binds that list with the article, approved-page consumers
re-check it, and a revision copies the files it keeps into its own folder. Drafts without
assets keep their exact token and contract.

The shared implementation lives in `src/tin_lite/workflow_reviews.py`,
`workflow_review_store.py`, `workflow_review_dispatch.py` and `article_review.py`.
The dashboard composer is `src/tin_lite/static/workflow-review.js`.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"type": "module",
"scripts": {
"test:browser": "node --test web/raw-file-security.browser.test.js web/fonts.browser.test.js web/diagram-renderer.browser.test.js web/diagram-viewport.browser.test.js scripts/check_diagram.test.mjs web/json-viewer.test.js web/json-viewer.browser.test.js web/output-comparison.browser.test.js web/content-plan.browser.test.js web/content-plan-page.browser.test.js web/billing.browser.test.js web/run-payment-card.browser.test.js web/style-capture.browser.test.js web/content-draft.browser.test.js web/content-delivery.browser.test.js web/workflow-review.browser.test.js web/auth.browser.test.js web/mcp-consent.browser.test.js web/app-domain.browser.test.js web/lock-page.browser.test.js web/project-menu.browser.test.js web/chat.browser.test.js web/project-connections.browser.test.js web/code-workflow.browser.test.js web/page-url.browser.test.js web/files-page.browser.test.js web/narrow-layout.browser.test.js web/decisions.browser.test.js web/website-changes.browser.test.js web/integration-resource.browser.test.js web/x-posts.browser.test.js web/x-feedback.browser.test.js web/x-reader-page.browser.test.js web/x-integration.browser.test.js web/x-workflow-setup.browser.test.js web/monthly-schedule.browser.test.js",
"test:browser": "node --test web/raw-file-security.browser.test.js web/fonts.browser.test.js web/diagram-renderer.browser.test.js web/diagram-viewport.browser.test.js scripts/check_diagram.test.mjs web/json-viewer.test.js web/json-viewer.browser.test.js web/output-comparison.browser.test.js web/content-plan.browser.test.js web/content-plan-page.browser.test.js web/billing.browser.test.js web/run-payment-card.browser.test.js web/style-capture.browser.test.js web/content-draft.browser.test.js web/content-delivery.browser.test.js web/workflow-review.browser.test.js web/auth.browser.test.js web/mcp-consent.browser.test.js web/app-domain.browser.test.js web/lock-page.browser.test.js web/project-menu.browser.test.js web/chat.browser.test.js web/project-connections.browser.test.js web/code-workflow.browser.test.js web/page-url.browser.test.js web/files-page.browser.test.js web/narrow-layout.browser.test.js web/decisions.browser.test.js web/website-changes.browser.test.js web/integration-resource.browser.test.js web/x-posts.browser.test.js web/x-feedback.browser.test.js web/x-reader-page.browser.test.js web/x-integration.browser.test.js web/x-workflow-setup.browser.test.js web/monthly-schedule.browser.test.js web/page-bundles.browser.test.js",
"test:x-browser": "node --test web/x-posts.browser.test.js web/x-feedback.browser.test.js web/x-reader-page.browser.test.js web/x-integration.browser.test.js web/x-workflow-setup.browser.test.js web/monthly-schedule.browser.test.js",
"test:files-browser": "node --test web/files-page.browser.test.js",
"test:narrow-layout-browser": "node --test web/narrow-layout.browser.test.js",
Expand Down
18 changes: 18 additions & 0 deletions sandbox/procedure_app_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,22 @@ def _identity_instruction(context: dict) -> str:
)


def _assets_instruction(output: dict[str, Any]) -> str:
"""The one exception to "modify no other project file": the article's assets folder."""
assets = output.get("assets")
if not isinstance(assets, dict):
return ""
folder = str(assets["folder"])
name = folder.rsplit("/", 1)[-1]
return (
f" The article may also use figures and embeds: write them as files directly in "
f"`{STATE_DIR / folder}` (SVG images, or self-contained HTML files for interactive "
f"pieces), at most {assets['max_files']} files and {assets['max_bytes']} bytes "
f"together, and refer to each one from the article as `./{name}/<file name>`. Tin "
"keeps only the files the article refers to."
)


def _result_instruction(output: dict[str, Any], output_kind: str, output_path: object) -> str:
if output_kind != "project.artifact":
text = (
Expand Down Expand Up @@ -382,12 +398,14 @@ def _result_instruction(output: dict[str, Any], output_kind: str, output_path: o
f"the structured judgment only to `{companion}` "
f"(at most {output['companion_max_bytes']} bytes). Modify no other project file. "
"Do not force an article when current coverage already satisfies the brief."
+ _assets_instruction(output)
)
return (
f"Write the public article only to `{absolute}`. Write internal generation notes "
f"only to `{companion}` (at most {output['companion_max_bytes']} bytes). "
"Modify no other project file. Do not put notes or source frontmatter in the article. "
"Do not merely describe the artifacts in your final response."
+ _assets_instruction(output)
)
workspace_note = ""
if STATE_DIR != WORKSPACE:
Expand Down
Loading
Loading