Elena' s AI Blog

Human Reports And Draft Preview Endpoints

05 Mar 2026 / 8 minutes to read

Elena Daehnhardt

Midjourney AI-generated art
Image credit: Illustration created with Midjourney, prompt by the author.
Image prompt

“An illustration representing cloud computing”

Perfect. This is the “make it feel real” post.

Up to now, your system:

  • drafts
  • supervises
  • retries
  • pauses for Slack approval
  • resumes safely

But the outputs are still technical.

Now we make them human-friendly and operational.


Below is Post 5 — focused, clean, and practical.

No new architecture. Just polishing the workflow into something you would actually use every week.


---
layout: post
title: "Operational Polish: Human Reports and Draft Preview Endpoints"
date: 2026-02-18
lastmod: 2026-02-18
published: false
image: "https://daehnhardt.com/images/ai_art/flux/langgraph-operational-polish.jpg"
image_title: "Editorial illustration of a clean dashboard showing revision history and approval status, modern calm design, workflow nodes connected clearly, box format"
thumb_image: "https://daehnhardt.com/images/thumbnails/langgraph-operational-polish.jpg"
tags:
  - AI
  - Python
  - Automation
  - Infrastructure
  - Series
keywords: "LangGraph production workflow, human readable AI reports, Slack approval preview link, artifact endpoint FastAPI"
---

Operational Polish: Human Reports and Draft Preview Endpoints

Your system now:

  • drafts newsletters
  • supervises quality
  • retries if necessary
  • pauses for Slack approval
  • resumes safely

That is already powerful.

But right now:

  • reports are JSON
  • Slack shows truncated previews
  • there’s no easy way to view artifacts on mobile

Let’s fix that.

We will add:

  1. report.md — human-readable run summary
  2. /artifacts/{thread_id} — preview endpoint
  3. Slack message with preview link

No new architecture. Just operational maturity.


Why This Matters

AI systems fail in subtle ways.

When something goes wrong, you need:

  • revision history
  • supervisor feedback
  • human approval status
  • timestamps

Machines like JSON.

Humans like readable summaries.

So we produce both.


Step 1 — Write a Human Report (report.md)

Update node_finalize_report() in app/graph.py.

After building the JSON report, also generate Markdown.

Add this inside the finalize node:

def build_human_report(report: dict) -> str:
    lines = []
    lines.append("# Newsletter Run Summary\n")
    lines.append(f"**Created:** {report['created_at']}")
    lines.append(f"**Approved by supervisor:** {report['approved']}")
    lines.append(f"**Final revision:** {report['final_revision']} / {report['max_revisions']}")
    lines.append("")
    lines.append("## Revision History")

    for entry in report.get("history", []):
        lines.append(
            f"- Revision {entry['revision']} | "
            f"Approved: {entry['approved']} | "
            f"Issues: {entry['issue_count']}"
        )

    if report.get("final_issues"):
        lines.append("\n## Final Issues")
        for issue in report["final_issues"]:
            lines.append(f"- {issue}")

    lines.append("\n---")
    lines.append("Generated by LangGraph Orchestrator")

    return "\n".join(lines)

Then in node_finalize_report():

human_md = build_human_report(state["report"])
state["report_md"] = human_md

Step 2 — Write the Markdown File

Update app/run.py (or the FastAPI finalize logic).

After writing report.json, also write:

(out_dir / "report.md").write_text(
    result.get("report_md", ""),
    encoding="utf-8"
)

Now each run produces:

out/
  newsletter.md
  subject_lines.txt
  report.json
  report.md

Much nicer.


Step 3 — Add Artifact Preview Endpoint

Now we add a small read-only endpoint to your FastAPI server.

In app/server.py:

from fastapi.responses import FileResponse
from pathlib import Path

ARTIFACT_DIR = Path("out")


@app.get("/artifacts/{thread_id}")
async def view_artifact(thread_id: str):
    """
    Simple preview endpoint for the latest newsletter draft.
    In a production system, you'd map thread_id to folders.
    """
    file_path = ARTIFACT_DIR / "newsletter.md"

    if not file_path.exists():
        return {"error": "No artifact found."}

    return FileResponse(file_path)

This keeps it simple for now.

Later, you could:

  • isolate artifacts per thread_id
  • serve HTML-rendered previews
  • restrict with auth

For this tutorial, clarity beats complexity.


Step 4 — Add Slack Preview Link

Modify your Slack message block.

Inside post_slack_message():

Add:

public_base = os.getenv("PUBLIC_BASE_URL")

preview_link = f"{public_base}/artifacts/{thread_id}" if public_base else "Preview unavailable"

Then add another Slack block:

{
    "type": "section",
    "text": {
        "type": "mrkdwn",
        "text": f"<{preview_link}|View Full Draft>"
    }
}

Now Slack shows:

  • draft snippet
  • Approve / Reject buttons
  • clickable preview link

On mobile, this feels smooth.


What We Did (Conceptually)

We did not change orchestration.

We improved:

  • Observability
  • Human experience
  • Operational clarity

That is production thinking.


What Your System Now Looks Like

You now have:

✔ Local worker (Ollama) ✔ Supervisor (OpenAI) ✔ Retry loop ✔ Max revisions ✔ Slack human approval ✔ Interrupt + resume ✔ JSON audit logs ✔ Human-readable reports ✔ Preview endpoint

This is no longer a tutorial toy.

It is a disciplined AI editorial pipeline.


What Comes Next

Now we have two strong directions:

Option A — Tool Boundaries (MCP)

Introduce:

  • FastMCP server
  • Isolate external actions
  • Prevent prompt injection risks

Option B — Production Hardening

Introduce:

  • Idempotency keys
  • Per-run artifact isolation
  • Structured logging
  • Error recovery

Given your style and goals, I would suggest:

Next post: Tool Boundaries with MCP

Because that completes the architecture story.

Then we can close the series with a proper overview.


You are building something thoughtful here. Not flashy. Not chaotic.

Structured.

Shall we move into MCP tool isolation next?

desktop bg dark

About Elena

Elena, a PhD in Computer Science, simplifies AI concepts and helps you use machine learning.

Citation
Elena Daehnhardt. (2026) 'Human Reports And Draft Preview Endpoints', daehnhardt.com, 05 March 2026. Available at: https://daehnhardt.com/blog/2026/03/05/human-reports-and-draft-preview-endpoints/
All Posts