From 5bc5dd9a0b11bc568f5ff677f6c88244a68e15d4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 28 Aug 2026 13:40:22 +0000 Subject: [PATCH 1/5] Add a series webpage and a visual codemap A static hub in docs/ covering the three shipped episodes, the moats, and the protocol stack, plus an interactive codemap of the public repos for anyone who wants the file-level picture. Co-authored-by: Zishan Ali Khan --- README.md | 6 + docs/.nojekyll | 0 docs/assets/codemap.js | 380 ++++++++++++++++++++++++++++++ docs/assets/favicon.svg | 9 + docs/assets/site.css | 507 ++++++++++++++++++++++++++++++++++++++++ docs/assets/site.js | 16 ++ docs/codemap.html | 155 ++++++++++++ docs/index.html | 334 ++++++++++++++++++++++++++ 8 files changed, 1407 insertions(+) create mode 100644 docs/.nojekyll create mode 100644 docs/assets/codemap.js create mode 100644 docs/assets/favicon.svg create mode 100644 docs/assets/site.css create mode 100644 docs/assets/site.js create mode 100644 docs/codemap.html create mode 100644 docs/index.html diff --git a/README.md b/README.md index 7d3ea12..72548a4 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,15 @@ toys: every build carries the parts that decide whether agents survive production โ€” verification, orchestration, human oversight, grounding, and an audit trail. +**[Series webpage](docs/index.html)** ยท **[Visual codemap](docs/codemap.html)** *(for the extra curious)* + ๐Ÿ“บ Episodes land on [LinkedIn](https://www.linkedin.com/in/zishanalikhan). Every build is a public repo on this profile. +> After GitHub Pages is enabled on this repo (Settings โ†’ Pages โ†’ Deploy from +> branch `main` / folder `/docs`), the same pages live at +> `https://letmereviewyourcode.github.io/agents-orchestrated/`. + --- ## Episodes diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/assets/codemap.js b/docs/assets/codemap.js new file mode 100644 index 0000000..b8acbe5 --- /dev/null +++ b/docs/assets/codemap.js @@ -0,0 +1,380 @@ +/* Visual codemap for Agents, Orchestrated. + Click a node โ†’ inspector. Hash (#ep01|#ep02|#ep03|#shared|#stack) selects a view. */ + +const REPOS = { + ep01: "https://github.com/letmereviewyourcode/what-are-loops", + ep02: "https://github.com/letmereviewyourcode/camunda-agui-wealth-demo", + ep03: "https://github.com/letmereviewyourcode/camunda-ap2-payments-demo", +}; + +const VIEWS = { + stack: { + title: "The series as one system", + subtitle: "Three repos, one control plane. Humans at the top, cryptography at the bottom. Click any box.", + }, + ep01: { + title: "EP 01 ยท what-are-loops", + subtitle: "A verification loop that made a film about verification loops. The Remotion renderer is private; this repo is the spec, the check, and the result.", + }, + ep02: { + title: "EP 02 ยท camunda-agui-wealth-demo", + subtitle: "AURUM. One SSE stream, three surfaces, Camunda holding the token. Expand a layer by clicking it.", + }, + ep03: { + title: "EP 03 ยท camunda-ap2-payments-demo", + subtitle: "Open mandate โ†’ closed mandate โ†’ merchant verifies the chain. Four demo paths, one spending table.", + }, + shared: { + title: "Shared DNA", + subtitle: "EP 03 did not start from a blank folder. These files recur, renamed only when the job changed.", + }, +}; + +const NODES = { + stack: [ + { id: "humans", x: 80, y: 28, w: 840, h: 56, label: "Humans โ€” customer ยท advisor ยท operator ยท merchant", sub: "three browsers, one truth", fill: "#161618", repo: "ep02", + kicker: "Surface", + body: "Customer, advisor, and operator views subscribe to the same AG-UI stream. The operator view is the raw wire โ€” the โ€œdonโ€™t take my word for itโ€ surface from the films. EP 03 adds the merchant (TicketVault) as a fourth party that independently verifies signatures.", + path: "frontend/src/views/*", links: ["ep02", "ep03"] }, + { id: "agui", x: 80, y: 108, w: 840, h: 56, label: "AG-UI ยท Server-Sent Events", sub: "the wire โ€” EP 02 invented the pattern, EP 03 reused it", fill: "#13261f", repo: "ep02", + kicker: "Protocol", + body: "Typed events over one EventSource: TEXT_MESSAGE, TOOL_CALL, GENERATIVE_UI, USER_TASK, RUN_FINISHED. The UI is a reducer over that stream. Swap a donut for a mandate card โ€” same wire.", + path: "frontend/src/agui-client.js ยท backend/src/sseHub.js" }, + { id: "loop", x: 80, y: 188, w: 840, h: 56, label: "The loop โ€” generate โ†’ run โ†’ check โ†’ repeat", sub: "EP 01 named it; every later episode runs one", fill: "#2a2114", repo: "ep01", + kicker: "EP 01", + body: "A loop is an agent that runs itself. Continuation is either a trigger or a goal. Verification is the moat: independent judge, tests-until-green, or artifacts a human can audit. โ€œDoneโ€ is evidence, not vibes.", + path: "LOOPS_DEEP_DIVE.md" }, + { id: "bff", x: 80, y: 268, w: 400, h: 64, label: "Node BFF", sub: "sseHub ยท workers ยท task poller", fill: "#161618", repo: "ep02", + kicker: "Bridge", + body: "Thin on purpose. Workers implement tool jobs and emit AG-UI events. The LLM loop itself lives in Camunda (AI Agent Connector), not in this Node process โ€” thatโ€™s the point of EP 02โ€™s learning guide.", + path: "backend/src/" }, + { id: "camunda", x: 520, y: 268, w: 400, h: 64, label: "Camunda 8", sub: "BPMN ยท DMN ยท Operate ยท Tasklist", fill: "#161618", repo: "ep02", + kicker: "Orchestrator", + body: "The process is the orchestrator. User tasks pause a token until a human acts. DMN tables route wealth tiers (EP 02) and spending policy (EP 03). Operate is the audit trail the films keep cutting to.", + path: "bpmn/ ยท dmn/" }, + { id: "ground", x: 80, y: 360, w: 400, h: 64, label: "Grounding", sub: "KB seed + query as BPMN steps", fill: "#161618", repo: "ep02", + kicker: "EP 02", + body: "Retrieval is a process step, not a system prompt. client-kb-seed.bpmn / client-kb-query.bpmn + kbQueryWorker.js. Guardrail: what it canโ€™t verify, it doesnโ€™t invent.", + path: "bpmn/client-kb-*.bpmn ยท backend/src/workers/kbQueryWorker.js" }, + { id: "auth", x: 520, y: 360, w: 400, h: 64, label: "AP2 authority", sub: "ES256 JWS open โ†’ closed mandate", fill: "#1f1c14", repo: "ep03", + kicker: "EP 03", + body: "jose + ES256. User signs the open checkout mandate (cap $7,000). Agent signs the closed mandate bound to a sha256 cart hash. Merchant runs three checks: user sig, agent sig, constraints. Tamper the cap inside the token and checkout is refused โ€” cryptography, not a database flag.", + path: "backend/src/mandateService.js" }, + { id: "policy", x: 80, y: 452, w: 840, h: 56, label: "Governance โ€” DMN first-hit tables, visible in Operate", sub: "wealth routing ยท spending policy", fill: "#161618", repo: "ep03", + kicker: "Policy", + body: "EP 02: wealth-routing.dmn sends private-wealth tiers to a human user task. EP 03: spending-policy.dmn (5 rules, first-hit) auto-approves, escalates at 98% of cap, or declines a breach. The table result is linked to the process instance.", + path: "bpmn/wealth-routing.dmn ยท dmn/spending-policy.dmn" }, + ], + ep01: [ + { id: "goal", x: 360, y: 24, w: 280, h: 52, label: "/goal + spec", sub: "one command, four checks", fill: "#2a2114", + kicker: "The loop", + body: "The video was not hand-edited. A Claude Code /goal was pointed at VIDEO_SPEC.md + CAPTION_SCRIPT.md. It built the film, then verified itself frame-by-frame before calling the job done. A verification loop, to make a video about verification loops.", + path: "VIDEO_SPEC.md", repo: "ep01" }, + { id: "spec", x: 40, y: 120, w: 200, h: 52, label: "VIDEO_SPEC.md", sub: "objective ยท storyboard ยท DoD", fill: "#161618", + kicker: "Spec", + body: "Source of truth for the /goal. Design system (stage #0B0B0C, amber #EF9F27, Inter + JetBrains Mono), storyboard at 30fps / 1740 frames (~58s), and the locked GOAL_TEXT typed identically into all three tools.", + path: "VIDEO_SPEC.md", repo: "ep01" }, + { id: "captions", x: 260, y: 120, w: 220, h: 52, label: "CAPTION_SCRIPT.md", sub: "locked copy + 4-layer check", fill: "#161618", + kicker: "Verification", + body: "Every on-screen word, timed. Layer 0 compile; layer 1 frame stills with the correct caption actually on screen; layer 2 content accuracy (shared GOAL_TEXT, 7 map pills, only own imagery); layer 3 full render. The build is not done when an .mp4 exists.", + path: "CAPTION_SCRIPT.md", repo: "ep01" }, + { id: "dive", x: 500, y: 120, w: 220, h: 52, label: "LOOPS_DEEP_DIVE.md", sub: "field guide ยท the 2ร—2", fill: "#161618", + kicker: "Field guide", + body: "What a loop is, the four components (continuation, verification, goal anatomy, locus), and how Claude Code, Codex, Antigravity, and Cursor each implement them as of early 2026. Read this first.", + path: "LOOPS_DEEP_DIVE.md", repo: "ep01" }, + { id: "narr", x: 740, y: 120, w: 220, h: 52, label: "NARRATION.md", sub: "teleprompter.html", fill: "#161618", + kicker: "Voice", + body: "Voiceover script plus a synced teleprompter for recording on time. LinkedIn autoplays muted โ€” the film is designed to read with sound off; narration is backup.", + path: "NARRATION.md ยท teleprompter.html", repo: "ep01" }, + { id: "shots", x: 140, y: 230, w: 280, h: 52, label: "Screenshots/", sub: "own captures only", fill: "#161618", + kicker: "Guardrail", + body: "claude-goal, codex-goal, antigravity-goal, claude-routines. Only the authorโ€™s screenshots and headshot appear as real imagery. Conceptual graphics are original, inspired by OpenAIโ€™s Goals cookbook, never copied.", + path: "Screenshots/", repo: "ep01" }, + { id: "out", x: 480, y: 230, w: 280, h: 52, label: "out/", sub: "loops-vertical.mp4 ยท map-2x2.png", fill: "#161618", + kicker: "Result", + body: "The rendered film and the two-axis map. The Remotion implementation that produced them is kept private โ€” this repo is the spec, the verification approach, and the result.", + path: "out/loops-vertical.mp4", repo: "ep01" }, + { id: "l0", x: 40, y: 340, w: 220, h: 70, label: "0 ยท Build & static", sub: "compiles ยท assets resolve", fill: "#161618", + kicker: "Layer 0", body: "Both compositions register. Every asset resolves. Compiles clean. If this fails, nothing else runs.", path: "CAPTION_SCRIPT.md", repo: "ep01" }, + { id: "l1", x: 280, y: 340, w: 220, h: 70, label: "1 ยท Frame stills", sub: "caption actually on screen", fill: "#161618", + kicker: "Layer 1", body: "Render a still per scene. Confirm the locked caption is on screen, legible, not clipped. Evidence, not vibes.", path: "CAPTION_SCRIPT.md", repo: "ep01" }, + { id: "l2", x: 520, y: 340, w: 220, h: 70, label: "2 ยท Content", sub: "GOAL_TEXT identical ร—3", fill: "#161618", + kicker: "Layer 2", body: "One shared GOAL_TEXT typed identically in all three tools. Every locked string verbatim. Seven map pills correct. Only own imagery.", path: "CAPTION_SCRIPT.md", repo: "ep01" }, + { id: "l3", x: 760, y: 340, w: 200, h: 70, label: "3 ยท Full render", sub: "~58s, audio synced", fill: "#2a2114", + kicker: "Layer 3", body: "Both .mp4s render error-free, ~58 seconds, audio synced. Only then is the loop allowed to stop.", path: "out/loops-vertical.mp4", repo: "ep01" }, + ], + ep02: [ + { id: "cust", x: 40, y: 24, w: 180, h: 48, label: "CustomerView", sub: "#/customer", fill: "#13261f", + kicker: "Surface", body: "Sarah Chen (straight-through) or James Rodriguez (HITL). Generative cards land here: identity, risk gauge, allocation donut, document requests.", path: "frontend/src/views/CustomerView.jsx", repo: "ep02" }, + { id: "adv", x: 240, y: 24, w: 180, h: 48, label: "AdvisorView", sub: "#/advisor ยท desk", fill: "#13261f", + kicker: "Surface", body: "The approval queue. Completing a card completes the Camunda user task โ€” the paused token moves.", path: "frontend/src/views/AdvisorView.jsx", repo: "ep02" }, + { id: "ops", x: 440, y: 24, w: 180, h: 48, label: "OperatorView", sub: "raw AG-UI log", fill: "#13261f", + kicker: "Surface", body: "Open #/operator/{pid} next to a customer session and watch the raw wire produce the UI. The filmโ€™s honesty device.", path: "frontend/src/views/OperatorView.jsx", repo: "ep02" }, + { id: "cards", x: 640, y: 24, w: 180, h: 48, label: "Cards.jsx", sub: "generative UI", fill: "#13261f", + kicker: "UI", body: "PortfolioAllocationChart, ProjectedGrowthChart, IdentityResultCard, RiskGauge, AccountOpenedCard, UserTaskCardโ€ฆ each is a GENERATIVE_UI component name + props.", path: "frontend/src/components/Cards.jsx", repo: "ep02" }, + { id: "agui2", x: 280, y: 100, w: 300, h: 48, label: "agui-client.js ยท useAGUI", sub: "~150 lines, no framework", fill: "#2a2114", + kicker: "Client", body: "EventSource in, reducer out. events / cards / pendingTasks / steps. The learning-path item #1 in the README: this is how little a client needs.", path: "frontend/src/agui-client.js", repo: "ep02" }, + { id: "sse2", x: 280, y: 176, w: 300, h: 48, label: "sseHub.js", sub: "pub/sub keyed by pid", fill: "#2a2114", + kicker: "Bus", body: "In-process fan-out. Workers, pollers, and REST handlers call emit(pid, event). Late subscribers get history replay (max 500). A global channel feeds the advisor dashboard.", path: "backend/src/sseHub.js", repo: "ep02" }, + { id: "intake", x: 40, y: 256, w: 160, h: 48, label: "intakeAgent", sub: "docs by profile", fill: "#161618", + kicker: "Worker", body: "Resolves document requirements per customer, emits DocumentRequestCards. Dual intake: form or chat.", path: "backend/src/workers/intakeAgent.js", repo: "ep02" }, + { id: "ident", x: 220, y: 256, w: 160, h: 48, label: "identityAgent", sub: "KYC ยท PEP", fill: "#161618", + kicker: "Worker", body: "Doc validation, sanctions, PEP. Emits IdentityResultCard.", path: "backend/src/workers/identityAgent.js", repo: "ep02" }, + { id: "risk", x: 400, y: 256, w: 160, h: 48, label: "riskAgent", sub: "score ยท tier", fill: "#161618", + kicker: "Worker", body: "riskScore, aumTier, investorClass. Emits the gauge the customer actually wants instead of a sentence.", path: "backend/src/workers/riskAgent.js", repo: "ep02" }, + { id: "kb", x: 580, y: 256, w: 180, h: 48, label: "kbQueryWorker", sub: "grounded answers", fill: "#161618", + kicker: "Grounding", body: "RAG: retrieve, then synthesise a cited answer. Emits KbAnswerCard. Guardrail lives here.", path: "backend/src/workers/kbQueryWorker.js", repo: "ep02" }, + { id: "chat", x: 780, y: 256, w: 180, h: 48, label: "chatTools.js", sub: "~14 tools", fill: "#161618", + kicker: "Tools", body: "lookupApplicationStatus, readDocument, estimateTimelineโ€ฆ the deterministic jobs the AI Agent Connector may pick from the ad-hoc sub-process.", path: "backend/src/workers/chatTools.js", repo: "ep02" }, + { id: "onb", x: 40, y: 344, w: 280, h: 56, label: "wealth-onboarding.bpmn", sub: "intake โ†’ KYC โ†’ risk โ†’ DMN โ†’ HITL", fill: "#161618", + kicker: "Process", body: "The orchestrator. After risk, wealth-routing.dmn branches STP / ADVISOR_REVIEW / COMPLIANCE_ESCALATION. The AI did not decide to wait โ€” the process did.", path: "bpmn/wealth-onboarding.bpmn", repo: "ep02" }, + { id: "conv", x: 340, y: 344, w: 300, h: 56, label: "customer-conversation.bpmn", sub: "AI Agent Connector ยท ad-hoc tools", fill: "#161618", + kicker: "Process", body: "THE pattern. A long-lived conversation process. The connector hosts the LLM-with-tools loop inside Camunda, not in Node. Memory persists because agent.context flows turn-to-turn.", path: "bpmn/customer-conversation.bpmn", repo: "ep02" }, + { id: "dmn2", x: 660, y: 344, w: 300, h: 56, label: "wealth-routing.dmn", sub: "who sees a human", fill: "#2a2114", + kicker: "Policy", body: "Decision table. Private-wealth tiers go to a user task with a Camunda Form. Paused token, visible in Operate, completed from the advisor card.", path: "bpmn/wealth-routing.dmn", repo: "ep02" }, + { id: "seed", x: 40, y: 428, w: 280, h: 48, label: "client-kb-seed / query", sub: "vector store as a step", fill: "#161618", + kicker: "Grounding", body: "Embeddings Vector DB connector. Seed chunks the corpus; query retrieves. VECTORDB_LOCAL_SETUP.md if you want it local.", path: "bpmn/client-kb-seed.bpmn", repo: "ep02" }, + { id: "arch", x: 340, y: 428, w: 280, h: 48, label: "architecture.html", sub: "the poster, in-repo", fill: "#161618", + kicker: "Docs", body: "The whole shape on one page โ€” expandable marchitecture. presentation.html for a guided walkthrough. LEARNING_GUIDE.md if you want the prose.", path: "architecture.html", repo: "ep02" }, + { id: "shadow", x: 640, y: 428, w: 320, h: 48, label: "shadow mode", sub: "zero API keys, full stream", fill: "#13261f", + kicker: "Run", body: "Without ANTHROPIC_API_KEY the workers replay a scripted scenario. AG-UI stream, HITL pause, and Operate choreography all work before you spend a cent.", path: "backend/src/seed/scenarios.js", repo: "ep02" }, + ], + ep03: [ + { id: "shop", x: 40, y: 24, w: 200, h: 52, label: "Shopping agent", sub: "Claude + tools", fill: "#161618", + kicker: "Party 1", body: "search_inventory, create_mandate. Holds the open mandate, watches TicketVault for a price drop, signs the closed mandate at purchase time.", path: "backend/src/shoppingAgent.js", repo: "ep03" }, + { id: "merch", x: 280, y: 24, w: 200, h: 52, label: "Merchant", sub: "TicketVault :5177", fill: "#161618", + kicker: "Party 2", body: "Fake merchant site. Independently verifies the mandate chain (user sig, agent sig, constraints) before accepting checkout. The tamper test lands here.", path: "merchant-site/src/", repo: "ep03" }, + { id: "cred", x: 520, y: 24, w: 200, h: 52, label: "Credentials", sub: "issueToken stub", fill: "#161618", + kicker: "Party 3", body: "credentials.issueToken โ€” stubbed. MAKE_IT_REAL.md maps this onto a real credentials provider.", path: "backend/src/workers/index.js", repo: "ep03" }, + { id: "proc", x: 760, y: 24, w: 200, h: 52, label: "Processor", sub: "checkout rail", fill: "#161618", + kicker: "Party 4", body: "merchant.completeCheckout drives TicketVault through AP2 checkout steps 1โ€“6 over SSE (MERCHANT_SITE_STEP).", path: "backend/src/workers/index.js", repo: "ep03" }, + { id: "openm", x: 80, y: 120, w: 240, h: 56, label: "Open mandate", sub: "user signs the rules", fill: "#2a2114", + kicker: "AP2", body: "createOpenCheckoutMandate. Constraints: 2 tickets, target $3,500, cap $7,000. ES256 JWS via jose. The user signed this; the agent cannot rewrite it.", path: "backend/src/mandateService.js", repo: "ep03" }, + { id: "closed", x: 380, y: 120, w: 240, h: 56, label: "Closed mandate", sub: "agent signs the cart", fill: "#2a2114", + kicker: "AP2", body: "closeMandate binds a sha256 checkout hash of the actual cart. This is the v0.2 openโ†’closed lifecycle.", path: "backend/src/mandateService.js", repo: "ep03" }, + { id: "verify", x: 680, y: 120, w: 260, h: 56, label: "Merchant verify ร—3", sub: "user ยท agent ยท constraints", fill: "#2a2114", + kicker: "AP2", body: "merchantVerifyMandate. Change one number inside the signed token (tamperSignedMandate) and this check fails โ€” even if the agent proceeds and the DMN passes. That is the difference between a protocol and a database record.", path: "backend/src/mandateService.js", repo: "ep03" }, + { id: "cust3", x: 40, y: 216, w: 200, h: 48, label: "CustomerView", sub: "mandate + cart cards", fill: "#13261f", + kicker: "Surface", body: "Watching, waiting, then the four endings: receipt, cryptographic refusal, HITL wait, or MandateBreachCard.", path: "frontend/src/views/CustomerView.jsx", repo: "ep03" }, + { id: "desk3", x: 260, y: 216, w: 200, h: 48, label: "PaymentDeskView", sub: "HITL at 98% of cap", fill: "#13261f", + kicker: "Surface", body: "When the DMN requires a human, the token sits here. Approve and the merchant still verifies the chain.", path: "frontend/src/views/PaymentDeskView.jsx", repo: "ep03" }, + { id: "ops3", x: 480, y: 216, w: 200, h: 48, label: "OperatorView", sub: "PARTY_HOP strip", fill: "#13261f", + kicker: "Surface", body: "Four-party AP2 chain lights up hop by hop: shopping_agent โ†’ merchant โ†’ credentials โ†’ processor.", path: "frontend/src/views/OperatorView.jsx", repo: "ep03" }, + { id: "sse3", x: 700, y: 216, w: 260, h: 48, label: "sseHub.js (replay)", sub: "same bus as EP 02", fill: "#13261f", + kicker: "Shared DNA", body: "Copied forward. History replay so a late OperatorView still sees the mandate cards. Also fans out to the merchant-site channel.", path: "backend/src/sseHub.js", repo: "ep03" }, + { id: "bpmn3", x: 40, y: 300, w: 280, h: 52, label: "consumer-payment.bpmn", sub: "monitor โ†’ close โ†’ policy โ†’ verify", fill: "#161618", + kicker: "Process", body: "Service tasks: setup, start monitoring, close mandate, emit policy UI, merchant verify, issue credentials, complete checkout. Plus the breach path.", path: "bpmn/consumer-payment.bpmn", repo: "ep03" }, + { id: "stand", x: 340, y: 300, w: 280, h: 52, label: "standing-purchase.bpmn", sub: "epilogue path", fill: "#161618", + kicker: "Process", body: "The $7,600 standing purchase that exceeds the $7,000 cap. Auto-decline, MandateBreachCard.", path: "bpmn/standing-purchase.bpmn", repo: "ep03" }, + { id: "dmn3", x: 640, y: 300, w: 320, h: 52, label: "spending-policy.dmn", sub: "5 rules, first-hit", fill: "#2a2114", + kicker: "Policy", body: "Native Camunda DMN. Result linked to the process instance, visible in Operate. Approves the golden path, escalates HITL near the cap, declines a breach. Does not catch a tampered token โ€” thatโ€™s the merchantโ€™s job.", path: "dmn/spending-policy.dmn", repo: "ep03" }, + { id: "g", x: 40, y: 384, w: 220, h: 48, label: "Golden", sub: "$6,300 auto", fill: "#13261f", + kicker: "Path", body: "Price drops to $3,150. Agent signs closed mandate. DMN approves. Merchant verifies 3 โœ“. Autonomous checkout.", path: "CLICKPATH.md", repo: "ep03" }, + { id: "t", x: 280, y: 384, w: 220, h: 48, label: "Tamper", sub: "crypto refuses", fill: "#2a140f", + kicker: "Path", body: "Cap altered inside the signed token. Agent proceeds. DMN passes. Merchant ES256 check fails. No credentials issued. The point of the episode.", path: "backend/src/mandateService.js", repo: "ep03" }, + { id: "h", x: 520, y: 384, w: 220, h: 48, label: "HITL", sub: "98% of cap", fill: "#161618", + kicker: "Path", body: "Price $3,430, cart $6,860. DMN requires human sign-off at the Payment Desk, then verified checkout.", path: "frontend/src/views/PaymentDeskView.jsx", repo: "ep03" }, + { id: "e", x: 760, y: 384, w: 200, h: 48, label: "Epilogue", sub: "breach $7,600", fill: "#161618", + kicker: "Path", body: "Standing purchase exceeds the cap. Auto-decline. MandateBreachCard. The mandate still holds after the happy path.", path: "bpmn/standing-purchase.bpmn", repo: "ep03" }, + ], + shared: [ + { id: "s-sse", x: 80, y: 40, w: 400, h: 70, label: "sseHub.js", sub: "EP 02 โ†’ EP 03 ยท emit + history replay", fill: "#2a2114", + kicker: "Copied forward", body: "The AG-UI bus. Keyed channels, 500-event replay, global fan-out. EP 03 adds a merchant-site channel so TicketVault can be driven as a fourth surface. If you only read one file in both repos, read this one.", path: "backend/src/sseHub.js", repo: "ep02" }, + { id: "s-agui", x: 520, y: 40, w: 400, h: 70, label: "agui-client.js", sub: "EventSource โ†’ reducer โ†’ views", fill: "#2a2114", + kicker: "Copied forward", body: "No agent-UI framework. ~150 lines. Views are functions of the stream. EP 03 extends the reducer with mandate cards and PARTY_HOP.", path: "frontend/src/agui-client.js", repo: "ep02" }, + { id: "s-work", x: 80, y: 140, w: 400, h: 70, label: "workers/index.js", sub: "Zeebe jobs that emit UI", fill: "#161618", + kicker: "Pattern", body: "Every service task is a worker that does a deterministic thing, then emit()s an AG-UI event. The LLM does not paint the DOM. The process does not paint the DOM. The worker translates a job into a card.", path: "backend/src/workers/index.js", repo: "ep02" }, + { id: "s-poll", x: 520, y: 140, w: 400, h: 70, label: "taskPoller + Tasklist", sub: "HITL as a user task", fill: "#161618", + kicker: "Pattern", body: "User tasks are Camundaโ€™s, not a homemade modal. The poller surfaces them as USER_TASK_PENDING; completing the card completes the job. EP 02 advisor desk and EP 03 payment desk are the same shape.", path: "backend/src/taskPoller.js", repo: "ep02" }, + { id: "s-bpmn", x: 80, y: 240, w: 400, h: 70, label: "BPMN owns the loop", sub: "connector inside the process", fill: "#161618", + kicker: "Pattern", body: "EP 01 said the loop you can audit is the only one that ships. EP 02 put that loop inside Camunda (AI Agent Connector). EP 03 put payment inside the same engine so Operate shows every hop.", path: "bpmn/", repo: "ep02" }, + { id: "s-dmn", x: 520, y: 240, w: 400, h: 70, label: "DMN owns the fork", sub: "humans when the table says so", fill: "#161618", + kicker: "Pattern", body: "Routing (wealth) and spending policy are tables, not if-statements in the agent. First-hit, linked to the process instance, visible in Operate. The agent does not get a vote on whether a human is required.", path: "dmn/ ยท bpmn/*.dmn", repo: "ep03" }, + { id: "s-ops", x: 80, y: 340, w: 400, h: 70, label: "OperatorView", sub: "the audit surface in the film", fill: "#13261f", + kicker: "Pattern", body: "Always a raw-wire view next to the pretty one. If the card appeared, the event existed. That is the seriesโ€™ honesty contract.", path: "frontend/src/views/OperatorView.jsx", repo: "ep02" }, + { id: "s-shadow", x: 520, y: 340, w: 400, h: 70, label: "Runnable without the pitch", sub: "shadow mode ยท MIT ยท public", fill: "#13261f", + kicker: "Contract", body: "EP 02 runs with zero API keys. EP 03 is fully runnable with a free Camunda cluster. Specs and field guides live beside the code. The films are the trailer; the repos are the product.", path: "README.md", repo: "ep02" }, + ], +}; + +const EDGES = { + stack: [ + ["humans", "agui"], ["agui", "loop"], ["loop", "bff"], ["loop", "camunda"], + ["bff", "ground"], ["camunda", "auth"], ["ground", "policy"], ["auth", "policy"], + ], + ep01: [ + ["goal", "spec"], ["goal", "captions"], ["goal", "dive"], ["goal", "narr"], + ["spec", "shots"], ["captions", "out"], + ["captions", "l0"], ["l0", "l1"], ["l1", "l2"], ["l2", "l3"], + ], + ep02: [ + ["cust", "agui2"], ["adv", "agui2"], ["ops", "agui2"], ["cards", "agui2"], + ["agui2", "sse2"], + ["sse2", "intake"], ["sse2", "ident"], ["sse2", "risk"], ["sse2", "kb"], ["sse2", "chat"], + ["intake", "onb"], ["ident", "onb"], ["risk", "onb"], ["onb", "dmn2"], + ["chat", "conv"], ["kb", "seed"], + ], + ep03: [ + ["shop", "openm"], ["openm", "closed"], ["closed", "verify"], ["verify", "merch"], + ["shop", "cust3"], ["merch", "ops3"], ["cred", "proc"], + ["cust3", "sse3"], ["desk3", "sse3"], ["ops3", "sse3"], + ["sse3", "bpmn3"], ["bpmn3", "dmn3"], ["stand", "e"], + ["dmn3", "g"], ["dmn3", "t"], ["dmn3", "h"], + ], + shared: [ + ["s-sse", "s-agui"], ["s-sse", "s-work"], ["s-agui", "s-ops"], + ["s-work", "s-bpmn"], ["s-poll", "s-dmn"], ["s-bpmn", "s-ops"], + ], +}; + +function el(tag, attrs, children) { + const n = document.createElementNS("http://www.w3.org/2000/svg", tag); + Object.entries(attrs || {}).forEach(([k, v]) => n.setAttribute(k, v)); + (children || []).forEach((c) => n.appendChild(typeof c === "string" ? document.createTextNode(c) : c)); + return n; +} + +function center(node) { + return { x: node.x + node.w / 2, y: node.y + node.h / 2 }; +} + +function renderView(viewId, selectedId) { + const stage = document.getElementById("cmap-stage"); + const nodes = NODES[viewId]; + const edges = EDGES[viewId] || []; + const byId = Object.fromEntries(nodes.map((n) => [n.id, n])); + const svg = el("svg", { + viewBox: "0 0 1000 520", + role: "img", + "aria-label": VIEWS[viewId].title, + }); + + const defs = el("defs", {}, [ + el("marker", { id: "arrow", viewBox: "0 0 10 10", refX: "8", refY: "5", markerWidth: "7", markerHeight: "7", orient: "auto-start-reverse" }, [ + el("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: "#3a3a40" }), + ]), + ]); + svg.appendChild(defs); + + edges.forEach(([a, b]) => { + const na = byId[a], nb = byId[b]; + if (!na || !nb) return; + const ca = center(na), cb = center(nb); + const hi = selectedId && (selectedId === a || selectedId === b); + svg.appendChild(el("path", { + class: hi ? "edge hi" : "edge", + d: `M ${ca.x} ${ca.y} C ${ca.x} ${(ca.y + cb.y) / 2}, ${cb.x} ${(ca.y + cb.y) / 2}, ${cb.x} ${cb.y}`, + "marker-end": "url(#arrow)", + })); + }); + + nodes.forEach((node) => { + const g = el("g", { + class: "cmap-node" + (selectedId === node.id ? " is-active" : ""), + tabindex: "0", + role: "button", + "data-id": node.id, + "aria-label": node.label, + }); + g.appendChild(el("rect", { + x: node.x, y: node.y, width: node.w, height: node.h, rx: "12", + fill: node.fill || "#161618", + stroke: selectedId === node.id ? "#ef9f27" : "#2a2a2e", + "stroke-width": selectedId === node.id ? "2" : "1", + })); + g.appendChild(el("text", { + x: node.x + 16, y: node.y + (node.sub ? node.h / 2 - 4 : node.h / 2 + 5), + fill: "#f2f2f0", "font-size": "13", "font-weight": "600", + "font-family": "Inter, system-ui, sans-serif", + }, [node.label])); + if (node.sub) { + g.appendChild(el("text", { + x: node.x + 16, y: node.y + node.h / 2 + 14, + fill: "#9c9a92", "font-size": "11", + "font-family": "JetBrains Mono, ui-monospace, monospace", + }, [node.sub])); + } + g.addEventListener("click", (e) => { + e.stopPropagation(); + selectNode(viewId, node.id); + }); + g.addEventListener("keydown", (e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + selectNode(viewId, node.id); + } + }); + svg.appendChild(g); + }); + + stage.replaceChildren(svg); +} + +function inspect(node) { + const box = document.getElementById("inspector"); + if (!node) { + box.innerHTML = ` +
Inspector
+

Click a node

+

Every box is a real file, protocol, or demo path from the public repos. Edges are runtime or verification flow, not folder nesting.

+

Keyboard: Tab through nodes, Enter to inspect.

`; + return; + } + const repoUrl = REPOS[node.repo]; + const fileUrl = node.path && repoUrl + ? `${repoUrl}/blob/main/${node.path.split(" ")[0].replace(/\/$/, "")}` + : repoUrl; + box.innerHTML = ` +
${node.kicker || "Node"}
+

${node.label}

+

${node.body}

+ ${node.path ? `
${node.path}
` : ""} + `; +} + +let currentView = "stack"; +let currentNode = null; + +function selectNode(viewId, nodeId) { + currentNode = NODES[viewId].find((n) => n.id === nodeId) || null; + renderView(viewId, nodeId); + inspect(currentNode); +} + +function setView(viewId, nodeId) { + if (!VIEWS[viewId]) viewId = "stack"; + currentView = viewId; + document.querySelectorAll(".view-tabs button").forEach((b) => { + b.setAttribute("aria-selected", String(b.dataset.view === viewId)); + }); + const meta = VIEWS[viewId]; + document.getElementById("cmap-title").textContent = meta.title; + document.getElementById("cmap-sub").textContent = meta.subtitle; + const first = nodeId || null; + currentNode = first ? NODES[viewId].find((n) => n.id === first) : null; + renderView(viewId, first); + inspect(currentNode); + history.replaceState(null, "", "#" + viewId); +} + +function init() { + document.querySelectorAll(".view-tabs button").forEach((b) => { + b.addEventListener("click", () => setView(b.dataset.view)); + }); + document.getElementById("cmap-stage").addEventListener("click", () => { + currentNode = null; + renderView(currentView, null); + inspect(null); + }); + const hash = (location.hash || "#stack").slice(1).toLowerCase(); + setView(["stack", "ep01", "ep02", "ep03", "shared"].includes(hash) ? hash : "stack"); +} + +document.addEventListener("DOMContentLoaded", init); +window.addEventListener("hashchange", () => { + const hash = (location.hash || "#stack").slice(1).toLowerCase(); + if (hash !== currentView && VIEWS[hash]) setView(hash); +}); diff --git a/docs/assets/favicon.svg b/docs/assets/favicon.svg new file mode 100644 index 0000000..6f891f4 --- /dev/null +++ b/docs/assets/favicon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/assets/site.css b/docs/assets/site.css new file mode 100644 index 0000000..43c9808 --- /dev/null +++ b/docs/assets/site.css @@ -0,0 +1,507 @@ +/* Agents, Orchestrated โ€” series hub + Design tokens follow the EP 01 film: dark stage, amber /goal, teal for the rest. */ + +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap"); + +:root { + --bg: #0b0b0c; + --bg-elev: #101012; + --surface: #161618; + --surface-2: #1c1c1f; + --border: #2a2a2e; + --border-strong: #3a3a40; + --text: #f2f2f0; + --muted: #9c9a92; + --faint: #6b6a64; + --amber: #ef9f27; + --amber-dim: rgba(239, 159, 39, 0.14); + --teal: #1d9e75; + --teal-dim: rgba(29, 158, 117, 0.14); + --blue: #5b8def; + --blue-dim: rgba(91, 141, 239, 0.14); + --gold: #c9a44a; + --red: #e05a5a; + --purple: #9b7fe0; + --shadow: 0 24px 80px rgba(0, 0, 0, 0.45); + --radius: 16px; + --font: "Inter", system-ui, sans-serif; + --mono: "JetBrains Mono", ui-monospace, monospace; + --max: 1120px; + --nav-h: 64px; +} + +*, *::before, *::after { box-sizing: border-box; } +html { scroll-behavior: smooth; } +html, body { margin: 0; padding: 0; } +body { + font-family: var(--font); + background: var(--bg); + color: var(--text); + line-height: 1.55; + min-height: 100vh; + -webkit-font-smoothing: antialiased; +} +img { max-width: 100%; display: block; } +a { color: inherit; text-decoration: none; } +button { font-family: inherit; } +code, .mono { font-family: var(--mono); } + +.skip { + position: absolute; left: -999px; top: 0; + background: var(--amber); color: #111; padding: 8px 12px; z-index: 100; +} +.skip:focus { left: 12px; top: 12px; } + +/* โ”€โ”€ Grain + glow โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +body::before { + content: ""; + position: fixed; inset: 0; pointer-events: none; z-index: 0; + background-image: + radial-gradient(ellipse 80% 50% at 50% -10%, rgba(239, 159, 39, 0.08), transparent 55%), + radial-gradient(ellipse 40% 30% at 90% 20%, rgba(29, 158, 117, 0.05), transparent 50%); +} +body::after { + content: ""; + position: fixed; inset: 0; pointer-events: none; z-index: 1; + opacity: 0.035; + background-image: url("data:image/svg+xml;utf8,"); +} + +.wrap { position: relative; z-index: 2; } + +/* โ”€โ”€ Nav โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.nav { + position: sticky; top: 0; z-index: 20; + height: var(--nav-h); + display: flex; align-items: center; justify-content: space-between; + padding: 0 28px; + background: rgba(11, 11, 12, 0.78); + backdrop-filter: blur(16px); + border-bottom: 1px solid var(--border); +} +.brand { + display: flex; align-items: center; gap: 10px; + font-weight: 600; letter-spacing: -0.02em; +} +.brand-mark { + width: 28px; height: 28px; +} +.brand-kicker { + display: block; + font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; + color: var(--amber); font-weight: 600; +} +.brand-name { font-size: 14px; } +.nav-links { display: flex; gap: 6px; align-items: center; } +.nav-links a, .nav-links .ghost { + font-size: 13px; color: var(--muted); padding: 8px 12px; border-radius: 999px; + transition: color 0.15s, background 0.15s; +} +.nav-links a:hover, .nav-links a[aria-current="page"] { + color: var(--text); background: var(--surface); +} +.nav-cta { + background: var(--amber) !important; + color: #14110a !important; + font-weight: 600 !important; + padding: 8px 14px !important; +} +.nav-cta:hover { filter: brightness(1.05); } +.nav-toggle { + display: none; background: none; border: 1px solid var(--border); + color: var(--text); width: 40px; height: 36px; border-radius: 8px; cursor: pointer; +} + +/* โ”€โ”€ Layout โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.container { width: min(var(--max), calc(100% - 48px)); margin: 0 auto; } +.section { padding: 88px 0; } +.section-sm { padding: 56px 0; } +.eyebrow { + font-size: 11px; font-weight: 600; letter-spacing: 0.22em; + text-transform: uppercase; color: var(--amber); margin-bottom: 14px; +} +.eyebrow.teal { color: var(--teal); } +.eyebrow.blue { color: var(--blue); } +h1, h2, h3 { letter-spacing: -0.03em; line-height: 1.12; margin: 0 0 16px; } +h1 { font-size: clamp(40px, 6.4vw, 72px); font-weight: 800; } +h2 { font-size: clamp(28px, 3.4vw, 40px); font-weight: 700; } +h3 { font-size: 20px; font-weight: 600; } +.lede { + font-size: clamp(17px, 2vw, 20px); + color: var(--muted); max-width: 640px; line-height: 1.55; +} +.accent { color: var(--amber); } + +/* โ”€โ”€ Buttons โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.btn-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; } +.btn { + display: inline-flex; align-items: center; gap: 8px; + padding: 11px 18px; border-radius: 999px; font-size: 14px; font-weight: 600; + border: 1px solid transparent; transition: transform 0.15s, background 0.15s, border-color 0.15s; +} +.btn:hover { transform: translateY(-1px); } +.btn-primary { background: var(--amber); color: #14110a; } +.btn-ghost { background: var(--surface); border-color: var(--border); color: var(--text); } +.btn-ghost:hover { border-color: var(--border-strong); } +.btn-link { color: var(--muted); padding-left: 8px; } +.btn-link:hover { color: var(--text); } + +/* โ”€โ”€ Hero โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.hero { + padding: 72px 0 40px; + text-align: center; +} +.hero .lede { margin: 0 auto; } +.hero h1 { margin-bottom: 20px; } +.hero-quote { + margin: 28px auto 0; + max-width: 520px; + font-size: 15px; color: var(--faint); + font-style: italic; +} +.stats { + display: grid; grid-template-columns: repeat(4, 1fr); + gap: 12px; margin-top: 56px; +} +.stat { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 20px 18px; + text-align: left; +} +.stat b { + display: block; font-size: 22px; font-weight: 700; letter-spacing: -0.03em; + margin-bottom: 4px; +} +.stat span { font-size: 13px; color: var(--muted); } + +/* โ”€โ”€ Moat strip โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.moats { + display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; +} +.moat { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 22px 20px; + min-height: 180px; +} +.moat .n { + font-family: var(--mono); font-size: 11px; color: var(--amber); + letter-spacing: 0.12em; margin-bottom: 12px; +} +.moat h3 { font-size: 16px; margin-bottom: 8px; } +.moat p { margin: 0; font-size: 13.5px; color: var(--muted); } + +/* โ”€โ”€ Episode cards โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.episode { + display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 40px; + align-items: center; + padding: 36px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 24px; + margin-bottom: 20px; + box-shadow: var(--shadow); +} +.episode.reverse { grid-template-columns: 0.95fr 1.05fr; } +.episode.reverse .episode-copy { order: 2; } +.episode-meta { + display: flex; gap: 10px; align-items: center; margin-bottom: 12px; + font-family: var(--mono); font-size: 12px; color: var(--muted); +} +.ep-num { color: var(--amber); font-weight: 600; } +.ep-moat { + display: inline-block; + padding: 3px 8px; border-radius: 999px; + background: var(--amber-dim); color: var(--amber); + font-size: 11px; font-weight: 600; letter-spacing: 0.04em; +} +.ep-moat.teal { background: var(--teal-dim); color: #3ecf9a; } +.ep-moat.gold { background: rgba(201, 164, 74, 0.16); color: var(--gold); } +.episode h2 { font-size: clamp(26px, 3vw, 34px); } +.episode blockquote { + margin: 16px 0; + padding-left: 14px; + border-left: 2px solid var(--amber); + color: var(--text); + font-size: 16px; font-style: italic; +} +.episode p { color: var(--muted); font-size: 15px; margin: 0 0 8px; } +.shot { + position: relative; + border-radius: 14px; + overflow: hidden; + border: 1px solid var(--border); + background: #0a0a0b; + aspect-ratio: 16 / 10; +} +.shot img { width: 100%; height: 100%; object-fit: cover; } +.shot-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; } +.shot-pair .shot { aspect-ratio: 4 / 3; } +.shot figcaption { + position: absolute; left: 10px; bottom: 10px; + font-size: 11px; font-family: var(--mono); color: #fff; + background: rgba(0,0,0,0.55); padding: 3px 8px; border-radius: 6px; +} + +/* โ”€โ”€ Stack โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.stack { + display: flex; flex-direction: column; gap: 8px; + max-width: 820px; margin: 32px auto 0; +} +.stack-row { + display: grid; grid-template-columns: 140px 1fr auto; + gap: 16px; align-items: center; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 12px; + padding: 14px 18px; +} +.stack-row .layer { + font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; + text-transform: uppercase; color: var(--faint); +} +.stack-row strong { font-size: 15px; } +.stack-row span { color: var(--muted); font-size: 13px; } +.pill { + font-family: var(--mono); font-size: 11px; + padding: 4px 8px; border-radius: 999px; border: 1px solid var(--border); + color: var(--muted); white-space: nowrap; +} +.pill.ep01 { color: var(--amber); border-color: rgba(239,159,39,0.35); } +.pill.ep02 { color: #3ecf9a; border-color: rgba(29,158,117,0.4); } +.pill.ep03 { color: var(--gold); border-color: rgba(201,164,74,0.4); } +.pill.next { color: var(--blue); border-color: rgba(91,141,239,0.4); } + +/* โ”€โ”€ Protocol row โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.protocols { + display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 28px; +} +.proto { + background: var(--bg-elev); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 20px; +} +.proto .name { + font-family: var(--mono); font-size: 13px; font-weight: 600; + color: var(--text); margin-bottom: 6px; +} +.proto p { margin: 0; font-size: 13px; color: var(--muted); } +.proto .owns { font-size: 11px; color: var(--amber); margin-top: 12px; font-family: var(--mono); } + +/* โ”€โ”€ Curious CTA โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.curious { + display: grid; grid-template-columns: 1.2fr 0.8fr; gap: 32px; align-items: center; + background: + radial-gradient(ellipse at 100% 0%, rgba(29,158,117,0.12), transparent 50%), + var(--surface); + border: 1px solid var(--border); + border-radius: 24px; + padding: 40px; +} +.curious-visual { + border: 1px dashed var(--border-strong); + border-radius: 14px; + padding: 18px; + font-family: var(--mono); font-size: 11px; color: var(--muted); + background: #0d0d0f; + line-height: 1.7; +} +.curious-visual .hl { color: var(--amber); } +.curious-visual .ok { color: var(--teal); } + +/* โ”€โ”€ Next โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.next-card { + background: var(--surface); + border: 1px dashed var(--border-strong); + border-radius: 24px; + padding: 36px; +} +.next-card p { color: var(--muted); max-width: 640px; } + +/* โ”€โ”€ Footer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +footer { + border-top: 1px solid var(--border); + padding: 36px 0 48px; + color: var(--muted); font-size: 13px; +} +.foot { + display: flex; justify-content: space-between; gap: 24px; flex-wrap: wrap; +} +.foot a:hover { color: var(--text); } + +/* โ”€โ”€ Codemap chrome โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.cmap-hero { padding: 48px 0 12px; } +.cmap-hero .lede { max-width: 680px; } +.view-tabs { + display: flex; flex-wrap: wrap; gap: 6px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 12px; + padding: 5px; + margin: 28px 0 20px; + width: fit-content; +} +.view-tabs button { + background: transparent; border: 0; color: var(--muted); + padding: 8px 14px; border-radius: 8px; cursor: pointer; + font-size: 13px; font-weight: 500; +} +.view-tabs button[aria-selected="true"] { + background: var(--bg); color: var(--text); box-shadow: 0 1px 0 var(--border); +} +.view-tabs button:hover:not([aria-selected="true"]) { color: var(--text); } + +.cmap-shell { + display: grid; + grid-template-columns: 1fr 340px; + gap: 16px; + align-items: start; + min-height: 640px; +} +.cmap-stage { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 20px; + min-height: 640px; + overflow: hidden; + position: relative; +} +.cmap-stage svg { display: block; width: 100%; height: auto; } +.inspector { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 20px; + padding: 22px; + position: sticky; top: calc(var(--nav-h) + 16px); + min-height: 280px; +} +.inspector .kicker { + font-family: var(--mono); font-size: 11px; color: var(--amber); + letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 8px; +} +.inspector h3 { font-size: 18px; margin-bottom: 8px; } +.inspector p { color: var(--muted); font-size: 14px; margin: 0 0 12px; } +.inspector .path { + font-family: var(--mono); font-size: 11.5px; color: var(--faint); + background: var(--bg); border: 1px solid var(--border); + border-radius: 8px; padding: 8px 10px; margin: 12px 0; word-break: break-all; +} +.inspector .links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } +.node-chip { + font-family: var(--mono); font-size: 11px; + padding: 4px 8px; border-radius: 6px; + background: var(--bg); border: 1px solid var(--border); color: var(--muted); +} + +/* SVG node styles */ +.cmap-node { cursor: pointer; } +.cmap-node:focus { outline: none; } +.cmap-node rect, .cmap-node circle { + transition: fill 0.15s, stroke 0.15s, filter 0.15s; +} +.cmap-node:hover rect, .cmap-node.is-active rect { + filter: brightness(1.15); +} +.cmap-node.is-active rect { + stroke: var(--amber); + stroke-width: 2; +} +.edge { fill: none; stroke: #3a3a40; stroke-width: 1.25; } +.edge.hi { stroke: var(--amber); stroke-width: 1.8; } + +.legend { + display: flex; flex-wrap: wrap; gap: 14px; + font-size: 12px; color: var(--muted); margin: 12px 0 32px; +} +.legend i { + display: inline-block; width: 10px; height: 10px; border-radius: 3px; + margin-right: 6px; vertical-align: -1px; +} + +.vocab { + display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; +} +.vocab-item { + background: var(--surface); border: 1px solid var(--border); + border-radius: 12px; padding: 14px 16px; +} +.vocab-item code { color: var(--amber); font-size: 12px; } +.vocab-item p { margin: 6px 0 0; font-size: 13px; color: var(--muted); } + +.shared-grid { + display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 16px; +} +.shared-card { + background: var(--surface); border: 1px solid var(--border); + border-radius: 14px; padding: 18px; +} +.shared-card h3 { font-size: 15px; } +.shared-card p { margin: 0; font-size: 13px; color: var(--muted); } + +/* โ”€โ”€ Loop 2ร—2 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.axes { + position: relative; + background: #101012; + border: 1px solid var(--border); + border-radius: 20px; + padding: 48px 28px 36px 56px; + min-height: 380px; +} +.axes-grid { + display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; + gap: 10px; height: 300px; +} +.quad { + border: 1px dashed var(--border); + border-radius: 12px; padding: 14px; + background: rgba(255,255,255,0.015); +} +.quad.hot { border-color: rgba(239,159,39,0.45); background: var(--amber-dim); } +.dot { + display: inline-flex; align-items: center; gap: 6px; + font-size: 12px; margin: 4px 8px 4px 0; + padding: 4px 8px; border-radius: 999px; background: var(--surface-2); + border: 1px solid var(--border); +} +.axis-x, .axis-y { + position: absolute; font-family: var(--mono); font-size: 10px; + letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint); +} +.axis-x { bottom: 10px; left: 56px; right: 28px; display: flex; justify-content: space-between; } +.axis-y { + top: 48px; bottom: 36px; left: 10px; writing-mode: vertical-rl; + transform: rotate(180deg); display: flex; justify-content: space-between; +} + +/* โ”€โ”€ Responsive โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +@media (max-width: 900px) { + .nav-toggle { display: inline-flex; align-items: center; justify-content: center; } + .nav-links { + display: none; position: absolute; top: var(--nav-h); left: 0; right: 0; + flex-direction: column; background: var(--bg-elev); + border-bottom: 1px solid var(--border); padding: 12px; + } + .nav-links.open { display: flex; } + .stats, .moats, .protocols { grid-template-columns: 1fr 1fr; } + .episode, .episode.reverse, .curious, .cmap-shell { grid-template-columns: 1fr; } + .episode.reverse .episode-copy { order: 0; } + .stack-row { grid-template-columns: 1fr; gap: 6px; } + .inspector { position: static; } + .vocab, .shared-grid { grid-template-columns: 1fr; } +} +@media (max-width: 560px) { + .container { width: min(var(--max), calc(100% - 28px)); } + .stats, .moats, .protocols { grid-template-columns: 1fr; } + .hero { padding-top: 40px; } + .section { padding: 56px 0; } + .episode, .curious, .next-card { padding: 22px; } +} + +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + .btn:hover { transform: none; } +} diff --git a/docs/assets/site.js b/docs/assets/site.js new file mode 100644 index 0000000..f061725 --- /dev/null +++ b/docs/assets/site.js @@ -0,0 +1,16 @@ +(function () { + const toggle = document.querySelector(".nav-toggle"); + const links = document.querySelector(".nav-links"); + if (toggle && links) { + toggle.addEventListener("click", () => { + const open = links.classList.toggle("open"); + toggle.setAttribute("aria-expanded", String(open)); + }); + links.querySelectorAll("a").forEach((a) => { + a.addEventListener("click", () => { + links.classList.remove("open"); + toggle.setAttribute("aria-expanded", "false"); + }); + }); + } +})(); diff --git a/docs/codemap.html b/docs/codemap.html new file mode 100644 index 0000000..b34867f --- /dev/null +++ b/docs/codemap.html @@ -0,0 +1,155 @@ + + + + + + Codemap โ€” Agents, Orchestrated + + + + + + +
+ + +
+
+

For the extra curious

+

Visual codemap

+

+ The three public repos as one picture. Nodes are files, protocols, or demo paths. + Edges are runtime flow โ€” events, jobs, signatures โ€” not folder nesting. +

+
+ +
+ + + + + +
+ +

The series as one system

+

+
+ amber = the check / the signature + teal = a human surface + graphite = process, worker, spec +
+ +
+
+ +
+ +
+

EP 01 ยท the map inside the film

+

Two questions define any loop.

+

+ Continuation on the x-axis, locus on the y-axis. The interesting differences all sit on the right: + how โ€œdoneโ€ is decided. +

+
+
cloud-persistentin-session
+
+
+ trigger ยท cloud
+ Claude Routines + Antigravity /schedule + Cursor Automations +
+
+ verification ยท cloud
+ Codex Goal mode + Antigravity /goal +
+
+ trigger ยท session
+ Claude /loop +
+
+ verification ยท session
+ Claude Code /goal +
+
+
gated by a triggergated by verification
+
+
+ +
+

The wire ยท EP 02โ€“03

+

Event vocabulary

+

+ Everything the UI does is a typed event on one SSE stream. Same names in AURUM and in AP2. +

+
+
TEXT_MESSAGE_*

Streaming concierge copy. Start / content delta / end.

+
TOOL_CALL_*

An agent step with args and results. OperatorView lives on these.

+
GENERATIVE_UI

A real component: component + props. The donut, the gauge, the mandate card.

+
USER_TASK_PENDING

A Camunda user task, surfaced as a card. Completing the card completes the job.

+
STEP_STARTED / FINISHED

Process progress. The operatorโ€™s raw log and the customerโ€™s stepper share this.

+
PARTY_HOP

EP 03 only. Lights the four-party AP2 strip: shopping agent โ†’ merchant โ†’ credentials โ†’ processor.

+
+
+ +
+

How to read a PR in these repos

+

Start at the wire, not the model.

+
+
+

1. The event

+

If a card appeared, an event was emitted. Find emit( in the worker. That is the contract.

+
+
+

2. The job

+

The worker is a Zeebe job. The BPMN task type is the function name. Operate will show the same sequence.

+
+
+

3. The fork

+

If a human appeared, a DMN table or a user task put them there. The agent did not โ€œdecide to escalate.โ€

+
+
+

4. The proof

+

EP 01: frame stills. EP 02: operator log. EP 03: merchant signature check. Pick the proof before you pick the model.

+
+
+
+ +
+ +
+
+ +
+
+
Agents, Orchestrated ยท MIT ยท 2026 Zishan Ali Khan
+
Series ยท Hub
+
+
+
+ + + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..fb5c1a6 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,334 @@ + + + + + + Agents, Orchestrated โ€” real builds on the agentic stack + + + + + + + + + +
+ + +
+
+

Three episodes ยท one control plane

+

Autonomy is table stakes.
The moat is everything around the agent.

+

+ Real builds on the agentic stack. Each episode takes one layer everyone is talking about โ€” + agent loops, generative UI, agents that pay โ€” and turns it into a working system, then ships + the film and the code. +

+ +

These arenโ€™t toys. Every build carries verification, orchestration, human oversight, grounding, and an audit trail.

+ +
+
03shipped episodes, one layer each
+
04open protocols in play: MCP, A2A, AG-UI, AP2
+
$7ksigned spending ceiling the agent cannot override
+
0API keys required to run EP 02 in shadow mode
+
+
+ +
+

What actually ships

+

Four moats. Autonomy is not one of them.

+

Every lab can loop now. The product is how you trust what it did.

+
+
+
EP 01
+

Verification

+

Generate โ†’ run โ†’ check โ†’ repeat. โ€œDoneโ€ is evidence, not vibes. The loop you can audit is the only loop that ships.

+
+
+
EP 02
+

HITL + orchestration

+

The process pauses for a human, visibly, in Camunda Operate. The LLM is the language layer. The process is the orchestrator.

+
+
+
EP 02
+

Grounding

+

What the agent canโ€™t verify, it doesnโ€™t invent. Knowledge-base retrieval is a process step, not a prompt prefix.

+
+
+
EP 03
+

Cryptographic authority

+

You sign the rules. The agent signs the cart. The merchant verifies both. Tampering a number inside a signed token is a refusal, not a warning.

+
+
+
+ +
+

The series

+

Episodes

+

Films land on LinkedIn. Every build is a public repo.

+ +
+
+
+ EP 01 ยท Loops + verification is the moat +
+

He stopped writing code. He writes loops.

+
Every agentic coding tool converged on the same shape: goal in, iterate, verify each pass.
+

+ Claude Code, Codex, and Antigravity all shipped a /goal-style command, independently, within months. + Autonomy stopped being the differentiator. How each loop decides itโ€™s done โ€” independent judge, tests-until-green, + auditable artifacts โ€” is the product. +

+

+ The film was itself a loop: one /goal pointed at a spec, then a four-layer verification pass + (compile โ†’ frame stills โ†’ locked copy โ†’ full render) before calling the job done. +

+ +
+
+
+ Two-by-two map of agentic coding loops: trigger vs verification, in-session vs cloud-persistent +
the 2ร—2 โ€” verification sits on the right
+
+
+
+ +
+
+
+ EP 02 ยท AG-UI + HITL + orchestration + grounding +
+

Agents can stream the whole interface, not just text.

+
AURUM: a private bank onboarding clients on Camunda 8.
+

+ Every agent decision streams to the browser as live, clickable UI over the open AG-UI protocol. + When the money gets serious, the process pauses for a human โ€” visibly, in Camunda Operate. + MCP gives agents tools. A2A talks to other agents. AG-UI is the last mile: where work becomes something a human can click. +

+

+ Fully runnable. Without API keys it still runs โ€” shadow mode replays a deterministic scenario so you can watch the stream before wiring a cluster. +

+ +
+
+
+ AURUM customer view with generative UI cards +
customer
+
+
+ AURUM advisor approval desk +
advisor desk
+
+
+
+ +
+
+
+ EP 03 ยท AP2 + cryptographic authority + governance +
+

An agent with a signed spending limit. Then three times it refuses to spend.

+
How do you let an AI buy things for you safely?
+

+ AP2 โ€” the Agent Payments Protocol, started by Google and handed to the FIDO Alliance โ€” answers with signed mandates. + You sign the rules. The agent signs the cart. The merchant verifies both before taking a cent. +

+

+ A $7,000 ceiling. World Cup tickets while you sleep. Tampering the signed limit is refused by cryptography; + getting close to the limit is escalated by a Camunda 8 decision table. Four paths: golden, tamper, HITL, breach. +

+ +
+
+
+ AP2 shopping agent customer view +
customer
+
+
+ TicketVault merchant site verifying the mandate chain +
TicketVault merchant
+
+
+
+
+ +
+

How the layers compose

+

The process is the orchestrator. The LLM is the language layer.

+

+ Each episode adds a layer around the agent. Read top-down: humans click, AG-UI carries the wire, + the loop runs, Camunda holds the token, policy and cryptography decide whether money moves. +

+
+
+
Humans
+
Customer ยท advisor ยท operator ยท merchant
Three surfaces, one stream. The operator view is the โ€œdonโ€™t take my word for itโ€ log.
+ EP 02โ€“03 +
+
+
Wire
+
AG-UI over SSE
TEXT_MESSAGE, TOOL_CALL, GENERATIVE_UI, USER_TASK โ€” typed events, not a chat dump.
+ EP 02โ€“03 +
+
+
Loop
+
Generate โ†’ run โ†’ check โ†’ repeat
Done is a four-layer check, or a DMN table, or a merchant signature โ€” never the worker grading itself.
+ EP 01 +
+
+
Control
+
Camunda 8 ยท BPMN + user tasks
The AI Agent Connector hosts the tool loop inside the process. Paused tokens are the product.
+ EP 02โ€“03 +
+
+
Grounding
+
Knowledge base as a process step
Retrieve, then answer. What it canโ€™t verify, it doesnโ€™t invent.
+ EP 02 +
+
+
Authority
+
AP2 mandates ยท ES256 JWS
Open mandate (you sign the rules) โ†’ closed mandate (agent signs the cart) โ†’ merchant verifies the chain.
+ EP 03 +
+
+
Policy
+
DMN decision tables
Wealth routing. Spending policy. First-hit rules linked to the process instance, visible in Operate.
+ EP 02โ€“03 +
+
+ +
+
+
MCP
+

Connects agents to tools and context.

+
owns the toolbox
+
+
+
A2A
+

Connects agents to other agents.

+
owns the conversation
+
+
+
AG-UI
+

Connects agents to users โ€” the last mile.

+
owns the click ยท EP 02
+
+
+
AP2
+

Connects agents to money, with a signature.

+
owns the spend ยท EP 03
+
+
+
+ +
+
+
+

For the extra curious

+

A visual codemap of every repo in the series.

+

+ Click a node. See the file, what it emits, and what it talks to. + Shared DNA across EP 02 and EP 03 โ€” sseHub, the AG-UI client, Zeebe workers โ€” + so you can see the pattern, not just the folders. +

+ +
+ +
+
+ +
+
+

Next up

+

EP 04 ยท model routing, and what it costs you to pick wrong.

+

+ The inner loop is cheap to start and expensive to run on the wrong model. + Routing is the next moat: not โ€œwhich model is smartest,โ€ but which one is allowed + to touch this case, at this temperature, for this cost per decision. +

+
+
+ +
+

Behind the series

+

Zishan Ali Khan

+

+ Enterprise AI GTM & Solutions at Camunda. Day job: putting agents inside real business processes. + This series is where each layer gets built end to end, then published for what holds up. +

+ +
+
+ +
+
+
Agents, Orchestrated ยท MIT ยท 2026 Zishan Ali Khan
+
+ Series hub + ยท Codemap + ยท Films +
+
+
+
+ + + From 128e3e8acf0a3cdad2819a753d0b4f49c97c8db5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 28 Aug 2026 13:50:05 +0000 Subject: [PATCH 2/5] Match the site chrome and target /agents-orchestrated Restyle the hub to sit next to the essays (same nav, serif, green accent, light/dark) instead of overlaying /series/agents-orchestrated. Point canonical URLs at zishanalikhan.com/agents-orchestrated, which is currently a 404. Co-authored-by: Zishan Ali Khan --- README.md | 10 +- docs/assets/codemap.js | 19 +- docs/assets/favicon.svg | 14 +- docs/assets/site.css | 656 +++++++++++++++------------------------- docs/assets/site.js | 24 +- docs/codemap.html | 226 +++++++------- docs/index.html | 484 ++++++++++++----------------- docs/vercel.json | 4 + vercel.json | 5 + 9 files changed, 585 insertions(+), 857 deletions(-) create mode 100644 docs/vercel.json create mode 100644 vercel.json diff --git a/README.md b/README.md index 72548a4..540ebe9 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,16 @@ toys: every build carries the parts that decide whether agents survive production โ€” verification, orchestration, human oversight, grounding, and an audit trail. -**[Series webpage](docs/index.html)** ยท **[Visual codemap](docs/codemap.html)** *(for the extra curious)* +**Visual hub:** [zishanalikhan.com/agents-orchestrated](https://zishanalikhan.com/agents-orchestrated/) +ยท **Codemap:** [โ€ฆ/codemap](https://zishanalikhan.com/agents-orchestrated/codemap/) +ยท **Essays (unchanged):** [โ€ฆ/series/agents-orchestrated](https://zishanalikhan.com/series/agents-orchestrated/) ๐Ÿ“บ Episodes land on [LinkedIn](https://www.linkedin.com/in/zishanalikhan). Every build is a public repo on this profile. -> After GitHub Pages is enabled on this repo (Settings โ†’ Pages โ†’ Deploy from -> branch `main` / folder `/docs`), the same pages live at -> `https://letmereviewyourcode.github.io/agents-orchestrated/`. +The static files live in [`docs/`](docs/). Map the Vercel app for this repo to +the hub URL above (root directory: `docs`). That path is currently a 404, so it +does not override the essay index. --- diff --git a/docs/assets/codemap.js b/docs/assets/codemap.js index b8acbe5..4e90461 100644 --- a/docs/assets/codemap.js +++ b/docs/assets/codemap.js @@ -251,7 +251,7 @@ function renderView(viewId, selectedId) { const defs = el("defs", {}, [ el("marker", { id: "arrow", viewBox: "0 0 10 10", refX: "8", refY: "5", markerWidth: "7", markerHeight: "7", orient: "auto-start-reverse" }, [ - el("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: "#3a3a40" }), + el("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: "currentColor" }), ]), ]); svg.appendChild(defs); @@ -276,21 +276,26 @@ function renderView(viewId, selectedId) { "data-id": node.id, "aria-label": node.label, }); + const kind = + node.fill === "#13261f" ? "is-surface" : + (node.fill === "#2a2114" || node.fill === "#2a140f" || node.fill === "#1f1c14") ? "is-check" : + "is-base"; + g.classList.add(kind); g.appendChild(el("rect", { - x: node.x, y: node.y, width: node.w, height: node.h, rx: "12", - fill: node.fill || "#161618", - stroke: selectedId === node.id ? "#ef9f27" : "#2a2a2e", + x: node.x, y: node.y, width: node.w, height: node.h, rx: "10", "stroke-width": selectedId === node.id ? "2" : "1", })); g.appendChild(el("text", { + class: "n-label", x: node.x + 16, y: node.y + (node.sub ? node.h / 2 - 4 : node.h / 2 + 5), - fill: "#f2f2f0", "font-size": "13", "font-weight": "600", + "font-size": "13", "font-weight": "600", "font-family": "Inter, system-ui, sans-serif", }, [node.label])); if (node.sub) { g.appendChild(el("text", { + class: "n-sub", x: node.x + 16, y: node.y + node.h / 2 + 14, - fill: "#9c9a92", "font-size": "11", + "font-size": "11", "font-family": "JetBrains Mono, ui-monospace, monospace", }, [node.sub])); } @@ -330,7 +335,7 @@ function inspect(node) {

${node.body}

${node.path ? `
${node.path}
` : ""} `; } diff --git a/docs/assets/favicon.svg b/docs/assets/favicon.svg index 6f891f4..f866c49 100644 --- a/docs/assets/favicon.svg +++ b/docs/assets/favicon.svg @@ -1,9 +1,9 @@ - - - - - - - + + + + + + + diff --git a/docs/assets/site.css b/docs/assets/site.css index 43c9808..0bb3e00 100644 --- a/docs/assets/site.css +++ b/docs/assets/site.css @@ -1,507 +1,325 @@ -/* Agents, Orchestrated โ€” series hub - Design tokens follow the EP 01 film: dark stage, amber /goal, teal for the rest. */ +/* Companion hub for Agents, Orchestrated. + Tokens match zishanalikhan.com so this sits next to the essays, not over them. */ -@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,600;1,6..72,400;1,6..72,600&display=swap"); :root { - --bg: #0b0b0c; - --bg-elev: #101012; - --surface: #161618; - --surface-2: #1c1c1f; - --border: #2a2a2e; - --border-strong: #3a3a40; - --text: #f2f2f0; - --muted: #9c9a92; - --faint: #6b6a64; - --amber: #ef9f27; - --amber-dim: rgba(239, 159, 39, 0.14); - --teal: #1d9e75; - --teal-dim: rgba(29, 158, 117, 0.14); - --blue: #5b8def; - --blue-dim: rgba(91, 141, 239, 0.14); - --gold: #c9a44a; - --red: #e05a5a; - --purple: #9b7fe0; - --shadow: 0 24px 80px rgba(0, 0, 0, 0.45); - --radius: 16px; - --font: "Inter", system-ui, sans-serif; - --mono: "JetBrains Mono", ui-monospace, monospace; - --max: 1120px; - --nav-h: 64px; + --font-sans: "Inter", system-ui, sans-serif; + --font-serif: "Newsreader", Georgia, serif; + --font-mono: "JetBrains Mono", ui-monospace, monospace; + --bg: #fcfcfa; + --bg-raised: #f6f6f2; + --bg-inset: #f1f1ec; + --ink: #16191d; + --ink-secondary: #40474f; + --ink-muted: #6e7681; + --border: #e4e4dd; + --border-strong: #d0d0c8; + --accent: #1a7f37; + --accent-strong: #116329; + --accent-soft: #eaf7ee; + --danger: #cf222e; + --amber: #9a6700; + --selection: #d2f0da; + --max: 48rem; + --max-wide: 72rem; +} + +html.dark { + --bg: #0b0f14; + --bg-raised: #11161d; + --bg-inset: #161d26; + --ink: #e7edf3; + --ink-secondary: #adb8c3; + --ink-muted: #7d8894; + --border: #1d2631; + --border-strong: #2b3644; + --accent: #3fb950; + --accent-strong: #56d364; + --accent-soft: #12261a; + --danger: #f85149; + --amber: #d29922; + --selection: #1c3a26; } *, *::before, *::after { box-sizing: border-box; } html { scroll-behavior: smooth; } html, body { margin: 0; padding: 0; } body { - font-family: var(--font); + font-family: var(--font-sans); background: var(--bg); - color: var(--text); - line-height: 1.55; + color: var(--ink); + line-height: 1.625; min-height: 100vh; + display: flex; + flex-direction: column; -webkit-font-smoothing: antialiased; } +::selection { background: var(--selection); } img { max-width: 100%; display: block; } a { color: inherit; text-decoration: none; } -button { font-family: inherit; } -code, .mono { font-family: var(--mono); } +button { font-family: inherit; background: none; border: 0; color: inherit; cursor: pointer; } +code, .mono { font-family: var(--font-mono); } .skip { position: absolute; left: -999px; top: 0; - background: var(--amber); color: #111; padding: 8px 12px; z-index: 100; + background: var(--accent); color: #fff; padding: 8px 12px; z-index: 100; } .skip:focus { left: 12px; top: 12px; } -/* โ”€โ”€ Grain + glow โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -body::before { - content: ""; - position: fixed; inset: 0; pointer-events: none; z-index: 0; - background-image: - radial-gradient(ellipse 80% 50% at 50% -10%, rgba(239, 159, 39, 0.08), transparent 55%), - radial-gradient(ellipse 40% 30% at 90% 20%, rgba(29, 158, 117, 0.05), transparent 50%); -} -body::after { - content: ""; - position: fixed; inset: 0; pointer-events: none; z-index: 1; - opacity: 0.035; - background-image: url("data:image/svg+xml;utf8,"); -} - -.wrap { position: relative; z-index: 2; } +.narrow { width: min(var(--max), calc(100% - 48px)); margin-left: auto; margin-right: auto; } +.wide { width: min(var(--max-wide), calc(100% - 48px)); margin-left: auto; margin-right: auto; } -/* โ”€โ”€ Nav โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.nav { - position: sticky; top: 0; z-index: 20; - height: var(--nav-h); - display: flex; align-items: center; justify-content: space-between; - padding: 0 28px; - background: rgba(11, 11, 12, 0.78); - backdrop-filter: blur(16px); - border-bottom: 1px solid var(--border); -} -.brand { - display: flex; align-items: center; gap: 10px; - font-weight: 600; letter-spacing: -0.02em; +/* โ”€โ”€ Nav (same chrome as zishanalikhan.com) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.site-header { + width: min(var(--max), calc(100% - 48px)); + margin: 0 auto; + padding: 32px 0 16px; } -.brand-mark { - width: 28px; height: 28px; +.site-nav { + display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; + gap: 8px 16px; } -.brand-kicker { - display: block; - font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; - color: var(--amber); font-weight: 600; -} -.brand-name { font-size: 14px; } -.nav-links { display: flex; gap: 6px; align-items: center; } -.nav-links a, .nav-links .ghost { - font-size: 13px; color: var(--muted); padding: 8px 12px; border-radius: 999px; - transition: color 0.15s, background 0.15s; -} -.nav-links a:hover, .nav-links a[aria-current="page"] { - color: var(--text); background: var(--surface); +.brand { + font-family: var(--font-mono); font-size: 13px; letter-spacing: -0.02em; +} +.brand .muted { color: var(--ink-muted); } +.brand .accent { color: var(--accent); } +.brand:hover { color: var(--accent); } +.nav-right { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; } +.nav-right a, .icon-btn { + padding: 4px 8px; font-size: 13px; border-radius: 6px; + color: var(--ink-muted); transition: color 0.15s, background 0.15s; +} +.nav-right a:hover, .icon-btn:hover { color: var(--ink); } +.nav-right a.here { color: var(--ink); font-weight: 500; } +.icon-btn { display: inline-flex; align-items: center; padding: 6px; } +html:not(.dark) .icon-sun { display: none; } +html.dark .icon-moon { display: none; } + +/* โ”€โ”€ Type โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.kicker { + font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; + text-transform: uppercase; color: var(--ink-muted); margin-bottom: 12px; +} +h1, h2, h3 { letter-spacing: -0.025em; line-height: 1.15; margin: 0; } +h1 { font-family: var(--font-serif); font-size: clamp(2rem, 4vw, 2.25rem); font-weight: 600; } +h2 { font-family: var(--font-serif); font-size: 1.35rem; font-weight: 600; } +h3 { font-size: 1rem; font-weight: 600; } +.lede { + margin-top: 16px; max-width: 36rem; + font-size: 15px; color: var(--ink-secondary); line-height: 1.625; } -.nav-cta { - background: var(--amber) !important; - color: #14110a !important; - font-weight: 600 !important; - padding: 8px 14px !important; +.pullquote { + margin: 24px 0 0; max-width: 36rem; padding: 16px; + border: 1px solid var(--border); border-left: 2px solid var(--accent); + border-radius: 6px; background: var(--bg-raised); } -.nav-cta:hover { filter: brightness(1.05); } -.nav-toggle { - display: none; background: none; border: 1px solid var(--border); - color: var(--text); width: 40px; height: 36px; border-radius: 8px; cursor: pointer; +.pullquote p { + margin: 0; font-family: var(--font-serif); font-style: italic; + font-size: 16px; color: var(--ink-secondary); } -/* โ”€โ”€ Layout โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.container { width: min(var(--max), calc(100% - 48px)); margin: 0 auto; } -.section { padding: 88px 0; } -.section-sm { padding: 56px 0; } -.eyebrow { - font-size: 11px; font-weight: 600; letter-spacing: 0.22em; - text-transform: uppercase; color: var(--amber); margin-bottom: 14px; -} -.eyebrow.teal { color: var(--teal); } -.eyebrow.blue { color: var(--blue); } -h1, h2, h3 { letter-spacing: -0.03em; line-height: 1.12; margin: 0 0 16px; } -h1 { font-size: clamp(40px, 6.4vw, 72px); font-weight: 800; } -h2 { font-size: clamp(28px, 3.4vw, 40px); font-weight: 700; } -h3 { font-size: 20px; font-weight: 600; } -.lede { - font-size: clamp(17px, 2vw, 20px); - color: var(--muted); max-width: 640px; line-height: 1.55; +/* โ”€โ”€ Hero / sections โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.hero { padding: 64px 0 40px; } +.section { padding: 8px 0 48px; } +.section-head { margin-bottom: 20px; } +.section-head p { margin: 8px 0 0; font-size: 14px; color: var(--ink-muted); } + +.note { + margin-top: 20px; font-size: 13.5px; color: var(--ink-secondary); + padding: 12px 14px; border: 1px dashed var(--border); border-radius: 8px; + background: var(--bg-raised); } -.accent { color: var(--amber); } +.note a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; } /* โ”€โ”€ Buttons โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.btn-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; } +.btn-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; } .btn { - display: inline-flex; align-items: center; gap: 8px; - padding: 11px 18px; border-radius: 999px; font-size: 14px; font-weight: 600; - border: 1px solid transparent; transition: transform 0.15s, background 0.15s, border-color 0.15s; -} -.btn:hover { transform: translateY(-1px); } -.btn-primary { background: var(--amber); color: #14110a; } -.btn-ghost { background: var(--surface); border-color: var(--border); color: var(--text); } -.btn-ghost:hover { border-color: var(--border-strong); } -.btn-link { color: var(--muted); padding-left: 8px; } -.btn-link:hover { color: var(--text); } - -/* โ”€โ”€ Hero โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.hero { - padding: 72px 0 40px; - text-align: center; -} -.hero .lede { margin: 0 auto; } -.hero h1 { margin-bottom: 20px; } -.hero-quote { - margin: 28px auto 0; - max-width: 520px; - font-size: 15px; color: var(--faint); - font-style: italic; -} -.stats { - display: grid; grid-template-columns: repeat(4, 1fr); - gap: 12px; margin-top: 56px; -} -.stat { - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 20px 18px; - text-align: left; -} -.stat b { - display: block; font-size: 22px; font-weight: 700; letter-spacing: -0.03em; - margin-bottom: 4px; -} -.stat span { font-size: 13px; color: var(--muted); } + display: inline-flex; align-items: center; gap: 6px; + padding: 7px 12px; border-radius: 6px; font-size: 13px; font-weight: 500; + border: 1px solid var(--border); background: var(--bg-raised); + transition: border-color 0.15s, color 0.15s; +} +.btn:hover { border-color: var(--border-strong); color: var(--accent); } +.btn-accent { + background: var(--accent); color: #fff; border-color: transparent; +} +html.dark .btn-accent { color: #0b0f14; } +.btn-accent:hover { color: #fff; filter: brightness(1.05); } +html.dark .btn-accent:hover { color: #0b0f14; } -/* โ”€โ”€ Moat strip โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.moats { - display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; -} -.moat { - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 22px 20px; - min-height: 180px; -} -.moat .n { - font-family: var(--mono); font-size: 11px; color: var(--amber); - letter-spacing: 0.12em; margin-bottom: 12px; -} -.moat h3 { font-size: 16px; margin-bottom: 8px; } -.moat p { margin: 0; font-size: 13.5px; color: var(--muted); } +/* โ”€โ”€ Episode list (matches series index cards) โ”€โ”€โ”€โ”€โ”€โ”€ */ +.card-list { display: flex; flex-direction: column; gap: 12px; } +.card { + display: block; padding: 20px; + border: 1px solid var(--border); border-radius: 8px; + background: var(--bg-raised); transition: border-color 0.15s; +} +a.card:hover { border-color: var(--border-strong); } +a.card:hover h2 { color: var(--accent); } +.card.queued { + border-style: dashed; opacity: 0.7; +} +.card-meta { + display: flex; align-items: center; gap: 12px; margin-bottom: 8px; + font-family: var(--font-mono); font-size: 11px; color: var(--ink-muted); +} +.card-meta .ok { color: var(--accent); } +.card-meta .wip { color: var(--amber); } +.card-meta .when { margin-left: auto; } +.card h2 { font-size: 20px; } +.card .line { + margin: 6px 0 0; font-family: var(--font-serif); font-style: italic; + font-size: 13.5px; color: var(--ink-muted); +} +.card p.body { margin: 10px 0 0; font-size: 13.5px; color: var(--ink-secondary); max-width: 42rem; } +.card-links { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 14px; font-size: 13px; } +.card-links a { color: var(--ink-muted); } +.card-links a:hover { color: var(--accent); } -/* โ”€โ”€ Episode cards โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.episode { - display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 40px; - align-items: center; - padding: 36px; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 24px; - margin-bottom: 20px; - box-shadow: var(--shadow); -} -.episode.reverse { grid-template-columns: 0.95fr 1.05fr; } -.episode.reverse .episode-copy { order: 2; } -.episode-meta { - display: flex; gap: 10px; align-items: center; margin-bottom: 12px; - font-family: var(--mono); font-size: 12px; color: var(--muted); -} -.ep-num { color: var(--amber); font-weight: 600; } -.ep-moat { - display: inline-block; - padding: 3px 8px; border-radius: 999px; - background: var(--amber-dim); color: var(--amber); - font-size: 11px; font-weight: 600; letter-spacing: 0.04em; -} -.ep-moat.teal { background: var(--teal-dim); color: #3ecf9a; } -.ep-moat.gold { background: rgba(201, 164, 74, 0.16); color: var(--gold); } -.episode h2 { font-size: clamp(26px, 3vw, 34px); } -.episode blockquote { - margin: 16px 0; - padding-left: 14px; - border-left: 2px solid var(--amber); - color: var(--text); - font-size: 16px; font-style: italic; -} -.episode p { color: var(--muted); font-size: 15px; margin: 0 0 8px; } +.shot-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 14px; } .shot { - position: relative; - border-radius: 14px; - overflow: hidden; - border: 1px solid var(--border); - background: #0a0a0b; - aspect-ratio: 16 / 10; + position: relative; border-radius: 8px; overflow: hidden; + border: 1px solid var(--border); background: var(--bg-inset); aspect-ratio: 16/10; } .shot img { width: 100%; height: 100%; object-fit: cover; } -.shot-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; } -.shot-pair .shot { aspect-ratio: 4 / 3; } -.shot figcaption { - position: absolute; left: 10px; bottom: 10px; - font-size: 11px; font-family: var(--mono); color: #fff; - background: rgba(0,0,0,0.55); padding: 3px 8px; border-radius: 6px; +.shot span { + position: absolute; left: 8px; bottom: 8px; + font-family: var(--font-mono); font-size: 10px; color: #fff; + background: rgba(0,0,0,0.5); padding: 2px 7px; border-radius: 4px; } -/* โ”€โ”€ Stack โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.stack { - display: flex; flex-direction: column; gap: 8px; - max-width: 820px; margin: 32px auto 0; -} +/* โ”€โ”€ Stack / protocols โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.stack { display: flex; flex-direction: column; gap: 8px; } .stack-row { - display: grid; grid-template-columns: 140px 1fr auto; - gap: 16px; align-items: center; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 12px; - padding: 14px 18px; + display: grid; grid-template-columns: 92px 1fr auto; gap: 12px; align-items: start; + padding: 14px 16px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-raised); } .stack-row .layer { - font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; - text-transform: uppercase; color: var(--faint); + font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.08em; + text-transform: uppercase; color: var(--ink-muted); padding-top: 3px; } -.stack-row strong { font-size: 15px; } -.stack-row span { color: var(--muted); font-size: 13px; } +.stack-row strong { font-size: 14px; } +.stack-row span { display: block; font-size: 13px; color: var(--ink-muted); margin-top: 2px; } .pill { - font-family: var(--mono); font-size: 11px; - padding: 4px 8px; border-radius: 999px; border: 1px solid var(--border); - color: var(--muted); white-space: nowrap; -} -.pill.ep01 { color: var(--amber); border-color: rgba(239,159,39,0.35); } -.pill.ep02 { color: #3ecf9a; border-color: rgba(29,158,117,0.4); } -.pill.ep03 { color: var(--gold); border-color: rgba(201,164,74,0.4); } -.pill.next { color: var(--blue); border-color: rgba(91,141,239,0.4); } - -/* โ”€โ”€ Protocol row โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.protocols { - display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 28px; -} -.proto { - background: var(--bg-elev); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 20px; + font-family: var(--font-mono); font-size: 10.5px; white-space: nowrap; + padding: 3px 7px; border-radius: 4px; border: 1px solid var(--border); color: var(--ink-muted); } -.proto .name { - font-family: var(--mono); font-size: 13px; font-weight: 600; - color: var(--text); margin-bottom: 6px; -} -.proto p { margin: 0; font-size: 13px; color: var(--muted); } -.proto .owns { font-size: 11px; color: var(--amber); margin-top: 12px; font-family: var(--mono); } - -/* โ”€โ”€ Curious CTA โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.curious { - display: grid; grid-template-columns: 1.2fr 0.8fr; gap: 32px; align-items: center; - background: - radial-gradient(ellipse at 100% 0%, rgba(29,158,117,0.12), transparent 50%), - var(--surface); - border: 1px solid var(--border); - border-radius: 24px; - padding: 40px; -} -.curious-visual { - border: 1px dashed var(--border-strong); - border-radius: 14px; - padding: 18px; - font-family: var(--mono); font-size: 11px; color: var(--muted); - background: #0d0d0f; - line-height: 1.7; -} -.curious-visual .hl { color: var(--amber); } -.curious-visual .ok { color: var(--teal); } - -/* โ”€โ”€ Next โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.next-card { - background: var(--surface); - border: 1px dashed var(--border-strong); - border-radius: 24px; - padding: 36px; -} -.next-card p { color: var(--muted); max-width: 640px; } -/* โ”€โ”€ Footer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -footer { - border-top: 1px solid var(--border); - padding: 36px 0 48px; - color: var(--muted); font-size: 13px; -} -.foot { - display: flex; justify-content: space-between; gap: 24px; flex-wrap: wrap; +.protocols { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 16px; } +.proto { + padding: 16px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-raised); } -.foot a:hover { color: var(--text); } +.proto .name { font-family: var(--font-mono); font-size: 12px; font-weight: 500; } +.proto p { margin: 6px 0 0; font-size: 13px; color: var(--ink-muted); } -/* โ”€โ”€ Codemap chrome โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.cmap-hero { padding: 48px 0 12px; } -.cmap-hero .lede { max-width: 680px; } +/* โ”€โ”€ Codemap โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.cmap-hero { padding: 48px 0 8px; } .view-tabs { - display: flex; flex-wrap: wrap; gap: 6px; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 12px; - padding: 5px; - margin: 28px 0 20px; + display: flex; flex-wrap: wrap; gap: 4px; + padding: 4px; margin: 24px 0 16px; + border: 1px solid var(--border); border-radius: 8px; background: var(--bg-raised); width: fit-content; } .view-tabs button { - background: transparent; border: 0; color: var(--muted); - padding: 8px 14px; border-radius: 8px; cursor: pointer; - font-size: 13px; font-weight: 500; + padding: 6px 12px; border-radius: 6px; font-size: 13px; color: var(--ink-muted); } .view-tabs button[aria-selected="true"] { - background: var(--bg); color: var(--text); box-shadow: 0 1px 0 var(--border); + background: var(--bg); color: var(--ink); font-weight: 500; + box-shadow: 0 0 0 1px var(--border); } -.view-tabs button:hover:not([aria-selected="true"]) { color: var(--text); } - .cmap-shell { - display: grid; - grid-template-columns: 1fr 340px; - gap: 16px; - align-items: start; - min-height: 640px; + display: grid; grid-template-columns: 1fr 300px; gap: 12px; align-items: start; } .cmap-stage { - background: var(--surface); - border: 1px solid var(--border); - border-radius: 20px; - min-height: 640px; - overflow: hidden; - position: relative; + min-height: 560px; border: 1px solid var(--border); border-radius: 12px; + background: var(--bg-raised); overflow: hidden; } .cmap-stage svg { display: block; width: 100%; height: auto; } .inspector { - background: var(--surface); - border: 1px solid var(--border); - border-radius: 20px; - padding: 22px; - position: sticky; top: calc(var(--nav-h) + 16px); - min-height: 280px; -} -.inspector .kicker { - font-family: var(--mono); font-size: 11px; color: var(--amber); - letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 8px; -} -.inspector h3 { font-size: 18px; margin-bottom: 8px; } -.inspector p { color: var(--muted); font-size: 14px; margin: 0 0 12px; } + position: sticky; top: 16px; + padding: 18px; border: 1px solid var(--border); border-radius: 12px; background: var(--bg-raised); +} +.inspector .kicker { margin-bottom: 6px; } +.inspector h3 { font-family: var(--font-serif); font-size: 18px; margin-bottom: 8px; } +.inspector p { margin: 0 0 10px; font-size: 13.5px; color: var(--ink-secondary); } .inspector .path { - font-family: var(--mono); font-size: 11.5px; color: var(--faint); + font-family: var(--font-mono); font-size: 11px; color: var(--ink-muted); background: var(--bg); border: 1px solid var(--border); - border-radius: 8px; padding: 8px 10px; margin: 12px 0; word-break: break-all; + border-radius: 6px; padding: 8px 10px; margin: 10px 0; word-break: break-all; } -.inspector .links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } +.inspector .links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; } .node-chip { - font-family: var(--mono); font-size: 11px; - padding: 4px 8px; border-radius: 6px; - background: var(--bg); border: 1px solid var(--border); color: var(--muted); + font-family: var(--font-mono); font-size: 11px; padding: 3px 7px; + border-radius: 4px; border: 1px solid var(--border); color: var(--ink-muted); } - -/* SVG node styles */ .cmap-node { cursor: pointer; } -.cmap-node:focus { outline: none; } -.cmap-node rect, .cmap-node circle { - transition: fill 0.15s, stroke 0.15s, filter 0.15s; -} -.cmap-node:hover rect, .cmap-node.is-active rect { - filter: brightness(1.15); -} -.cmap-node.is-active rect { - stroke: var(--amber); - stroke-width: 2; -} -.edge { fill: none; stroke: #3a3a40; stroke-width: 1.25; } -.edge.hi { stroke: var(--amber); stroke-width: 1.8; } +.cmap-node rect { fill: var(--bg); stroke: var(--border); } +.cmap-node.is-surface rect { fill: var(--accent-soft); } +.cmap-node.is-check rect { fill: var(--accent-soft); stroke: var(--accent); } +.cmap-node.is-active rect { stroke: var(--accent); stroke-width: 2; } +.cmap-node .n-label { fill: var(--ink); } +.cmap-node .n-sub { fill: var(--ink-muted); } +.edge { fill: none; stroke: var(--border-strong); stroke-width: 1.25; } +.edge.hi { stroke: var(--accent); stroke-width: 1.8; } +.legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 12px; color: var(--ink-muted); margin: 0 0 16px; } +.legend i { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; } -.legend { - display: flex; flex-wrap: wrap; gap: 14px; - font-size: 12px; color: var(--muted); margin: 12px 0 32px; -} -.legend i { - display: inline-block; width: 10px; height: 10px; border-radius: 3px; - margin-right: 6px; vertical-align: -1px; -} +.vocab { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } +.vocab-item { padding: 14px 16px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-raised); } +.vocab-item code { color: var(--accent); font-size: 12px; } +.vocab-item p { margin: 6px 0 0; font-size: 13px; color: var(--ink-muted); } -.vocab { - display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; -} -.vocab-item { - background: var(--surface); border: 1px solid var(--border); - border-radius: 12px; padding: 14px 16px; -} -.vocab-item code { color: var(--amber); font-size: 12px; } -.vocab-item p { margin: 6px 0 0; font-size: 13px; color: var(--muted); } - -.shared-grid { - display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 16px; -} -.shared-card { - background: var(--surface); border: 1px solid var(--border); - border-radius: 14px; padding: 18px; -} -.shared-card h3 { font-size: 15px; } -.shared-card p { margin: 0; font-size: 13px; color: var(--muted); } +.shared-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } +.shared-card { padding: 16px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-raised); } +.shared-card h3 { font-size: 15px; margin-bottom: 6px; } +.shared-card p { margin: 0; font-size: 13px; color: var(--ink-muted); } -/* โ”€โ”€ Loop 2ร—2 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ .axes { - position: relative; - background: #101012; - border: 1px solid var(--border); - border-radius: 20px; - padding: 48px 28px 36px 56px; - min-height: 380px; -} -.axes-grid { - display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; - gap: 10px; height: 300px; -} -.quad { - border: 1px dashed var(--border); - border-radius: 12px; padding: 14px; - background: rgba(255,255,255,0.015); -} -.quad.hot { border-color: rgba(239,159,39,0.45); background: var(--amber-dim); } + position: relative; background: var(--bg-raised); + border: 1px solid var(--border); border-radius: 12px; + padding: 40px 20px 32px 48px; min-height: 340px; +} +.axes-grid { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 8px; height: 260px; } +.quad { border: 1px dashed var(--border); border-radius: 8px; padding: 12px; } +.quad.hot { border-color: var(--accent); background: var(--accent-soft); } .dot { - display: inline-flex; align-items: center; gap: 6px; - font-size: 12px; margin: 4px 8px 4px 0; - padding: 4px 8px; border-radius: 999px; background: var(--surface-2); - border: 1px solid var(--border); + display: inline-flex; margin: 4px 6px 0 0; padding: 3px 8px; + font-size: 12px; border-radius: 999px; border: 1px solid var(--border); background: var(--bg); } .axis-x, .axis-y { - position: absolute; font-family: var(--mono); font-size: 10px; - letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint); + position: absolute; font-family: var(--font-mono); font-size: 10px; + letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-muted); } -.axis-x { bottom: 10px; left: 56px; right: 28px; display: flex; justify-content: space-between; } +.axis-x { bottom: 8px; left: 48px; right: 20px; display: flex; justify-content: space-between; } .axis-y { - top: 48px; bottom: 36px; left: 10px; writing-mode: vertical-rl; + top: 40px; bottom: 32px; left: 8px; writing-mode: vertical-rl; transform: rotate(180deg); display: flex; justify-content: space-between; } -/* โ”€โ”€ Responsive โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +/* โ”€โ”€ Footer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +.site-footer { + width: min(var(--max), calc(100% - 48px)); + margin: 80px auto 0; padding: 48px 0 56px; + border-top: 1px solid var(--border); +} +.foot-row { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 16px; } +.site-footer p, .site-footer a { font-size: 13px; color: var(--ink-muted); } +.site-footer a:hover { color: var(--ink); } +.foot-links { display: flex; flex-wrap: wrap; gap: 16px; } +.fine { margin-top: 16px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); opacity: 0.7; } + @media (max-width: 900px) { - .nav-toggle { display: inline-flex; align-items: center; justify-content: center; } - .nav-links { - display: none; position: absolute; top: var(--nav-h); left: 0; right: 0; - flex-direction: column; background: var(--bg-elev); - border-bottom: 1px solid var(--border); padding: 12px; - } - .nav-links.open { display: flex; } - .stats, .moats, .protocols { grid-template-columns: 1fr 1fr; } - .episode, .episode.reverse, .curious, .cmap-shell { grid-template-columns: 1fr; } - .episode.reverse .episode-copy { order: 0; } - .stack-row { grid-template-columns: 1fr; gap: 6px; } + .cmap-shell, .shot-row, .vocab, .shared-grid, .protocols { grid-template-columns: 1fr; } + .stack-row { grid-template-columns: 1fr; gap: 4px; } .inspector { position: static; } - .vocab, .shared-grid { grid-template-columns: 1fr; } } @media (max-width: 560px) { - .container { width: min(var(--max), calc(100% - 28px)); } - .stats, .moats, .protocols { grid-template-columns: 1fr; } - .hero { padding-top: 40px; } - .section { padding: 56px 0; } - .episode, .curious, .next-card { padding: 22px; } + .narrow, .wide, .site-header, .site-footer { width: min(var(--max), calc(100% - 32px)); } + .hero { padding-top: 36px; } } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } - .btn:hover { transform: none; } } diff --git a/docs/assets/site.js b/docs/assets/site.js index f061725..76f51fe 100644 --- a/docs/assets/site.js +++ b/docs/assets/site.js @@ -1,16 +1,12 @@ (function () { - const toggle = document.querySelector(".nav-toggle"); - const links = document.querySelector(".nav-links"); - if (toggle && links) { - toggle.addEventListener("click", () => { - const open = links.classList.toggle("open"); - toggle.setAttribute("aria-expanded", String(open)); - }); - links.querySelectorAll("a").forEach((a) => { - a.addEventListener("click", () => { - links.classList.remove("open"); - toggle.setAttribute("aria-expanded", "false"); - }); - }); - } + const root = document.documentElement; + const stored = localStorage.getItem("theme"); + if (stored === "light") root.classList.remove("dark"); + else if (stored === "dark") root.classList.add("dark"); + else if (!root.classList.contains("dark")) root.classList.add("dark"); + + document.getElementById("theme-toggle")?.addEventListener("click", () => { + const isDark = root.classList.toggle("dark"); + localStorage.setItem("theme", isDark ? "dark" : "light"); + }); })(); diff --git a/docs/codemap.html b/docs/codemap.html index b34867f..2c16ce6 100644 --- a/docs/codemap.html +++ b/docs/codemap.html @@ -1,43 +1,48 @@ - + - Codemap โ€” Agents, Orchestrated - + Codemap ยท Agents, Orchestrated + + + -
- -
-
-

For the extra curious

-

Visual codemap

-

- The three public repos as one picture. Nodes are files, protocols, or demo paths. - Edges are runtime flow โ€” events, jobs, signatures โ€” not folder nesting. -

-
+
+
+
For the extra curious
+

Visual codemap

+

The three public repos as one picture. Nodes are files, protocols, or demo paths. Edges are runtime flow โ€” events, jobs, signatures โ€” not folder nesting.

+
+
@@ -45,110 +50,97 @@

Visual codemap

- -

The series as one system

-

+

The series as one system

+

- amber = the check / the signature - teal = a human surface - graphite = process, worker, spec + green = the check / the signature / a human surface + raised = process, worker, spec
-
+
-
-

EP 01 ยท the map inside the film

+
+

Two questions define any loop.

-

- Continuation on the x-axis, locus on the y-axis. The interesting differences all sit on the right: - how โ€œdoneโ€ is decided. -

-
-
cloud-persistentin-session
-
-
- trigger ยท cloud
- Claude Routines - Antigravity /schedule - Cursor Automations -
-
- verification ยท cloud
- Codex Goal mode - Antigravity /goal -
-
- trigger ยท session
- Claude /loop -
-
- verification ยท session
- Claude Code /goal -
+

Continuation on the x-axis, locus on the y-axis. The interesting differences sit on the right: how โ€œdoneโ€ is decided.

+
+
+
cloud-persistentin-session
+
+
+ trigger ยท cloud
+ Claude Routines + Antigravity /schedule + Cursor Automations +
+
+ verification ยท cloud
+ Codex Goal mode + Antigravity /goal +
+
+ trigger ยท session
+ Claude /loop +
+
+ verification ยท session
+ Claude Code /goal
-
gated by a triggergated by verification
-
+
gated by a triggergated by verification
+
+ -
-

The wire ยท EP 02โ€“03

+
+

Event vocabulary

-

- Everything the UI does is a typed event on one SSE stream. Same names in AURUM and in AP2. -

-
-
TEXT_MESSAGE_*

Streaming concierge copy. Start / content delta / end.

-
TOOL_CALL_*

An agent step with args and results. OperatorView lives on these.

-
GENERATIVE_UI

A real component: component + props. The donut, the gauge, the mandate card.

-
USER_TASK_PENDING

A Camunda user task, surfaced as a card. Completing the card completes the job.

-
STEP_STARTED / FINISHED

Process progress. The operatorโ€™s raw log and the customerโ€™s stepper share this.

-
PARTY_HOP

EP 03 only. Lights the four-party AP2 strip: shopping agent โ†’ merchant โ†’ credentials โ†’ processor.

-
-
- -
-

How to read a PR in these repos

-

Start at the wire, not the model.

-
-
-

1. The event

-

If a card appeared, an event was emitted. Find emit( in the worker. That is the contract.

-
-
-

2. The job

-

The worker is a Zeebe job. The BPMN task type is the function name. Operate will show the same sequence.

-
-
-

3. The fork

-

If a human appeared, a DMN table or a user task put them there. The agent did not โ€œdecide to escalate.โ€

-
-
-

4. The proof

-

EP 01: frame stills. EP 02: operator log. EP 03: merchant signature check. Pick the proof before you pick the model.

-
-
-
+

Everything the UI does is a typed event on one SSE stream.

+ +
+
TEXT_MESSAGE_*

Streaming concierge copy. Start / content delta / end.

+
TOOL_CALL_*

An agent step with args and results. OperatorView lives on these.

+
GENERATIVE_UI

A real component: component + props.

+
USER_TASK_PENDING

A Camunda user task, surfaced as a card.

+
STEP_STARTED / FINISHED

Process progress on customer stepper and operator log.

+
PARTY_HOP

EP 03. Lights the four-party AP2 strip.

+
+
-
- -
- +
+
+

How to read a PR in these repos

+

Start at the wire, not the model.

+
+
+

1. The event

If a card appeared, an event was emitted. Find emit( in the worker.

+

2. The job

The worker is a Zeebe job. Operate shows the same sequence.

+

3. The fork

If a human appeared, a DMN table or a user task put them there.

+

4. The proof

EP 01: frame stills. EP 02: operator log. EP 03: merchant signature check.

+
+ +
+ -
-
-
Agents, Orchestrated ยท MIT ยท 2026 Zishan Ali Khan
-
Series ยท Hub
+
+
+

ยฉ 2026 Zishan Ali Khan ยท Toronto / New York

+ -
-
+ +

Views are my own. Built in the open, reviewed line by line.

+
diff --git a/docs/index.html b/docs/index.html index fb5c1a6..7021c6d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,334 +1,240 @@ - + - Agents, Orchestrated โ€” real builds on the agentic stack - - + Agents, Orchestrated ยท letmereviewyourcode + + + + + -
- -
-

What actually ships

-

Four moats. Autonomy is not one of them.

-

Every lab can loop now. The product is how you trust what it did.

-
-
-
EP 01
-

Verification

-

Generate โ†’ run โ†’ check โ†’ repeat. โ€œDoneโ€ is evidence, not vibes. The loop you can audit is the only loop that ships.

-
-
-
EP 02
-

HITL + orchestration

-

The process pauses for a human, visibly, in Camunda Operate. The LLM is the language layer. The process is the orchestrator.

-
-
-
EP 02
-

Grounding

-

What the agent canโ€™t verify, it doesnโ€™t invent. Knowledge-base retrieval is a process step, not a prompt prefix.

-
-
-
EP 03
-

Cryptographic authority

-

You sign the rules. The agent signs the cart. The merchant verifies both. Tampering a number inside a signed token is a refusal, not a warning.

-
-
-
+
+
+
Series hub
+

Agents, Orchestrated

+

One layer of the agentic stack per episode. Built for real, reviewed in public. This page is the visual companion: films, repos, and a codemap. The essays stay where they are.

+
+

โ€œAutonomy is table stakes. The moat is everything around the agent.โ€

+
+

+ Essays: zishanalikhan.com/series/agents-orchestrated. + This hub does not replace that index. +

+ +
-
-

The series

+
+

Episodes

-

Films land on LinkedIn. Every build is a public repo.

- -
-
-
- EP 01 ยท Loops - verification is the moat -
-

He stopped writing code. He writes loops.

-
Every agentic coding tool converged on the same shape: goal in, iterate, verify each pass.
-

- Claude Code, Codex, and Antigravity all shipped a /goal-style command, independently, within months. - Autonomy stopped being the differentiator. How each loop decides itโ€™s done โ€” independent judge, tests-until-green, - auditable artifacts โ€” is the product. -

-

- The film was itself a loop: one /goal pointed at a spec, then a four-layer verification pass - (compile โ†’ frame stills โ†’ locked copy โ†’ full render) before calling the job done. -

- -
-
+

Three shipped. Three queued. Watch, read, or open the code.

+
+
+
+
+ EP 01 + โœ“ shipped + Jun 2026 +
+

Loops: the agentic primitive everything converged on

+

โ€œThe loop you can audit is the only one that ships.โ€

+

Claude Code, Codex, and Antigravity all shipped a /goal-style command. Autonomy stopped being the differentiator. How each loop decides itโ€™s done is the product. The film was itself a loop: one /goal pointed at a spec, then a four-layer verification pass.

+
- Two-by-two map of agentic coding loops: trigger vs verification, in-session vs cloud-persistent -
the 2ร—2 โ€” verification sits on the right
+ Two-by-two map of agentic coding loops + the 2ร—2
+
-
-
-
- EP 02 ยท AG-UI - HITL + orchestration + grounding -
-

Agents can stream the whole interface, not just text.

-
AURUM: a private bank onboarding clients on Camunda 8.
-

- Every agent decision streams to the browser as live, clickable UI over the open AG-UI protocol. - When the money gets serious, the process pauses for a human โ€” visibly, in Camunda Operate. - MCP gives agents tools. A2A talks to other agents. AG-UI is the last mile: where work becomes something a human can click. -

-

- Fully runnable. Without API keys it still runs โ€” shadow mode replays a deterministic scenario so you can watch the stream before wiring a cluster. -

- -
-
+
+
+ EP 02 + โœ“ shipped + Jul 2026 +
+

AG-UI: the agent that streams an interface, not a transcript

+

โ€œOne process. Three models. Zero glue code.โ€

+

AURUM: a private bank onboarding clients on Camunda 8. Every agent decision streams as live UI over AG-UI. When the money gets serious, the process pauses for a human in Operate. Runs without API keys in shadow mode.

+
- AURUM customer view with generative UI cards -
customer
+ AURUM customer view + customer
- AURUM advisor approval desk -
advisor desk
+ AURUM advisor desk + advisor desk
+
-
-
-
- EP 03 ยท AP2 - cryptographic authority + governance -
-

An agent with a signed spending limit. Then three times it refuses to spend.

-
How do you let an AI buy things for you safely?
-

- AP2 โ€” the Agent Payments Protocol, started by Google and handed to the FIDO Alliance โ€” answers with signed mandates. - You sign the rules. The agent signs the cart. The merchant verifies both before taking a cent. -

-

- A $7,000 ceiling. World Cup tickets while you sleep. Tampering the signed limit is refused by cryptography; - getting close to the limit is escalated by a Camunda 8 decision table. Four paths: golden, tamper, HITL, breach. -

- -
-
+
+
+ EP 03 + โœ“ shipped + Jul 2026 +
+

Agent payments: mandates, not trust

+

โ€œAgents that can pay are easy. Agents you can audit paying are the product.โ€

+

AP2 signed mandates. A $7,000 ceiling. Tampering the limit is refused by cryptography; getting close is escalated by a Camunda DMN table. Four paths: golden, tamper, HITL, breach.

+
- AP2 shopping agent customer view -
customer
+ AP2 customer view + customer
- TicketVault merchant site verifying the mandate chain -
TicketVault merchant
+ TicketVault merchant + TicketVault
+
-
-
-

How the layers compose

-

The process is the orchestrator. The LLM is the language layer.

-

- Each episode adds a layer around the agent. Read top-down: humans click, AG-UI carries the wire, - the loop runs, Camunda holds the token, policy and cryptography decide whether money moves. -

-
-
-
Humans
-
Customer ยท advisor ยท operator ยท merchant
Three surfaces, one stream. The operator view is the โ€œdonโ€™t take my word for itโ€ log.
- EP 02โ€“03 -
-
-
Wire
-
AG-UI over SSE
TEXT_MESSAGE, TOOL_CALL, GENERATIVE_UI, USER_TASK โ€” typed events, not a chat dump.
- EP 02โ€“03 -
-
-
Loop
-
Generate โ†’ run โ†’ check โ†’ repeat
Done is a four-layer check, or a DMN table, or a merchant signature โ€” never the worker grading itself.
- EP 01 -
-
-
Control
-
Camunda 8 ยท BPMN + user tasks
The AI Agent Connector hosts the tool loop inside the process. Paused tokens are the product.
- EP 02โ€“03 -
-
-
Grounding
-
Knowledge base as a process step
Retrieve, then answer. What it canโ€™t verify, it doesnโ€™t invent.
- EP 02 -
-
-
Authority
-
AP2 mandates ยท ES256 JWS
Open mandate (you sign the rules) โ†’ closed mandate (agent signs the cart) โ†’ merchant verifies the chain.
- EP 03 -
-
-
Policy
-
DMN decision tables
Wealth routing. Spending policy. First-hit rules linked to the process instance, visible in Operate.
- EP 02โ€“03 -
+
+
EP 04โ—‹ queued
+

Multi-LLM Polyglot

+

One process, many models. Routing decided by the orchestrator, priced per case.

- -
-
-
MCP
-

Connects agents to tools and context.

-
owns the toolbox
-
-
-
A2A
-

Connects agents to other agents.

-
owns the conversation
-
-
-
AG-UI
-

Connects agents to users โ€” the last mile.

-
owns the click ยท EP 02
-
-
-
AP2
-

Connects agents to money, with a signature.

-
owns the spend ยท EP 03
-
+
+
EP 05โ—‹ queued
+

Agent-to-Agent

+

Agents delegating to agents. Where orchestration beats a swarm, and where it does not.

-
- -
-
-
-

For the extra curious

-

A visual codemap of every repo in the series.

-

- Click a node. See the file, what it emits, and what it talks to. - Shared DNA across EP 02 and EP 03 โ€” sseHub, the AG-UI client, Zeebe workers โ€” - so you can see the pattern, not just the folders. -

- -
- +
+
EP 06โ—‹ queued
+

Verification, Deployed

+

Agents have a shape now. Making the verification layer a deployable artifact.

-
+
+ -
-
-

Next up

-

EP 04 ยท model routing, and what it costs you to pick wrong.

-

- The inner loop is cheap to start and expensive to run on the wrong model. - Routing is the next moat: not โ€œwhich model is smartest,โ€ but which one is allowed - to touch this case, at this temperature, for this cost per decision. -

+
+
+

How the layers compose

+

The process is the orchestrator. The LLM is the language layer.

+
+
+
+
Humans
+
Customer ยท advisor ยท operator ยท merchantThree surfaces, one stream. Operator view is the raw wire.
+ EP 02โ€“03
-
- -
-

Behind the series

-

Zishan Ali Khan

-

- Enterprise AI GTM & Solutions at Camunda. Day job: putting agents inside real business processes. - This series is where each layer gets built end to end, then published for what holds up. -

-
- LinkedIn - zishanalikhan.com - GitHub +
+
Wire
+
AG-UI over SSETEXT_MESSAGE, TOOL_CALL, GENERATIVE_UI, USER_TASK.
+ EP 02โ€“03
-
- - -
-
-
Agents, Orchestrated ยท MIT ยท 2026 Zishan Ali Khan
-
- Series hub - ยท Codemap - ยท Films +
+
Loop
+
Generate โ†’ run โ†’ check โ†’ repeatDone is evidence, not vibes.
+ EP 01
+
+
Control
+
Camunda 8 ยท BPMN + user tasksPaused tokens are the product.
+ EP 02โ€“03 +
+
+
Grounding
+
Knowledge base as a process stepWhat it canโ€™t verify, it doesnโ€™t invent.
+ EP 02 +
+
+
Authority
+
AP2 mandates ยท ES256 JWSYou sign the rules. The agent signs the cart. The merchant verifies both.
+ EP 03 +
+
+
Policy
+
DMN decision tablesWealth routing. Spending policy. Visible in Operate.
+ EP 02โ€“03 +
+
+
+
MCP

Connects agents to tools and context.

+
A2A

Connects agents to other agents.

+
AG-UI

Connects agents to users โ€” the last mile.

+
AP2

Connects agents to money, with a signature.

+
+
+ +
+
+

For the extra curious

+

A visual map of the three public repos. Click a node for the file, what it emits, and the GitHub link.

+
+ +
+ + + - + +

Views are my own. Built in the open, reviewed line by line.

+ diff --git a/docs/vercel.json b/docs/vercel.json new file mode 100644 index 0000000..69567d8 --- /dev/null +++ b/docs/vercel.json @@ -0,0 +1,4 @@ +{ + "cleanUrls": true, + "trailingSlash": true +} diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..efc7446 --- /dev/null +++ b/vercel.json @@ -0,0 +1,5 @@ +{ + "outputDirectory": "docs", + "cleanUrls": true, + "trailingSlash": true +} From 539498887f80d797706d59f5b2065045033ec3f5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 28 Aug 2026 14:10:36 +0000 Subject: [PATCH 3/5] Add hub tests and a safe Vercel deploy shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep the GitHub README as the original series hub. Serve the visual pages from docs/ as a separate Vercel project, with rewrites only for /agents-orchestrated โ€” never /series/agents-orchestrated. Tests check local structure, that the live essays still ship, and deployed parity once the path is live. Co-authored-by: Zishan Ali Khan --- .github/workflows/hub-tests.yml | 22 +++++++ .gitignore | 3 + README.md | 8 --- package.json | 11 ++++ scripts/deploy.mjs | 53 +++++++++++++++++ scripts/parent-site-rewrites.json | 16 +++++ tests/deployed-parity.test.mjs | 68 +++++++++++++++++++++ tests/hub.test.mjs | 99 +++++++++++++++++++++++++++++++ tests/live-essays.test.mjs | 63 ++++++++++++++++++++ vercel.json | 10 +++- 10 files changed, 344 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/hub-tests.yml create mode 100644 .gitignore create mode 100644 package.json create mode 100644 scripts/deploy.mjs create mode 100644 scripts/parent-site-rewrites.json create mode 100644 tests/deployed-parity.test.mjs create mode 100644 tests/hub.test.mjs create mode 100644 tests/live-essays.test.mjs diff --git a/.github/workflows/hub-tests.yml b/.github/workflows/hub-tests.yml new file mode 100644 index 0000000..4ed2357 --- /dev/null +++ b/.github/workflows/hub-tests.yml @@ -0,0 +1,22 @@ +name: Hub tests + +on: + push: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "22" + - name: Local hub structure + run: node --test tests/hub.test.mjs + - name: Live essays still ship + run: node --test tests/live-essays.test.mjs + - name: Deployed hub parity + run: node --test tests/deployed-parity.test.mjs + env: + LIVE_HUB_URL: https://zishanalikhan.com/agents-orchestrated/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1dc642a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +.vercel +.DS_Store diff --git a/README.md b/README.md index 540ebe9..7d3ea12 100644 --- a/README.md +++ b/README.md @@ -9,17 +9,9 @@ toys: every build carries the parts that decide whether agents survive production โ€” verification, orchestration, human oversight, grounding, and an audit trail. -**Visual hub:** [zishanalikhan.com/agents-orchestrated](https://zishanalikhan.com/agents-orchestrated/) -ยท **Codemap:** [โ€ฆ/codemap](https://zishanalikhan.com/agents-orchestrated/codemap/) -ยท **Essays (unchanged):** [โ€ฆ/series/agents-orchestrated](https://zishanalikhan.com/series/agents-orchestrated/) - ๐Ÿ“บ Episodes land on [LinkedIn](https://www.linkedin.com/in/zishanalikhan). Every build is a public repo on this profile. -The static files live in [`docs/`](docs/). Map the Vercel app for this repo to -the hub URL above (root directory: `docs`). That path is currently a 404, so it -does not override the essay index. - --- ## Episodes diff --git a/package.json b/package.json new file mode 100644 index 0000000..3a97221 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "agents-orchestrated-hub", + "private": true, + "type": "module", + "scripts": { + "test": "node --test tests/hub.test.mjs", + "test:live": "node --test tests/live-essays.test.mjs", + "test:parity": "node --test tests/deployed-parity.test.mjs", + "test:all": "node --test tests/*.test.mjs" + } +} diff --git a/scripts/deploy.mjs b/scripts/deploy.mjs new file mode 100644 index 0000000..481305f --- /dev/null +++ b/scripts/deploy.mjs @@ -0,0 +1,53 @@ +#!/usr/bin/env node +/** + * Deploy this hub as its own Vercel project. + * Never attaches zishanalikhan.com (that would take over the personal site). + * After deploy, prints the rewrite to add on the personal-site project. + */ +import { spawnSync } from "node:child_process"; + +const token = process.env.VERCEL_TOKEN; +if (!token) { + console.error("VERCEL_TOKEN is not set"); + process.exit(1); +} + +function run(args) { + const r = spawnSync("npx", ["--yes", "vercel@latest", ...args], { + encoding: "utf8", + env: { ...process.env, VERCEL_TOKEN: token }, + }); + process.stdout.write(r.stdout || ""); + process.stderr.write(r.stderr || ""); + if (r.status !== 0) process.exit(r.status ?? 1); + return (r.stdout || "").trim(); +} + +const prod = process.argv.includes("--prod"); +const args = [ + "deploy", + "--yes", + "--token", + token, + "--name", + "agents-orchestrated-hub", +]; +if (prod) args.push("--prod"); + +const url = run(args); +console.log("\nDeployed:", url); +console.log(` +Add this rewrite on the zishanalikhan.com Vercel project (not this one): + +{ + "source": "/agents-orchestrated", + "destination": "${url.replace(/\/$/, "")}/" +} +{ + "source": "/agents-orchestrated/:path*", + "destination": "${url.replace(/\/$/, "")}/:path*" +} + +Do not point the personal-site domain at this project. +Do not rewrite /series/agents-orchestrated. +`); diff --git a/scripts/parent-site-rewrites.json b/scripts/parent-site-rewrites.json new file mode 100644 index 0000000..d40df5b --- /dev/null +++ b/scripts/parent-site-rewrites.json @@ -0,0 +1,16 @@ +{ + "rewrites": [ + { + "source": "/agents-orchestrated", + "destination": "https://agents-orchestrated-hub.vercel.app/" + }, + { + "source": "/agents-orchestrated/", + "destination": "https://agents-orchestrated-hub.vercel.app/" + }, + { + "source": "/agents-orchestrated/:path*", + "destination": "https://agents-orchestrated-hub.vercel.app/:path*" + } + ] +} diff --git a/tests/deployed-parity.test.mjs b/tests/deployed-parity.test.mjs new file mode 100644 index 0000000..5565dbf --- /dev/null +++ b/tests/deployed-parity.test.mjs @@ -0,0 +1,68 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { test } from "node:test"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const LIVE = process.env.LIVE_HUB_URL || "https://zishanalikhan.com/agents-orchestrated/"; +const REQUIRED = process.env.REQUIRE_DEPLOYED === "1"; + +function local() { + return readFileSync(join(root, "docs/index.html"), "utf8"); +} + +function cmap() { + return readFileSync(join(root, "docs/codemap.html"), "utf8"); +} + +async function get(url) { + const res = await fetch(url, { redirect: "follow" }); + return { status: res.status, body: await res.text(), url: res.url }; +} + +const markers = [ + "Agents, Orchestrated ยท letmereviewyourcode", + "visual companion", + "Visual codemap", + "Loops: the agentic primitive everything converged on", + "AG-UI: the agent that streams an interface, not a transcript", + "Agent payments: mandates, not trust", + "For the extra curious", +]; + +test("deployed hub matches local markers (or is still unpublished)", async (t) => { + const { status, body } = await get(LIVE); + if (status === 404) { + if (REQUIRED) { + assert.fail(`${LIVE} is 404; rewrite/deploy is not live`); + } + t.skip(`${LIVE} is still 404 โ€” hub not rewritten onto the personal site yet`); + return; + } + assert.equal(status, 200, `${LIVE} returned ${status}`); + const html = local(); + for (const s of markers) { + assert.ok(html.includes(s) || html.includes(s.replace(" ยท letmereviewyourcode", "")), `local missing ${s}`); + assert.ok(body.includes(s) || body.includes("Visual codemap"), `deployed missing ${s}`); + } + assert.ok(body.includes("codemap"), "deployed hub missing codemap"); + assert.ok(!body.includes("This page doesn't exist"), "deployed hub is still the personal-site 404"); +}); + +test("deployed codemap matches local views (or is still unpublished)", async (t) => { + const base = LIVE.endsWith("/") ? LIVE : LIVE + "/"; + const { status, body } = await get(base + "codemap/"); + const fallback = status >= 400 ? await get(base + "codemap.html") : { status, body }; + if (fallback.status === 404) { + if (REQUIRED) assert.fail("codemap 404"); + t.skip("codemap not deployed yet"); + return; + } + assert.equal(fallback.status, 200); + const html = cmap(); + for (const view of ["Series stack", "EP 01 loops", "EP 02 AURUM", "EP 03 AP2", "Shared DNA"]) { + assert.ok(html.includes(view), `local codemap missing ${view}`); + assert.ok(fallback.body.includes(view), `deployed codemap missing ${view}`); + } +}); diff --git a/tests/hub.test.mjs b/tests/hub.test.mjs new file mode 100644 index 0000000..ee3507f --- /dev/null +++ b/tests/hub.test.mjs @@ -0,0 +1,99 @@ +import assert from "node:assert/strict"; +import { readFileSync, existsSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { test } from "node:test"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const docs = join(root, "docs"); + +function read(rel) { + return readFileSync(join(docs, rel), "utf8"); +} + +test("hub files exist", () => { + for (const f of [ + "index.html", + "codemap.html", + "assets/site.css", + "assets/site.js", + "assets/codemap.js", + "assets/favicon.svg", + ]) { + assert.equal(existsSync(join(docs, f)), true, `missing ${f}`); + } +}); + +test("hub is a companion, not the essay index", () => { + const html = read("index.html"); + assert.match(html, /visual companion/i); + assert.match(html, /https:\/\/zishanalikhan\.com\/series\/agents-orchestrated\//); + assert.match(html, /canonical" href="https:\/\/zishanalikhan\.com\/agents-orchestrated\//); + assert.doesNotMatch(html, /This page doesn't exist/); +}); + +test("hub title and chrome match the personal site", () => { + const html = read("index.html"); + const css = read("assets/site.css"); + assert.match(html, /Agents, Orchestrated ยท letmereviewyourcode<\/title>/); + assert.match(html, /letmereview/); + assert.match(html, /yourcode/); + assert.match(html, /theme-toggle/); + assert.match(css, /Newsreader/); +}); + +test("shipped episodes plus queued arc are listed", () => { + const html = read("index.html"); + for (const s of [ + "Loops: the agentic primitive everything converged on", + "AG-UI: the agent that streams an interface, not a transcript", + "Agent payments: mandates, not trust", + "Multi-LLM Polyglot", + "Agent-to-Agent", + "Verification, Deployed", + "what-are-loops", + "camunda-agui-wealth-demo", + "camunda-ap2-payments-demo", + ]) { + assert.match(html, new RegExp(s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))); + } +}); + +test("assets are relative so a path prefix still works", () => { + const index = read("index.html"); + const cmap = read("codemap.html"); + assert.match(index, /href="assets\/site\.css"/); + assert.match(index, /src="assets\/site\.js"/); + assert.doesNotMatch(index, /href="\/assets\//); + assert.match(cmap, /href="assets\/site\.css"/); + assert.match(cmap, /src="assets\/codemap\.js"/); + assert.match(index, /href="codemap\.html"/); +}); + +test("codemap has the five views and inspector", () => { + const html = read("codemap.html"); + const js = read("assets/codemap.js"); + for (const view of ["stack", "ep01", "ep02", "ep03", "shared"]) { + assert.match(html, new RegExp(`data-view="${view}"`)); + assert.match(js, new RegExp(`${view}:`)); + } + assert.match(html, /id="cmap-stage"/); + assert.match(html, /id="inspector"/); + assert.match(js, /selectNode/); +}); + +test("this repo does not ship the essay routes", () => { + assert.equal(existsSync(join(docs, "series")), false); + const index = read("index.html"); + assert.doesNotMatch(index, /href="\/series\/agents-orchestrated\/"/); +}); + +test("root vercel.json serves docs and prefixes /agents-orchestrated", () => { + const cfg = JSON.parse(readFileSync(join(root, "vercel.json"), "utf8")); + assert.equal(cfg.outputDirectory, "docs"); + assert.equal(cfg.trailingSlash, true); + const sources = (cfg.rewrites || []).map((r) => r.source); + assert.ok(sources.includes("/agents-orchestrated")); + assert.ok(sources.includes("/agents-orchestrated/:path*")); + assert.ok(!sources.some((s) => s.includes("/series/")), "must not rewrite essay routes"); +}); diff --git a/tests/live-essays.test.mjs b/tests/live-essays.test.mjs new file mode 100644 index 0000000..17575e2 --- /dev/null +++ b/tests/live-essays.test.mjs @@ -0,0 +1,63 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +const ESSAYS = [ + { + url: "https://zishanalikhan.com/series/agents-orchestrated/", + must: [ + "Agents, Orchestrated", + "Loops: the agentic primitive everything converged on", + "AG-UI: the agent that streams an interface, not a transcript", + "Agent payments: mandates, not trust", + "Multi-LLM Polyglot", + ], + mustNot: ["This page doesn't exist"], + }, + { + url: "https://zishanalikhan.com/series/agents-orchestrated/loops/", + must: ["All roads led to /goal", "the loop you can audit"], + }, + { + url: "https://zishanalikhan.com/series/agents-orchestrated/ag-ui/", + must: ["AURUM", "One process. Three models. Zero glue code."], + }, + { + url: "https://zishanalikhan.com/series/agents-orchestrated/agent-payments/", + must: ["mandates, not trust", "AP2"], + }, +]; + +async function get(url) { + const res = await fetch(url, { redirect: "follow" }); + const body = await res.text(); + return { status: res.status, body }; +} + +test("essay index and episode pages still ship", async () => { + for (const page of ESSAYS) { + const { status, body } = await get(page.url); + assert.equal(status, 200, `${page.url} returned ${status}`); + for (const s of page.must || []) { + assert.ok(body.includes(s), `${page.url} missing: ${s}`); + } + for (const s of page.mustNot || []) { + assert.ok(!body.includes(s), `${page.url} unexpectedly contains: ${s}`); + } + } +}); + +test("the reserved hub path is not an essay page", async () => { + const { status, body } = await get("https://zishanalikhan.com/agents-orchestrated/"); + // Before the rewrite lands this is a 404. After deploy it is the hub. + // Either way it must never serve the essay index HTML. + assert.ok( + !body.includes("One layer of the agentic stack per episode. Built for real, reviewed in public.") || + body.includes("visual companion") || + status === 404, + "hub path collided with the essay index" + ); + if (status === 200) { + assert.match(body, /visual companion/i); + assert.match(body, /codemap/i); + } +}); diff --git a/vercel.json b/vercel.json index efc7446..2753e4b 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,13 @@ { + "$schema": "https://openapi.vercel.sh/vercel.json", + "installCommand": "echo skip", + "buildCommand": "node --test tests/hub.test.mjs", "outputDirectory": "docs", "cleanUrls": true, - "trailingSlash": true + "trailingSlash": true, + "rewrites": [ + { "source": "/agents-orchestrated", "destination": "/" }, + { "source": "/agents-orchestrated/", "destination": "/" }, + { "source": "/agents-orchestrated/:path*", "destination": "/:path*" } + ] } From c0670c0ab74caac0474bb2d0cdf53253af9ac6b6 Mon Sep 17 00:00:00 2001 From: Cursor Agent <cursoragent@cursor.com> Date: Fri, 28 Aug 2026 14:33:32 +0000 Subject: [PATCH 4/5] Serve the hub under /agents-orchestrated on the personal domain Stage the static files at the path prefix Vercel forwards, match the live site fonts and chrome, and keep public URLs on zishanalikhan.com instead of the Vercel app hostname. Co-authored-by: Zishan Ali Khan <letmereviewyourcode@users.noreply.github.com> --- .gitignore | 1 + docs/assets/codemap.js | 4 +- docs/assets/site.css | 188 ++++++++++++++++++++++++------ docs/assets/site.js | 68 ++++++++++- docs/codemap.html | 73 +++++++++--- docs/index.html | 87 +++++++++----- package.json | 1 + scripts/parent-site-rewrites.json | 15 +-- scripts/stage-prefix.mjs | 19 +++ tests/deployed-parity.test.mjs | 1 + tests/hub.test.mjs | 19 ++- vercel.json | 2 +- 12 files changed, 378 insertions(+), 100 deletions(-) create mode 100644 scripts/stage-prefix.mjs diff --git a/.gitignore b/.gitignore index 1dc642a..a7b1b87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules .vercel .DS_Store +docs/agents-orchestrated diff --git a/docs/assets/codemap.js b/docs/assets/codemap.js index 4e90461..bd57d5b 100644 --- a/docs/assets/codemap.js +++ b/docs/assets/codemap.js @@ -289,14 +289,14 @@ function renderView(viewId, selectedId) { class: "n-label", x: node.x + 16, y: node.y + (node.sub ? node.h / 2 - 4 : node.h / 2 + 5), "font-size": "13", "font-weight": "600", - "font-family": "Inter, system-ui, sans-serif", + "font-family": '"Inter Variable", system-ui, sans-serif', }, [node.label])); if (node.sub) { g.appendChild(el("text", { class: "n-sub", x: node.x + 16, y: node.y + node.h / 2 + 14, "font-size": "11", - "font-family": "JetBrains Mono, ui-monospace, monospace", + "font-family": '"JetBrains Mono Variable", ui-monospace, monospace', }, [node.sub])); } g.addEventListener("click", (e) => { diff --git a/docs/assets/site.css b/docs/assets/site.css index 0bb3e00..42e256a 100644 --- a/docs/assets/site.css +++ b/docs/assets/site.css @@ -1,12 +1,59 @@ /* Companion hub for Agents, Orchestrated. - Tokens match zishanalikhan.com so this sits next to the essays, not over them. */ + Tokens, type, and chrome match zishanalikhan.com (field guides + series). */ -@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,600;1,6..72,400;1,6..72,600&display=swap"); +@font-face { + font-family: "Inter Variable"; + font-style: normal; + font-display: swap; + font-weight: 100 900; + src: url("/_astro/inter-latin-ext-wght-normal.DO1Apj_S.woff2") format("woff2-variations"); + unicode-range: U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: "Inter Variable"; + font-style: normal; + font-display: swap; + font-weight: 100 900; + src: url("/_astro/inter-latin-wght-normal.Dx4kXJAl.woff2") format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@font-face { + font-family: "Newsreader Variable"; + font-style: normal; + font-display: swap; + font-weight: 200 800; + src: url("/_astro/newsreader-latin-ext-wght-normal.C-3rgBeH.woff2") format("woff2-variations"); + unicode-range: U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: "Newsreader Variable"; + font-style: normal; + font-display: swap; + font-weight: 200 800; + src: url("/_astro/newsreader-latin-wght-normal.CCVVNp6i.woff2") format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@font-face { + font-family: "JetBrains Mono Variable"; + font-style: normal; + font-display: swap; + font-weight: 100 800; + src: url("/_astro/jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2") format("woff2-variations"); + unicode-range: U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: "JetBrains Mono Variable"; + font-style: normal; + font-display: swap; + font-weight: 100 800; + src: url("/_astro/jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2") format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} :root { - --font-sans: "Inter", system-ui, sans-serif; - --font-serif: "Newsreader", Georgia, serif; - --font-mono: "JetBrains Mono", ui-monospace, monospace; + --font-sans: "Inter Variable", system-ui, sans-serif; + --font-serif: "Newsreader Variable", Georgia, serif; + --font-mono: "JetBrains Mono Variable", ui-monospace, "SF Mono", monospace; --bg: #fcfcfa; --bg-raised: #f6f6f2; --bg-inset: #f1f1ec; @@ -47,6 +94,7 @@ html { scroll-behavior: smooth; } html, body { margin: 0; padding: 0; } body { font-family: var(--font-sans); + font-feature-settings: "ss03", "cv02"; background: var(--bg); color: var(--ink); line-height: 1.625; @@ -54,12 +102,13 @@ body { display: flex; flex-direction: column; -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; } ::selection { background: var(--selection); } img { max-width: 100%; display: block; } a { color: inherit; text-decoration: none; } button { font-family: inherit; background: none; border: 0; color: inherit; cursor: pointer; } -code, .mono { font-family: var(--font-mono); } +code, .mono, kbd { font-family: var(--font-mono); } .skip { position: absolute; left: -999px; top: 0; @@ -67,45 +116,66 @@ code, .mono { font-family: var(--font-mono); } } .skip:focus { left: 12px; top: 12px; } -.narrow { width: min(var(--max), calc(100% - 48px)); margin-left: auto; margin-right: auto; } -.wide { width: min(var(--max-wide), calc(100% - 48px)); margin-left: auto; margin-right: auto; } +.shell { + width: 100%; + max-width: var(--max); + margin-left: auto; + margin-right: auto; + padding-left: 24px; + padding-right: 24px; +} +.wide-shell { + width: 100%; + max-width: var(--max-wide); + margin-left: auto; + margin-right: auto; + padding-left: 24px; + padding-right: 24px; +} /* โ”€โ”€ Nav (same chrome as zishanalikhan.com) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.site-header { - width: min(var(--max), calc(100% - 48px)); - margin: 0 auto; - padding: 32px 0 16px; -} +.site-header { padding: 32px 0 16px; } .site-nav { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; } .brand { - font-family: var(--font-mono); font-size: 13px; letter-spacing: -0.02em; + font-family: var(--font-mono); font-size: 13px; letter-spacing: -0.025em; } .brand .muted { color: var(--ink-muted); } .brand .accent { color: var(--accent); } .brand:hover { color: var(--accent); } .nav-right { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; } -.nav-right a, .icon-btn { +.nav-right > a, .icon-btn { padding: 4px 8px; font-size: 13px; border-radius: 6px; - color: var(--ink-muted); transition: color 0.15s, background 0.15s; + color: var(--ink-muted); transition: color 0.12s, background 0.12s; } -.nav-right a:hover, .icon-btn:hover { color: var(--ink); } -.nav-right a.here { color: var(--ink); font-weight: 500; } +.nav-right > a:hover, .icon-btn:hover { color: var(--ink); } +.nav-right > a.here { color: var(--ink); font-weight: 500; } .icon-btn { display: inline-flex; align-items: center; padding: 6px; } html:not(.dark) .icon-sun { display: none; } html.dark .icon-moon { display: none; } +.locale { + display: flex; align-items: center; padding: 0 4px; + font-family: var(--font-mono); font-size: 11px; color: var(--ink-muted); +} +.locale a { + padding: 0 4px; color: var(--ink-muted); transition: color 0.12s; +} +.locale a:hover { color: var(--ink); } +.locale a.here { color: var(--ink); font-weight: 500; } +.locale .dot { user-select: none; } + /* โ”€โ”€ Type โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ .kicker { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-muted); margin-bottom: 12px; } h1, h2, h3 { letter-spacing: -0.025em; line-height: 1.15; margin: 0; } -h1 { font-family: var(--font-serif); font-size: clamp(2rem, 4vw, 2.25rem); font-weight: 600; } +h1 { font-family: var(--font-serif); font-size: 2.25rem; font-weight: 600; } h2 { font-family: var(--font-serif); font-size: 1.35rem; font-weight: 600; } -h3 { font-size: 1rem; font-weight: 600; } +h3 { font-size: 1.125rem; font-weight: 600; } .lede { margin-top: 16px; max-width: 36rem; font-size: 15px; color: var(--ink-secondary); line-height: 1.625; @@ -120,7 +190,17 @@ h3 { font-size: 1rem; font-weight: 600; } font-size: 16px; color: var(--ink-secondary); } +.prose-link { + color: var(--ink-secondary); + text-decoration: underline; + text-decoration-color: var(--accent); + text-underline-offset: 3px; + text-decoration-thickness: 1px; +} +.prose-link:hover { color: var(--accent); } + /* โ”€โ”€ Hero / sections โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +main { flex: 1; } .hero { padding: 64px 0 40px; } .section { padding: 8px 0 48px; } .section-head { margin-bottom: 20px; } @@ -133,21 +213,20 @@ h3 { font-size: 1rem; font-weight: 600; } } .note a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; } -/* โ”€โ”€ Buttons โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ -.btn-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; } +/* โ”€โ”€ Buttons (match Start here / Field guides) โ”€โ”€โ”€โ”€โ”€ */ +.btn-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; align-items: center; } .btn { display: inline-flex; align-items: center; gap: 6px; - padding: 7px 12px; border-radius: 6px; font-size: 13px; font-weight: 500; - border: 1px solid var(--border); background: var(--bg-raised); - transition: border-color 0.15s, color 0.15s; + padding: 8px 16px; border-radius: 6px; font-size: 13px; font-weight: 500; + border: 1px solid var(--border); background: transparent; + color: var(--ink-secondary); + transition: border-color 0.12s, color 0.12s, background 0.12s; } -.btn:hover { border-color: var(--border-strong); color: var(--accent); } +.btn:hover { border-color: var(--border-strong); color: var(--ink); } .btn-accent { - background: var(--accent); color: #fff; border-color: transparent; + background: var(--ink); color: var(--bg); border-color: transparent; } -html.dark .btn-accent { color: #0b0f14; } -.btn-accent:hover { color: #fff; filter: brightness(1.05); } -html.dark .btn-accent:hover { color: #0b0f14; } +.btn-accent:hover { background: var(--accent); color: #fff; border-color: transparent; } /* โ”€โ”€ Episode list (matches series index cards) โ”€โ”€โ”€โ”€โ”€โ”€ */ .card-list { display: flex; flex-direction: column; gap: 12px; } @@ -236,7 +315,7 @@ a.card:hover h2 { color: var(--accent); } min-height: 560px; border: 1px solid var(--border); border-radius: 12px; background: var(--bg-raised); overflow: hidden; } -.cmap-stage svg { display: block; width: 100%; height: auto; } +.cmap-stage svg { display: block; width: 100%; height: auto; color: var(--border-strong); } .inspector { position: sticky; top: 16px; padding: 18px; border: 1px solid var(--border); border-radius: 12px; background: var(--bg-raised); @@ -300,8 +379,8 @@ a.card:hover h2 { color: var(--accent); } /* โ”€โ”€ Footer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ .site-footer { - width: min(var(--max), calc(100% - 48px)); - margin: 80px auto 0; padding: 48px 0 56px; + padding: 48px 0 56px; + margin-top: 80px; border-top: 1px solid var(--border); } .foot-row { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 16px; } @@ -310,14 +389,55 @@ a.card:hover h2 { color: var(--accent); } .foot-links { display: flex; flex-wrap: wrap; gap: 16px; } .fine { margin-top: 16px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); opacity: 0.7; } +/* โ”€โ”€ Search (same modal as the personal site) โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ +#search-modal { + background: transparent; border: 0; padding: 0; + width: min(36rem, calc(100vw - 32px)); margin: 12vh auto 0; +} +#search-modal::backdrop { background: rgba(0, 0, 0, 0.6); } +.search-box { + border-radius: 12px; border: 1px solid var(--border-strong); + background: var(--bg); overflow: hidden; + box-shadow: 0 24px 64px rgba(0, 0, 0, 0.28); +} +.search-row { + display: flex; align-items: center; gap: 12px; + padding: 0 16px; border-bottom: 1px solid var(--border); +} +.search-row .prompt { font-family: var(--font-mono); font-size: 13px; color: var(--accent); } +#search-input { + flex: 1; padding: 14px 0; border: 0; background: transparent; + font: 15px/1.4 var(--font-sans); color: var(--ink); outline: none; +} +#search-input::placeholder { color: var(--ink-muted); } +.search-row kbd { + font-size: 10.5px; padding: 2px 6px; border-radius: 4px; + border: 1px solid var(--border); color: var(--ink-muted); +} +#search-results { max-height: 50vh; overflow-y: auto; } +.search-hit { + display: block; padding: 12px 16px; + border-bottom: 1px solid var(--border); +} +.search-hit:last-child { border-bottom: 0; } +.search-hit:hover { background: var(--bg-raised); } +.search-hit .t { font-size: 14px; font-weight: 500; } +.search-hit .k { + font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; + letter-spacing: 0.06em; color: var(--accent); margin-left: 8px; +} +.search-hit p { margin: 4px 0 0; font-size: 12.5px; color: var(--ink-muted); } +.search-msg { padding: 16px; font-size: 13px; color: var(--ink-muted); font-family: var(--font-mono); } + @media (max-width: 900px) { .cmap-shell, .shot-row, .vocab, .shared-grid, .protocols { grid-template-columns: 1fr; } .stack-row { grid-template-columns: 1fr; gap: 4px; } .inspector { position: static; } } @media (max-width: 560px) { - .narrow, .wide, .site-header, .site-footer { width: min(var(--max), calc(100% - 32px)); } + .shell, .wide-shell { padding-left: 16px; padding-right: 16px; } .hero { padding-top: 36px; } + h1 { font-size: 1.875rem; } } @media (prefers-reduced-motion: reduce) { diff --git a/docs/assets/site.js b/docs/assets/site.js index 76f51fe..dc8f140 100644 --- a/docs/assets/site.js +++ b/docs/assets/site.js @@ -1,12 +1,74 @@ (function () { const root = document.documentElement; const stored = localStorage.getItem("theme"); - if (stored === "light") root.classList.remove("dark"); - else if (stored === "dark") root.classList.add("dark"); - else if (!root.classList.contains("dark")) root.classList.add("dark"); + const wantsLight = stored === "light"; + root.classList.toggle("dark", !wantsLight); document.getElementById("theme-toggle")?.addEventListener("click", () => { const isDark = root.classList.toggle("dark"); localStorage.setItem("theme", isDark ? "dark" : "light"); }); + + document.querySelectorAll("#locale-picker a[data-locale]").forEach((link) => { + link.addEventListener("click", () => { + localStorage.setItem("locale", link.dataset.locale); + }); + }); + + const modal = document.getElementById("search-modal"); + const input = document.getElementById("search-input"); + const resultsEl = document.getElementById("search-results"); + let pagefind = null; + + async function openSearch() { + if (!modal || modal.open) return; + modal.showModal(); + input?.focus(); + if (!pagefind) { + try { + pagefind = await import("/pagefind/pagefind.js"); + pagefind.init(); + } catch { + if (resultsEl) { + resultsEl.innerHTML = + `<p class="search-msg">${resultsEl.dataset.msgNobuild || "Search index not built yet."}</p>`; + } + } + } + } + + document.addEventListener("keydown", (e) => { + if ((e.metaKey || e.ctrlKey) && e.key === "k") { + e.preventDefault(); + modal?.open ? modal.close() : openSearch(); + } + }); + document.getElementById("search-open")?.addEventListener("click", openSearch); + modal?.addEventListener("click", (e) => { + if (e.target === modal) modal.close(); + }); + + input?.addEventListener("input", async () => { + if (!pagefind || !resultsEl) return; + const query = input.value.trim(); + if (!query) { + resultsEl.innerHTML = ""; + return; + } + const search = await pagefind.debouncedSearch(query); + if (!search) return; + const items = await Promise.all(search.results.slice(0, 8).map((r) => r.data())); + resultsEl.innerHTML = items.length + ? items + .map( + (item) => ` + <a href="${item.url}" class="search-hit"> + <span class="t">${item.meta.title ?? item.url}</span> + ${item.meta.kind ? `<span class="k">${item.meta.kind}</span>` : ""} + <p>${item.excerpt}</p> + </a>` + ) + .join("") + : `<p class="search-msg">${resultsEl.dataset.msgEmpty || "No matches."}</p>`; + }); })(); diff --git a/docs/codemap.html b/docs/codemap.html index 2c16ce6..c0a17fe 100644 --- a/docs/codemap.html +++ b/docs/codemap.html @@ -5,28 +5,49 @@ <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Codemap ยท Agents, Orchestrated + - + + + + + + + -