Skip to content

docs: dev note for workflow chaining#775

Open
andreatgretel wants to merge 2 commits into
mainfrom
andreatgretel/docs/hitl-document-extraction
Open

docs: dev note for workflow chaining#775
andreatgretel wants to merge 2 commits into
mainfrom
andreatgretel/docs/hitl-document-extraction

Conversation

@andreatgretel

Copy link
Copy Markdown
Contributor

📋 Summary

Adds a dev note and runnable recipe for workflow chaining as a pause/inspect/resume control surface. The post focuses on what named stage boundaries enable, including human review, reusable intermediate artifacts, downstream resume, and future workflow shapes.

🔗 Related Issue

N/A

🔄 Changes

  • Adds the "Pause, Inspect, Resume" dev note with hero/diagram/result assets for the workflow chaining story.
  • Adds a headless document review gate recipe that runs to review_candidates, writes a reviewed artifact, and resumes downstream from that artifact.
  • Adds the recipe page, recipe card, dev note card, and Fern navigation entry.
  • Adds tests covering page generation, review artifact creation, and the end-to-end recipe output.
  • Compresses large PNG assets below the pre-commit 600 KB limit; the workflow chaining diagram now uses a clean no-grid background.

🧪 Testing

  • .venv/bin/ruff check .
  • .venv/bin/ruff format --check .
  • .venv/bin/pytest packages/data-designer/tests/docs/test_document_review_gate_recipe.py
  • env npm_config_cache=/private/tmp/npm-cache-datadesigner make check-fern-docs (0 errors, 1 existing Fern warning)

✅ Checklist

  • Follows commit message conventions
  • Commits are signed off (DCO)
  • Architecture docs updated (N/A - docs/recipe only)

@github-actions

Copy link
Copy Markdown
Contributor

Fern preview: https://nvidia-preview-pr-775.docs.buildwithfern.com/nemo/datadesigner

Fern previews include the docs-website version archive with PR changes synced into latest. Notebook tutorials are rendered without execution outputs in previews.

@andreatgretel andreatgretel changed the title docs: add workflow chaining review gate docs: dev note for workflow chaining Jun 26, 2026
The new part is the boundary. Because the boundary has a name, you can stop there, inspect the selected output, replace it with an approved artifact, and resume downstream without rerunning the trusted upstream work.

```python
from data_designer.interface import ResumeMode

@nabinchha nabinchha Jun 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes me wonder if ResumeMode should be moved to config so we can just do dd.ResumeMode ....


In that shape, `quality_gate` is both a normal stage and a contract. Upstream work promises a schema. Downstream work consumes that schema. A reviewer, evaluator, dashboard, or cleanup script only has to preserve the contract.

![Linear workflow chain showing source rows, named stages, a durable stage boundary, and downstream resume](/assets/document-hitl/workflow-chaining-linear-chain.png)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something seems off here....
Image

Comment on lines +83 to +85
The demo task is document field extraction: generate synthetic invoices and forms, propose boxes around the fields to extract, and turn those boxes into structured rows. A weak detector proposes boxes on each page and assigns uncertainty. The workflow pauses at `review_candidates`, where all rows are still present but only the uncertain rows are marked for review.

The reviewer corrects the proposed boxes for that uncertain slice. They do not relabel the whole dataset, and they do not change the workflow shape. They write a replacement artifact with the same row count and schema, then the downstream stages use human-corrected boxes where they exist and calibrated detector boxes everywhere else.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe conditional generation could achieve the same goal within a same DD stage? Just calling it out. Might be interesting to offer as an alternative?

Signed-off-by: Andre Manoel <amanoel@nvidia.com>
@andreatgretel andreatgretel marked this pull request as ready for review July 7, 2026 20:46
@andreatgretel andreatgretel requested a review from a team as a code owner July 7, 2026 20:46
@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a workflow-chaining docs story and runnable review-gate recipe. The main changes are:

  • A new headless document review gate recipe.
  • New Fern recipe and dev note pages.
  • New navigation entries, cards, redirect, and image assets.
  • Tests for recipe generation, review artifacts, and final output.

Confidence Score: 4/5

The recipe repeat-run path needs a fix before merging.

  • Reusing an artifact directory can silently ignore a new record count or seed.
  • The CLI also accepts zero records and lets that reach a workflow setup error.
  • The Fern links and navigation changes look consistent with the added pages.

docs/assets/recipes/workflow_chaining/document_review_gate.py

Important Files Changed

Filename Overview
docs/assets/recipes/workflow_chaining/document_review_gate.py Adds the runnable workflow-chaining recipe; repeat runs can reuse stale metadata, and zero-record CLI input reaches a workflow setup error.
packages/data-designer/tests/docs/test_document_review_gate_recipe.py Adds happy-path tests for page generation, review artifact creation, and final dataset export.
fern/versions/latest/pages/devnotes/posts/annotate-hard-pages-review-gates.mdx Adds the workflow-chaining dev note with images and a link to the recipe.
fern/versions/latest/pages/recipes/workflow_chaining/document_review_gate.mdx Adds the Document Review Gate recipe page and download link.
fern/versions/latest.yml Adds navigation entries for the new dev note and recipe.
fern/docs.yml Adds a redirect from the older dev-note path to the new page.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Generate or load page metadata] --> B[Run to review_candidates]
  B --> C[Export review candidates]
  C --> D[Write reviewed artifact]
  D --> E[Resume with stage output override]
  E --> F[Calibrate extractor]
  F --> G[Extract remaining rows]
  G --> H[Export final dataset]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart LR
  A[Generate or load page metadata] --> B[Run to review_candidates]
  B --> C[Export review candidates]
  C --> D[Write reviewed artifact]
  D --> E[Resume with stage output override]
  E --> F[Calibrate extractor]
  F --> G[Extract remaining rows]
  G --> H[Export final dataset]
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
docs/assets/recipes/workflow_chaining/document_review_gate.py:219-222
**Cached Metadata Ignores Run Arguments**

When the same artifact directory is reused without `--overwrite`, this branch returns the existing metadata parquet without checking the requested `count` or `seed`. A user can run once with 12 records, rerun with `--num-records 24`, and still get a final dataset with the old 12 rows because `build_workflow()` sizes the workflow from the cached dataframe.

### Issue 2 of 2
docs/assets/recipes/workflow_chaining/document_review_gate.py:790
**Zero Records Crash Workflow Setup**

`--num-records 0` is accepted by the CLI and reaches `build_workflow()`, where the generated page dataframe has length 0 and the first stage is added with `num_records=0`. The workflow API rejects that value, so this advertised runnable recipe exits with an engine error instead of handling the CLI input at the recipe boundary.

Reviews (1): Last reviewed commit: "docs: clarify workflow chaining value" | Re-trigger Greptile

Comment on lines +219 to +222
if path.exists() and not force:
df = pd.read_parquet(path)
validate_metadata_rows(df)
return df

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Cached Metadata Ignores Run Arguments

When the same artifact directory is reused without --overwrite, this branch returns the existing metadata parquet without checking the requested count or seed. A user can run once with 12 records, rerun with --num-records 24, and still get a final dataset with the old 12 rows because build_workflow() sizes the workflow from the cached dataframe.

Context Used: Do not suggest defensive coding patterns such as a... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/assets/recipes/workflow_chaining/document_review_gate.py
Line: 219-222

Comment:
**Cached Metadata Ignores Run Arguments**

When the same artifact directory is reused without `--overwrite`, this branch returns the existing metadata parquet without checking the requested `count` or `seed`. A user can run once with 12 records, rerun with `--num-records 24`, and still get a final dataset with the old 12 rows because `build_workflow()` sizes the workflow from the cached dataframe.

**Context Used:** Do not suggest defensive coding patterns such as a... ([source](greptile.json))

How can I resolve this? If you propose a fix, please make it concise.

def build_arg_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Headless workflow chaining review gate recipe.")
parser.add_argument("--model-alias", default="unused", help="Accepted for recipe runner compatibility.")
parser.add_argument("--num-records", type=int, default=12)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Zero Records Crash Workflow Setup

--num-records 0 is accepted by the CLI and reaches build_workflow(), where the generated page dataframe has length 0 and the first stage is added with num_records=0. The workflow API rejects that value, so this advertised runnable recipe exits with an engine error instead of handling the CLI input at the recipe boundary.

Context Used: Do not suggest defensive coding patterns such as a... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/assets/recipes/workflow_chaining/document_review_gate.py
Line: 790

Comment:
**Zero Records Crash Workflow Setup**

`--num-records 0` is accepted by the CLI and reaches `build_workflow()`, where the generated page dataframe has length 0 and the first stage is added with `num_records=0`. The workflow API rejects that value, so this advertised runnable recipe exits with an engine error instead of handling the CLI input at the recipe boundary.

**Context Used:** Do not suggest defensive coding patterns such as a... ([source](greptile.json))

How can I resolve this? If you propose a fix, please make it concise.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for putting this together, @andreatgretel — this is a genuinely nice dev note, and the runnable recipe makes the workflow-chaining story concrete.

Summary

This is a docs + recipe PR: it adds the "Pause, Inspect, Resume" dev note, a headless document_review_gate recipe that runs a workflow to a named review_candidates stage, exports the intermediate artifact, simulates an external review, and resumes downstream via stage_output_overrides. It also wires up the Fern navigation, recipe card, dev-note card, a redirect, compressed PNG assets, and behavioral tests. The implementation matches the stated intent, and I verified the workflow-chaining APIs it leans on (compose_workflow, add_stage, run(targets=…, resume=…, stage_output_overrides=…), export_stage, ResumeMode, custom_column_generator, CustomColumnConfig) all exist and are used with correct signatures.

Findings

Suggestions — Take it or leave it

docs/assets/recipes/workflow_chaining/document_review_gate.py:497-499 — Fallback indexes into ground_truth_boxes[0] without a guard

  • What: When every proposed box is dropped (the signature-skip branch removes the only box), the fallback does parse_boxes(row["ground_truth_boxes"])[0]. If a row ever had zero ground-truth boxes, this would raise IndexError.
  • Why: In this recipe every generated page always has several labeled boxes, so it can't trigger today — but as a reusable, copy-pasteable recipe it's the kind of edge a reader might hit after swapping in their own seed data.
  • Suggestion: A one-line guard (e.g. skip the row or fall back to [] when ground_truth_boxes is empty) would make the pattern safer to lift. Optional given the demo scope.

docs/assets/recipes/workflow_chaining/document_review_gate.py:119_font return annotation is slightly imprecise

  • What: The annotation is ImageFont.ImageFont, but ImageFont.truetype(...) returns a FreeTypeFont and only the load_default() fallback returns ImageFont.
  • Why: Purely cosmetic — callers only use draw.text/textbbox, which accept both. Flagging only for accuracy.
  • Suggestion: Leave as-is, or widen to the shared base if you want it exact. Not worth churn.

fern/docs.yml:37 — Redirect from a slug that was never published

  • What: The new redirect maps /dev-notes/annotate-the-hard-pages/dev-notes/pause-inspect-resume. Since this dev note is brand new (its slug resolves to pause-inspect-resume), there doesn't appear to be a prior public URL at annotate-the-hard-pages.
  • Why: A redirect for a URL that was never live is harmless but adds a little config noise and can be confusing later ("what used to be here?").
  • Suggestion: If the post was previously shared under that slug (draft/preview link), keep it; otherwise it can probably be dropped. Your call.

What Looks Good

  • The dev note is well-structured and honest — the "Why not just run two workflows?" table draws the operational distinction clearly, and the recipe stays "deliberately ordinary Data Designer," which reinforces the declarative contract nicely.
  • Tests verify behavior, not plumbing — asserting image dimensions, metadata validity, box-in-bounds geometry, selected-row counts, and source == "human_review" counts through the full run is exactly the right altitude for a recipe test. The spec_from_file_location loader for a non-importable script is a clean touch.
  • Slug/nav/card consistency checks out — the BlogCard href (/dev-notes/pause-inspect-resume), the recipe card, the nav entry, and the recipe-page cross-link all line up with Fern's title-derived slugs.

Notes

  • I couldn't re-run ruff check / ruff format --check in this CI environment (ruff isn't on PATH here). The PR checklist indicates they passed locally, and CI will enforce them regardless.
  • Two existing inline comments already raise (a) whether ResumeMode should live in data_designer.config so recipes can use dd.ResumeMode, and (b) whether conditional generation could achieve the same result within one stage. I'm not duplicating those — they're worth a reply from you, but neither blocks this docs/recipe PR.

Structural Impact (graphify, 2.4s)

Risk: LOW (localized change)

  • 2 Python files, 0 AST entities, 0/82 clusters

This confirms a localized, additive change with no god-node or cross-package blast radius — consistent with a docs + standalone-recipe PR that imports the public API surface without modifying it.

Verdict

Ship it (with nits) — Only optional suggestions above; nothing blocking. Worth a quick reply to the two existing inline comments before merge, but the change itself is in good shape.


This review was generated by an AI assistant.

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