Case Study

Last-Edit Trail

A Page Context Panel that shows the last 5 versions of the active page in the current language — newest-first, with age and (when surfaced) author. "git log for this page" instead of a Slack ping.

Case Study7 min read

The question that always lands in chat

"Who touched this page last and when?" Editors ask it before publishing, before a deadline check, before a rollback decision. Native Pages does not surface a per-page version history in the editing pane — so editors leave the canvas, hop into another tool, scroll a list, then come back and try to remember why they were there.

Last-Edit Trail puts that history back where the work happens. It renders inside the Page Builder Context Panel (~360 px wide) and lists the 5 most recent versions of the active page in the editor's current language — newest-first, with relative age (2h ago, 3d ago), absolute UTC timestamp on hover, and the author name when the API surfaces one. The version that matches the editor's active draft gets a 2 px left-edge stripe; a "Show all (N)" toggle expands to every version returned. Switching pages or languages re-fetches and re-renders without a manual refresh.

5
versions shown
by default
0
backend / API
routes
5
Blok primitives
only — no custom UI
76
tests across
component + structural

Lessons learned at a glance

  • What happened initially: PRD-000 shipped clean in one session. The read-only version trail was stable, test-covered, and aligned with the iframe constraints.
  • What went crazy later: PRD-001 expanded into Compare view and added four new integration surfaces in one PRD. The pipeline still shipped with 35 commits and 304/304 tests green, but first real-tenant smoke surfaced nine integration gaps.
  • Why it happened: mocks and structural coverage validated architecture, but did not fully match real tenant response behavior across all new surfaces at once.
  • Full detail: The Pipeline Is a Partnership.

How it actually works

Last-Edit Trail is a client-side iframe app: no backend, no Auth0, no API route, no token cache. Everything it needs — knowing which page the editor is on, fetching that page's version history — runs through the Marketplace SDK's browser bridge to the parent portal.

The panel binds to a single Pages extension point and reactively re-fetches whenever the editor changes pages or switches language. A monotonic request-id guard drops stale responses if a slower fetch arrives after a faster one — the symptom this prevents is the editor flipping pages quickly and seeing the wrong trail flash on screen.

The active-draft row is identified by matching the row's version against the editor's current version directly, rather than calling an extra "page state" endpoint. The simpler check is cheaper, and any diff would have been zero rows.

Why one extension point, one route, no compromises

Build · 04

The pure-pipeline shipment

2026-05-04 · Page Builder Context Panel

The fourth product through the dogfood loop. PageShot crossed every layer; QuickCopy proved the patches survived a clean rebuild; Atlas proved the same skill set scales to a tenant-wide live atlas; Last-Edit Trail proved a full PRD pipeline now runs end-to-end in a single session without re-patching the skills.

  • Iframe-only architecture — runs in the browser through the Marketplace SDK. No backend, no external store, no cron. Threat surface stays narrow; rollout stays simple.
  • Two reactivity paths, one chosen on purpose — the SDK exposes two ways to react to editor context changes; the panel uses the query-driven one. The dogfood loop documented both, so the choice is principled, not accidental.
  • Latest-emission-wins guard — a monotonic request id drops out-of-order responses, so quick page-switching can never show the wrong trail.
  • 5 Blok primitives, no custom UI library — the panel sticks to a tight set of Sitecore-styled building blocks, with a structural test that fails the build if anything else creeps in.
  • Graceful author omission — when the API surfaces no author for a version, the row drops the author cell entirely instead of rendering “Unknown”. Empty-state copy is verbatim and snapshot-tested.

What the build crossed

This was the fourth product through the full agentic pipeline after PageShot, QuickCopy, and Component Atlas. Every layer that earlier runs hardened — scaffold, SDK wiring, iframe-only architecture, Blok primitives, Tailwind v4 tokens, iframe sandboxing — held without a single new patch. The build's distinctive work was upstream of code: nine ADRs that pinned the architecture variant, the reactivity model, the empty-state matrix, and the route URL before the first component was written.

The implementation produced 76 tests across structural, component, and behavioural surfaces — including ADR-enforcement assertions that fail loudly if a future change tries to add a backend route, sneak in a forbidden subscription pattern, or reach for a non-listed Blok primitive. The skill set never had to be touched.

Where this leaves the loop

Last-Edit Trail is the fourth Marketplace product shipped end-to-end through /create-prd/architect/task-breakdown/implement/code-review/test/document/ship. PRD-000 (read-only version trail) shipped clean in a single session. PRD-001 (the Compare view) is where the loop met its limits — and the lesson worth keeping.

For the methodology that produced PRD-000's clean shipment, see the dogfood loop.

Where it stopped being clean

PRD-001 set out to extend the read-only trail into a full Compare view: pick two page versions, see the field-level diff, drill into a per-component accordion, and flip to a side-by-side visual canvas with a draggable seam slider. Same agentic pipeline. Same eight commands. Same kind of session.

The pipeline ran end-to-end clean. 35 commits. 304 of 304 tests passing. Strict-TypeScript build green. /ship completed. Manifest status: shipped_with_caveats, with four operator gates pending.

Then the panel loaded inside an actual SitecoreAI tenant.

The first real-tenant smoke surfaced nine integration findings in one session. The shape was repetitive: the Compare CTA was illegible against its own background; the Compare body still showed a scaffold placeholder instead of the real view; the page-fields section rendered nothing on success when the field list came back empty; component rows lost their names because real layout data shipped IDs without human-readable labels; the Visual tab loaded "Page not found" because the URL builder didn't have the page's real route forwarded to it.

Each was a small, surgical fix. None were architectural. The framework's structural tests caught architectural drift the whole way through. What the framework didn't catch was the gap between the shape of the data the mocks were returning and the shape of the data the real tenant returns.

What this case study is, then

Two things, on purpose:

  • The idea, intact. The recursive trail-row primitive (one component, two consumers — page level and component level — sharing the same multi-select-and-compare interaction) is the strongest design move in the Compare view. The components-used union with presence badges and drift indicators is the right way to surface Sitecore's per-page-version-vs-per-datasource-version manifold. The Visual tab's overlay-clip-path seam slider is the only way to wipe cross-origin iframes without DOM access. None of those got invalidated by the smoke pass — they just got tested.
  • The lesson, surfaced. The lesson isn't "AI fell apart" — the pipeline produced 35 commits of well-architected, test-covered code. The lesson is that the pipeline is one half of a partnership. The other half is one human, one real tenant, every time the integration surface area grows. PRD-001 added four new SDK integration surfaces in a single PRD. With no real-tenant checkpoint between them, four divergences stacked on each other before the operator ever clicked Compare.

The methodology essay that distilled this, in detail, lives at The Pipeline Is a Partnership. Read it for the cadence (autonomous within a phase, hard checkpoint between phases) and for the scope discipline that follows from it (phase by SDK surface, not by feature — when 3+ new surfaces stack into one PRD, split).

Ship your idea

The wrong takeaway from a smoke pass that surfaces nine findings is "don't try the ambitious thing." The right takeaway is "the ambitious thing is the right thing — phase it differently."

PRD-001 of Last-Edit Trail should have been three phases — one new SDK surface per phase, each shipping against a verified prior phase, each ending at a real-tenant checkpoint with the F12 console open and a five-minute walkthrough as an actual editor. The Compare view as a product still belongs in the editor; what changes is the cadence that gets it there.

If you're building on the Marketplace SDK and the agentic pipeline, ship your idea. Phase it by where the integration risk lives. Pause between phases on a real tenant. The framework will run the inside half of the loop without you. The outside half is yours, and it's where the work is most worth doing.

Related case studies