Dev Day: non-coder follow-along + A2A governed quorum ("policy beats consensus") - #25
Merged
Conversation
…cklist Last live run: liked it but hard to follow. New bar = attendees who've never coded and don't know GitHub. build.html defaults to the Codespaces lane and surfaces UUIDs/tokens/SSE/make connect — unfollowable for them. - docs/follow.html: new watch-along page. Sticky tap-along "YOU ARE HERE" stage tracker (auto-advances on scroll), every stage in plain English, the 4 controls as human-language cards. Optional collapsed "try it yourself" brave lane (install Bob, paste the one make-connect line, type bob, 2 safe prompts). build.html stays the deeper coder version. - .github/workflows/pages.yml: publish docs/ to GitHub Pages so the follow-along gets a stable URL for a slide QR. - docs/assets/follow-qr.png: QR to the published follow.html. - docs/dev-day-runsheet.md: pin a "Fire up ContextForge from Codespaces" checklist at the top + an audience-follow-along note. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A tap now wins over scroll-sync for ~1.1s while it animates to the section (the tapped stage stayed lit instead of snapping back), and reaching the page bottom pins the last short stage (Proof) as current. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Watch-only had no on-ramp to actually doing it. Replace the minimal brave-lane card with a 3-way chooser: Just watch (default) / Try it — Codespaces + Bob / Full local. The two hands-on lanes deep-link into the existing build.html cards, preselected via ?mode=. Honest note that hands-on needs a laptop + Bob (phones are watch-only). build.html now honors ?mode=codespaces|local|watch so the chooser lands visitors on the right lane. Dropped the now-unused tap-to-copy handler. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reframe the chooser into 3 tiers by how much is on the attendee machine:
T1 Follow along + run it — browser only (phone/laptop), no install:
watch here + run scenarios on the shared Companion dashboard
T2 Drive Bob yourself — laptop + Bob, connect to cloud control plane
T3 Do it all yourself — Docker + Bob, make quickstart
T1 carries a "Run it live" button wired from ?dash=<companion-url> (http(s)
only, to block javascript: injection); falls back to "presenter will share
the link" when absent. New `make follow-link` (scripts/follow-link.sh) prints
the follow.html?dash=... share link, auto-detecting Codespaces (port 7070)
like make connect. Companion gets a phones media-query so Tier-1 mobile users
get a stacked, full-width dashboard.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- runsheet: refresh the audience section to the 3 tiers; add a "Where the single control plane runs" ASCII diagram + how each tier connects + the shared-state/scale caveats. - build.html: new "Where it runs — how the whole room connects" collapsible (diagram + tier→port table + caveat) beside the existing Architecture one. - follow.html: optional "🗺️ The big picture" collapsible showing the existing architecture.svg with a plain-English caption + tap-to-zoom (collapsed so it never overwhelms the non-coder watch flow). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Companion: POST /api/register-agent (real register_mcp_server -> salestax-<INITIALS>, dedup name + retry), GET /api/agents (count + recent initials), GET /wall (projector view). Dashboard gets a register bar + live "agents built by the room" counter (polls 3s). AGENT_BACKEND_URL configurable. make agents-reset (scripts/agents-reset.sh) clears salestax-*; folded into demo-reset. build.html Stage 2 + dev-day-runsheet: name-with-initials participation prompt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… dont collide ContextForge enforces a unique url per gateway, so N agents could not share one backend url verbatim (409 Gateway already exists). Carry the unique name as a query suffix; the backend ignores it and serves /mcp normally. Verified: MG -> salestax-MG, second MG -> salestax-MG-2 (dedup), count climbs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Bob-lane register prompt pointed at the bare sales-tax url, so a 2nd Bob driver would 409 on ContextForge unique-url. Add the ?agent=<initials> suffix (matches the phone path) and note the Operator persona + shared-vs-own gateway. Verified: controlplane-register-mcp-server with the suffixed url -> registered:true, count climbs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The chooser's 3 cards now each open a dedicated page with only that tier's steps, instead of the shared mode-switching build.html that attendees found confusing: - path-run.html (Tier 1, everyone) — open dashboard, register, run scenarios - path-bob.html (Tier 2, intermediate) — install Bob, connect, drive the controls - path-diy.html (Tier 3, advanced) — full local stack, build + govern end to end Shared assets/path.css + assets/path.js (copy buttons, ?dash= forwarding). follow.html cards repointed; ?dash= now forwards to path-run.html. Verified against the live stack and captured in evidence/followalong-proof.html: 12 concurrent registrations (0->12, dedup MG/-2/-3, truncation, case-fold), 6/6 scenarios governed (ALLOWED/BLOCKED/REDACTED/NEUTRALIZED/EXECUTED), dashboard + wall shots. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The connect line carries a ~470-char bearer — retyping it off a slide is
hopeless and error-prone. New opt-in /connect surface lets laptop
attendees copy or download it instead:
- GET /connect — dark page: one-click Copy (Analyst/Operator),
Download settings.json, a fixed curl one-liner,
and the 3 proven drive prompts
- GET /api/connect — JSON {base, analyst, operator, oneliner, prompts}
- GET /bob/settings.json[?persona=] — ready .bob config to curl into place
- Dashboard gains a "Connect Bob (laptop)" link
Gated behind EXPOSE_CONNECT=1 (a.k.a. make companion-connect) because it
reveals the token to the browser; off by default. Public gateway URL is
derived for Codespaces; copy uses the full command (verified 612 chars,
display dimmed). Aligned the drive prompts across path-bob.html and the
runsheet to the explicit make-connect wording. Evidence page updated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A room needs BOTH ports public: 4444 (gateway) and 7070 (Companion) — phones hit 7070 and Tier-2 laptops pull their connect config from 7070/bob/settings.json. Checklist now starts the companion with the connect page on (make companion-connect) and flips both ports. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Room registration (phones + Tier-2 Bob Operator) points at http://sales-tax:8000/mcp, but the lite `make up` stack doesn't include sales-tax — so every registration 422s with SSRF_DNS_FAIL_CLOSED. - post-create.sh now scaffolds + runs sales-tax (make stage1-scaffold && make salestax-up), non-fatal on a Docker Hub pull limit. - Runsheet checklist adds the step (for reopened Codespaces) + a verify one-liner and the 422 explanation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Registration points at http://sales-tax:8000/mcp, which the lite stack omits — so attendees hit 422 (SSRF_DNS_FAIL_CLOSED) unless someone remembered to run salestax-up first. Now `make companion` depends on a new idempotent `salestax-ensure` (scaffolds + starts sales-tax only if not already running), so `make companion-connect` is genuinely the one presenter command. Runsheet collapsed back to a single companion step with the 422 as a troubleshooting note, not a required step. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Removes the GitHub Pages dependency for the live demo — the whole attendee experience now runs on one public port: - companion serves docs/ (follow.html, path-*.html, assets, diagrams); bounded to static extensions, source files 404. - GET /qr — full-screen projectable QR page (scan to join) - GET /qr.png — the QR image, generated server-side (qrcode[pil]) encoding <companion>/follow.html?dash=<companion> - make companion adds qrcode[pil]; prints the /qr URL - make follow-link defaults to the self-hosted follow.html (no Pages) and surfaces the /qr URL - make ports-public — one command to set 4444 + 7070 Public via gh (instead of clicking the PORTS tab) - runsheet checklist updated for both Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Dashboard roombar now has a prominent blue "📲 Join QR" link → /qr, so the presenter shows the room QR straight from the home page (no separate make follow-link / URL hunting). - make companion-connect now runs ports-public first, so the single command makes 4444+7070 Public, ensures sales-tax, and serves the dashboard + connect page + QR. Falls back to a PORTS-tab hint if gh lacks the codespace scope (non-fatal). - runsheet collapsed to: make companion-connect → click 📲 Join QR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sistency From pre-merge code review: - register_agent: only retry on a name/url collision (409 / "already exists"); fail fast on other errors. A down sales-tax backend returned 422 SSRF_DNS_FAIL_CLOSED and the loop hammered it 10× (~5min hang) — now returns instantly with "run make salestax-up". Verified 0s. - /api/agents: sanitize recent chips to alnum in _initials_of (names can be registered directly via Operator Bob and land on the projector /wall). - post-create.sh: use idempotent `make salestax-ensure` (gates scaffold on file-absence so it won't clobber a real server.py) instead of an unconditional stage1-scaffold; DRYs the duplicate logic. - connect.sh: add the missing rcpt_injection drive prompt so `make connect` prints all three (the runsheet + evidence page claim "these match"). - build.html / path-diy.html: align Stage-3 drive prompts to the canonical verbose wording (Acme LLC, "use the finbyte-gateway tools"). - evidence page: drop the stale "enable GitHub Pages / merge PR" note (it's served from :7070 now). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
From the Codex redundancy review (two P1s): - Drive prompts now have ONE source: docs/assets/prompts.json. The two runtime surfaces read it directly — companion/app.py (_load_drive_prompts + new public /api/prompts) and scripts/connect.sh (python loader, literal fallback). Static docs still embed copies for offline/copy-paste; new `make check-prompts` (scripts/check-prompts.py) fails if any surface drifts from the canonical set. Verified: guard passes now, catches an injected sentinel. - _companion_base() now honors COMPANION_URL / COMPANION_PUBLIC_URL, matching scripts/follow-link.sh. Fixes the divergence where /qr + /bob/settings.json pointed at localhost/Codespace while the shared link used the override. Verified: COMPANION_URL=… → /qr encodes that host. Deferred (Codex P2, post-talk): Flask templates extraction, shared site.css, data-driven path-page generator — higher churn, no demo-day benefit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GitHub Codespaces forwarded ports return 404 to anonymous clients (a phone with no GitHub login), so the companion-connect/Codespaces-ports path can't serve a real room — only logged-in browsers. `make present` (scripts/present.sh) fixes this: - installs cloudflared if missing, opens public quick-tunnels for the Companion (:7070) and gateway (:4444) - runs the Companion pointed at those tunnel URLs (COMPANION_URL / GATEWAY_PUBLIC_URL), so the QR + /connect line are publicly reachable - prints a banner (QR / dashboard / gateway URLs) and opens the presenter's browser to the join QR - Ctrl-C tears down the companion + both tunnels (trap kills by pid) Quick-tunnel URLs are random per run; the QR is generated live so nothing is hardcoded. Verified locally: tunnels up, companion serves tunnel URLs, anonymous external fetch returns the dashboard JSON. Runsheet updated to make `make present` the room command (companion-connect demoted to the same-network variant). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- add a "PRESENT TO A ROOM" help group listing `make present` (the room command) — it was missing from `make` help entirely - remove the duplicate `connect` line (it was printed under both the CODESPACES lane and DRIVE BOB groups) - present.sh kills a stale Companion on :7070 before starting, so a re-run doesn't fail with "address already in use" Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- remove the "📸 Static evidence gallery" header link and the /proof + /screenshots routes that served it (not needed for the demo) - add a "🛡️ Agentic AI Control Plane →" header link that opens the ContextForge admin at <gateway>/admin/login with the email pre-filled (?email=, which the gateway's login form honors) and the password shown to paste — throwaway demo creds (PLATFORM_ADMIN_* in .env). One click to the catalog where attendees can see their registered salestax-<INI> server. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Attendees create real, governed A2A agents (name + voting stance) that land in ContextForge's own /a2a catalog — no external registry. A quorum scenario fans an expense to every room agent via the gateway (votes audited), then OPA blocks the $12k wire regardless of the tally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8 tasks, TDD where it applies (pure vote logic), grounded in the real codebase: a2a-sdk room backend mirroring auditor, Companion /a2a registration + stance, gateway-mediated fan-out, and a headless quorum money-shot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Crowd votes locally (instant, no catalog writes); 5 fixed A2A voters seeded once provide the governed proof; OPA blocks the $50k wire regardless. Adds a gating ContextForge spike (Task 0), fixes the as_completed timeout crash with wait(), and adds a presenter freeze + room cap. Removes per-phone catalog tools, materialization races, and public-tunnel spam. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Live spike against the seeded mesh: shared-backend ?agent= yields unique callable a2a-* tools (<2s), the gateway bridges a text message to a Python a2a-sdk agent (auditor completed + returned an artifact), and delete removes the tool. Build is cleared. Artifact text is nested in result.content[0].text so the json.dumps(resp)+grep approach in _vote_one is correct. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hboard Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Leftover from the removed per-attendee register path (flagged in review). Unused; deleting avoids the false impression that AGENT_PREFIX name-gen is live. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Final-review should-fixes for a public room: - /api/freeze now requires PRESENTER_KEY when set (open when unset for local). Attendees with the shared QR dashboard URL get 403; presenter opens /?k=<key>. - Remove the dead "register an MCP server named salestax-..." line from the Connect page (the register path was replaced by local crowd voting). - Wall tab title + footer copy now reflect voting, not agent registration. Verified live: attendee freeze->403, presenter freeze->ok, vote-when-frozen->423, connect page salestax matches=0, quorum still BLOCKED, no-key local still open. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… gateway, attempts wire Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Evidence page (docs/evidence/a2a-quorum/index.html) with 6 screenshots from a live run, including the chair-agent orchestrated quorum (real agent->agent A2A) and a 'how to test it' guide. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ctor link Re-adds /api/register-mcp (salestax-<initials> MCP servers → ContextForge), removed when PR #24 repurposed the old endpoint for crowd voting. Adds a header link to the a2a-inspector. Voting + quorum untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Scales the quorum to a roomful of attendee voter agents; each call still abstains on error so gather never raises. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…CP server Consolidates participation: each attendee taps Join (initials + stance) and gets a room-<stance>-<initials> A2A voter (joins the chair-orchestrated quorum) AND a salestax-<initials> MCP server (joins the catalog). Caps + presenter freeze. Drops the now-empty crowd line from the quorum scenarios.
The gateway lowercases derived tool names, so an attendee agent room-strict-MG
creates tool a2a-room-strict-mg. The chair + companion built a2a-{name} with the
uppercase initials -> 'tool not found' -> abstain. Lowercase to match. Fixed
voters (no uppercase) were unaffected, which masked it until attendees joined.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rver Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d votes per the rule The voter fetches the owner's private note via the gateway corpus-get-corpus tool (PII-redacted, audited) and votes by its rule (dollar cap / keyword); falls back to stance when there is no note or the read fails. MCP now feeds the A2A quorum.
…ance Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The chair/companion prompt ends '... agent=room-strict-MG.' so parse_owner captured 'MG.' and the corpus lookup missed -> notes silently ignored (stance fallback). Strip non-alnum; add a period-terminated regression test. Caught live: a strict agent with an 'approve everything' note now correctly approves.
The chair now carries each voter's reason ([[name|vote|reason]]) and the card renders it, so 'strict-AB -> approve' is explained by 'owner's note says approve'. Also fix parse_threshold to prefer a $-amount: a PII-masked card '...-1111' was being read as a $1,111 cap. Caught by surfacing the reason on the card.
…iling The OPA policy now has two tiers: wires in [$10k, $100k) need approval, which a governed quorum MAJORITY supplies (the chair/companion derive approval=true from the tally); wires >= $100k are denied regardless — not even a unanimous quorum or dual approval can authorize them (policy beats consensus). Adds a $150k ceiling scenario + money shots. verify-controls 22/0.
…docs to two-tier - companion/app.py: remove extraneous f-prefix (ruff F541) -> 'make lint' green - room/agent_executor.py: drop unused 'import json' - policy-docs MCP server: describe the two-tier policy (the $100k hard ceiling + the quorum-majority approval) so the documented policy matches the enforced Rego
A2A quorum demo — "policy beats consensus"
…lowalong # Conflicts: # Makefile # companion/app.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the Dev Day demo work from `dev-day-non-coder-followalong` into `main`.
What's included
Provenance
Squashes the full follow-along branch history (incl. PR #24, already merged into this branch). All CI checks were green on #24 at merge time.
🤖 Generated with Claude Code