Field Note · AI & Autonomous Agents

TicketStub: Building an AI-Powered Personalized Print Platform

How our neatHack 2026 team turned an event link into an AI-illustrated keepsake stub, and which parts work versus remain a prototype.

Published
Status
Prototype

TicketStub is a keepsake studio for events: you paste a link to a concert, game or show, the app researches the night, and it produces a personalized commemorative ticket stub with original artwork that you can edit, approve, download and, eventually, order as a print. Our team, Jevelopers, built it for neatHack 2026, an online hackathon that ran October 10-12, 2026. I was team captain and wrote a large share of the code, but this was a team effort and the credits are at the end of the post.

The interesting part is the pipeline behind the stub: research that tracks where every fact came from, image generation that never draws text, durable jobs that survive a crash, approval bound to an exact design version, print checks, and cost controls meant to run before any paid API call.

Status: this is a hackathon prototype. The customer studio runs end to end in demo mode with simulated research and art. Most specialist components are tested libraries that are not yet wired into the app. Nothing here takes real payments or ships physical prints.

Problem

Mobile tickets replaced the paper stub that people used to keep from a night out. Our idea was to bring it back as a designed object: real facts about the night, artwork that fits the event, and a printable file at the end.

Solution

On the first day the team wrote an architecture kit with one goal: turn an event URL into researched facts, original artwork, customer revisions, approved tickets, downloadable files and an authorized print order, while recording what the workflow cost. It also set a few rules:

  • One owner for each business record. The app owns customers, orders and approvals. The harness owns workflow execution. Finance owns spending.
  • Meter billable attempts from the first working call, reserve budget before execution, and keep missing or uncertain billing data for reconciliation instead of guessing.
  • Aim for a narrow but complete demonstration rather than a broad, half-working platform.

We followed the first rule closely. The last one we only partly met, as the status table shows.

Architecture

This post covers six repositories: getticketstub.com (the Next.js customer app and composition root), event-intelligence (research and fact provenance), creative-engine (briefs, image generation, design versions), print-production (composition and preflight), cost-intelligence (pricing, budgets, ledger) and agent-harness (durable workflow state and telemetry). The organization has a few more, such as brand kit, shared contracts and fulfillment connectors.

Diagram of the TicketStub repositories: the getticketstub.com app at the top with studio, API routes, accounts and a vendor folder; five specialist repositories below it, with solid arrows for code vendored into the app and dashed arrows for handoffs that are not integrated yet; external AI, research and payment services on the right, reached only in live mode.
How the repositories connect at the commits I reviewed. Solid arrows mean a pinned code subset is vendored into the app; dashed arrows mean the handoff is designed but not integrated. Diagram drawn for this article from the repositories’ READMEs and vendor manifests.

Each specialist repository is a library, not a service. The app vendors pinned subsets into src/server/vendor/, each with a MANIFEST.json recording the upstream commit and a SHA-256 per file. Today that covers agent-harness, creative-engine, cost-intelligence and fulfillment-connectors. Event-intelligence and print-production are not vendored; their handoffs exist only in design documents.

That split let four people build and test in parallel over a weekend. The cost is integration: much of the remaining work is agreeing shared contracts between repositories.

Implementation

Event research and structured extraction

The app’s /api/research route turns a link into a list of facts. Every fact carries a source, a confidence and a provenance label (live, estimated, simulated or pending) that the studio displays.

In live mode the route reads schema.org Event data from JSON-LD, falls back to the page title, and gets weather from Open-Meteo. A date guessed from page text gets confidence 0.5 and two alternatives (the day before and after) for the customer to choose from.

A language model can add up to four extra facts, but it must quote the page. The route only trusts a fact as live if the quoted evidence actually appears in the fetched text. Otherwise the fact is downgraded to estimated and its confidence is capped:

// getticketstub.com/src/app/api/research/route.ts
// Only trust as "live" when the quoted evidence really appears on the fetched page.
const grounded = typeof i.evidence === "string" && i.evidence.trim().length > 0 && i.evidence.length <= 2000 && lowerText.includes(i.evidence.toLowerCase());
return {
  ...
  confidence: Math.min(grounded ? 0.85 : 0.5, typeof i.confidence === "number" ? i.confidence : 0.6),
  provenance: grounded ? ("live" as const) : ("estimated" as const),
};

This does not prove the fact is true, only that the model did not invent the quote. It was the level of rigor we could ship during the event.

The event-intelligence repository goes further, as separate private components. Its SafeFetcher validates URLs and DNS, pins connections to vetted addresses, revalidates redirects and enforces hard limits per run:

// event-intelligence/src/security/controlled-fetch.mjs
export const DEFAULT_RUN_LIMITS = Object.freeze({
  requests: 30,
  redirectsPerFetch: 5,
  concurrentFetches: 4,
  connectMilliseconds: 10_000,
  totalMilliseconds: 30_000,
  wireBytesPerRequest: 10 * 1024 * 1024,
  ...
});

It also has deterministic JSON-LD, HTML and text-PDF extraction, an encrypted evidence store, and a privacy boundary that quarantines fetched content until a trusted review approves the public facts. Its README is clear that this is not yet a public research API.

AI image generation and personalization

One early decision: the image model never draws text. Names, dates and venues are set by the stub template in real fonts over a text-free scene. Image models still misspell words, and a wrong date on a printed keepsake is the error we most wanted to rule out.

Personalization goes into the prompt instead. The app’s prompt builder turns researched facts into scene constraints, and adds their opposites as negatives so a rainy night does not come back sunny:

// getticketstub.com/src/customer/studio/research/artPrompt.ts
} else if (/rain|drizzle|shower/.test(w)) {
  weather = "rainy, wet reflective streets,";
  negatives.push("snow", "clear blue sky", "harsh sunshine", "dry dusty ground");
} else if (/dry|clear|sun/.test(w)) {
  weather = "dry, clear air,";
  negatives.push("rain", "puddles", "umbrellas open against rain", "snow");
}

Event type picks a scene, the venue is “described, not copied”, and remix chips such as “moodier” add a mood line. Live art generation is capped at 10 images per day per account or device, and 30 per IP.

The creative-engine repository is the fuller version. Its library API offers generate, edit, inspect, compare, restore and brief operations around injected ports. Every result is an immutable version in SQLite with content-addressed bytes, so restore never overwrites history, and a missing port returns unavailable rather than a fake success. It has OpenAI and OpenRouter image adapters, but every live gate exits with code 2 and makes no call. I called no image API for this article.

Durable jobs and checkpoints

If a process dies after a provider has charged for an image but before we saved it, a naive retry pays twice. The agent-harness repository addresses that using Node’s built-in node:sqlite and no third-party runtime dependencies.

A workflow moves through a small, explicit state machine:

// agent-harness/src/internal/durable-store.js
const TRANSITIONS = new Map([
  ['queued', new Set(['running', 'review_required'])],
  ['running', new Set(['reconciliation_pending', 'review_required', 'succeeded', 'failed'])],
  ['awaiting_response', new Set(['running', 'reconciliation_pending', 'review_required', 'succeeded', 'failed'])],
  ['reconciliation_pending', new Set(['running', 'review_required', 'succeeded', 'failed'])],
  ['review_required', new Set()],
  ...

Every state change runs in a BEGIN IMMEDIATE transaction that compares the expected revision and the worker’s fencing token, then writes the event and an outbox entry together. A stale worker or an expired lease cannot write. A provider response, its hash and the proposed next checkpoint are saved as one receipt. After a restart, recovery verifies the hash and advances the checkpoint without repeating the external call, and a completed receipt blocks redispatch of the same operation.

The design document says plainly that this is durable local state and deduplication, not exactly-once execution. An unknown outcome stays pending for reconciliation instead of being guessed. The controller rejects mode: "live", so today this runs in mock mode and tests only.

Human review and correction

AI research gets things wrong, so the customer is the final reviewer, and we tried to make that review explicit.

  • Unsure facts are questions. A low-confidence fact shows up in the editor’s Facts panel with its alternatives. Missing details, such as where an event took place, become prompts (“Where was it?”).
  • Inclusion is separate from verification. Adding a fact to the stub does not mark it as verified, and the panel says so.
  • Corrections keep history. In event-intelligence, a customer answer is stored as customer_supplied, not “sourced”, older versions are kept, and a later source that disagrees creates a review question instead of overwriting the answer.
  • Approval binds to bytes. The app issues a signed approval bound to the exact design version, art hash and facts hash, for one purpose (print.compose or artifact.download). It explicitly does not authorize a purchase, payment, order or asset rights.
The TicketStub studio editor in demo mode, showing style options on the left, a stub for the neatHack 2026 demo event in the centre with an agent note, and an inspector on the right with facts, a sandbox price notice and Download PNG and Tear off buttons.
The studio editor for the built-in neatHack 2026 demo event, from a local run in demo mode. The art is procedural sample art, the agent note is simulated, and the checkout panel says “Sandbox prices. Nothing is charged.”

Creative-engine adds machine inspection first. A deterministic gate checks hashes, decodes the image within limits, and checks the brief, unresolved facts and asset permissions before a visual model is asked narrow questions. A favorable model answer cannot override a failed check, uncertain evidence goes to human review, and automatic repair is capped (two claims, 60 seconds and three simulated units in the fixture policy).

Print-production checks

Print-production, where most of the commits are Zach Wright’s, is where the stub becomes a physical-dimension file. It composes in millimetres, measures and shapes text with pinned fonts (no host font fallback), and builds SVG output before rendering through a pinned Chromium.

Preflight checks geometry, effective raster DPI, bleed coverage (including rotated art), safe zones, clipping and font integrity. Repairs are limited to shrinking or moving text within bounds. Names, dates, seats and serials are never rewritten, overflow blocks instead of truncating, and anything riskier returns NEEDS_EXTERNAL_REVIEW.

The limits matter. The offline calibration renders a synthetic 4 x 2 inch fixture to PNG and PDF and checks both. The profile is sRGB, with no CMYK, PDF/X or printer certification claimed, and supplier eligibility stays blocked even when every local check passes.

Cost tracking and budgeting

Every paid call was meant to go through Finance first. Cost-intelligence implements pricing catalogs, usage intake, budget reservations (authorize, markDispatched, changeBudget), an append-only ledger and scoped reporting and exports, on PostgreSQL.

Two choices I would reuse anywhere. An unconfigured call returns unavailable instead of a financial zero, so a missing integration cannot look like “this cost nothing”. And unknown usage stays unknown, rather than totals quietly summing only the known parts.

In the app, the config-gated /api/creative route composes the vendored creative-engine with the Finance package and the harness decision port, and the app’s tests include a “Finance-first barrier” case that checks paid routes are denied without the runtime override. Settlement, release and reconciliation are still unavailable facades, and the financial release gate is open.

Evidence

The status review and local runs below were captured on 2026-10-21 at commits 9fd9512 (getticketstub.com), 333c6f0 (agent-harness), a4df4ef (event-intelligence), 0430b4a (creative-engine), 4ca2b83 (print-production) and 521e4be (cost-intelligence).

What works, what is a prototype, what is not built

This table reflects the READMEs and source at the commits listed below, plus what I ran locally.

Component What works Prototype/stub Not built
Customer studio (getticketstub.com) Runs locally with no keys: intake, printing reveal, five styles, fact picker with provenance labels, editing, versions, wall, PNG export, magic-link sign-in, SQLite storage, backups Demo research fixtures, simulated agent replies, procedural or demo art; sandbox checkout; wallet passes and QR codes simulated Live payment acceptance, physical fulfillment, production access and budget controls, integrated v1 acceptance
Event research App (live mode only): JSON-LD and title extraction, weather, quote-grounded model facts. Event-intelligence: safe fetch, extraction, evidence store, correction history, offline tests Event-intelligence components are private and not wired into the app Public research API, complete source traversal, canonical fact selection, scanned-PDF and screenshot inspection
AI image generation (creative-engine) Library API with injected ports, immutable versions, deterministic inspection, bounded repair, offline tests OpenAI and OpenRouter adapters exist, but live gates exit 2; app art route is a direct prototype path Live mode, durable handoff, accepted cross-repository contracts
Durable jobs (agent-harness) SQLite checkpoints, leases and fencing, receipts, outbox, migrations, crash and restart tests Private bounded controller, mock mode only; telemetry and decision transports are opt-in Deployed workflow, live mode, AH-01 and AH-03 acceptance
Human review Unsure-fact questions, customer corrections, signed approvals bound to version, art and facts hashes Approval consumers are checked with public fixture keys only; versioned signing-key work is on an open PR Product and print-layout review sign-off for print readiness
Print production Offline calibration render, composition, typography, preflight, bounded repair, bundle byte verification Candidate adapter against an unaccepted shared-contracts development version; sRGB only CMYK or PDF/X output, approved supplier specification, supplier eligibility, app integration
Cost tracking (cost-intelligence) Pricing, usage intake, budget reservations, ledger, reporting and exports on PostgreSQL; app-side art quota Shared contract pinned as proposal-only; host authority issuers not real yet Public settle and release, reconciliation, financial release gate

Demonstrations and what I ran

I ran everything locally with no provider keys and called no paid API. Test runs used a Node 22.23.3 Linux container.

  • Studio demo. The dev server’s /api/health reported "mode":"demo". I captured the screenshots above with all non-localhost browser requests blocked.
  • getticketstub.com: 1,305 tests, 1,303 passed, 1 failed (a landing-page reduced-motion test I did not investigate), 1 skipped. The Finance-first barrier test passed.
  • creative-engine: the api target passed 61 of 61 tests; the versions target passed 253 of 254, with one schema-migration replay case failing.
  • agent-harness: make check passed. Its owned test runner did not complete in my container, so I ran the durability, foundation and recovery files directly: 42 of 42 passed. That is a partial run.

All of these use synthetic fixtures. They show components behaving as designed in isolation, not real-world validation or an integrated journey.

Limitations

TicketStub is a hackathon prototype. The app and event-intelligence repositories had commits as recently as October 21, 2026; the other four were last updated on October 15.

  • The full journey from link to printed stub does not exist yet. The integrated v1 acceptance issue is open.
  • Demo mode is the default. Research, art and agent replies are simulated unless live mode and provider keys are both configured, and enabling them requires the team’s budget approval.
  • No real payments are taken. With no payment provider configured, orders stay in sandbox, and every physical variant is preview-only or unavailable.
  • Print output is not certified for any printer or supplier, and supplier eligibility is blocked by design.
  • Event-intelligence and print-production are not integrated into the app. Cross-repository contracts are proposals awaiting owner agreement.
  • Durability, inspection and Finance behavior is tested only offline with synthetic data, never against real provider traffic.

What I am happiest with is that the repositories say all of this themselves. Nearly every README separates “implemented locally” from “pending”, and several runners exit with a failure code rather than report an acceptance they cannot prove.

Resources

  • getticketstub.com: no license file; source-available on GitHub. Commit 9fd9512.
  • agent-harness: MIT License for original project code (banner artwork excluded, see its COPYRIGHT.md). Commit 333c6f0.
  • event-intelligence: no license file; source-available on GitHub. Commit a4df4ef.
  • creative-engine: no license file; source-available on GitHub. Commit 0430b4a.
  • print-production: no project-wide license file (bundled fonts keep the SIL Open Font License); source-available on GitHub. Commit 4ca2b83.
  • cost-intelligence: no license file; source-available on GitHub. Commit 521e4be.

If you have an idea that needs a working prototype with this kind of separation between what is real and what is simulated, that is the work behind our Prototyping and MVP Development service.

TicketStub was built by the Jevelopers team for neatHack 2026: Timothy Wayne Gregg (CompleteDotTech, team captain), Lars van der Zande (lmvdz, brand, editions and studio design), Zachary Wright (zwright8, print-production) and Crystal Aguilar (lajefahemp-collab, landing page and documentation). Git history in these six repositories shows commits from CompleteDotTech across all of them, lmvdz in getticketstub.com, zwright8 in print-production and cost-intelligence, and a few commits from an automated Codex identity.

Related CompleteTech services

Discuss a similar project
Written by

Timothy Gregg · Founder, CompleteTech

Enterprise IT operations and ITSM implementation on ManageEngine and Microsoft platforms, now focused on AI automation and systems engineering. Presented at the 2023 ManageEngine user conference.