Skip to content

feat(demo): guided demo mode with human approval gate - #7

Merged
GautamTalksDev merged 3 commits into
mainfrom
feat/guided-demo
Aug 30, 2026
Merged

feat(demo): guided demo mode with human approval gate#7
GautamTalksDev merged 3 commits into
mainfrom
feat/guided-demo

Conversation

@GautamTalksDev

Copy link
Copy Markdown
Owner

One button drives the full flow for screen recording: scan, fan-out, sandbox
reconciliation, summary, staggered approvals — then stops at the protected CI card
and waits indefinitely for a human Continue. Every step uses the real API, not a
scripted animation. Stop/reset supported for repeat takes. Hidden outside
demo/replay mode.

@GautamTalksDev GautamTalksDev changed the title https://github.com/GautamTalksDev/keyring/pull/new/feat/guided-demo feat(demo): guided demo mode with human approval gate Aug 30, 2026
GautamTalksDev and others added 2 commits August 30, 2026 02:18
Automate the paced scan, human CI approval gate, execution results, and verified audit-ledger finish while keeping the controls isolated to demo and replay modes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Prevent replay-mode EventSource reconnects from duplicating the scan history after a run has completed.

Co-authored-by: Cursor <cursoragent@cursor.com>
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add guided demo flow and pending invitation support

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Adds demo-only orchestration for scanning, gated approvals, streamed execution, and ledger
 verification.
• Prevents stale scan responses and terminal SSE reconnects from corrupting active sessions.
• Models and revokes pending GitHub invitations while hardening TrueForge local setup.
Diagram

graph TD
  UI["Demo Controls"] --> Hook["Demo Orchestrator"] --> Session["Scan Session"] --> Scan["Scan API"] --> Decision["Decision API"] --> Execute["Execute Stream"] --> Audit["Audit API"] --> Panel["Ledger View"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Server-managed demo workflow
  • ➕ Centralizes workflow state and sequencing across browser refreshes.
  • ➕ Can enforce the human gate and timing independently of the UI.
  • ➖ Introduces demo-specific backend persistence and control endpoints.
  • ➖ Makes recording-oriented pacing harder to tune directly in the client.
  • ➖ Expands production server surface for a replay-only capability.

Recommendation: Keep the client-side orchestrator for this demo/replay-only workflow: it uses existing real APIs, isolates recording controls from normal operation, and avoids introducing backend-only demo state. A server-managed workflow would be preferable only if runs must survive page reloads or support multiple synchronized operators.

Files changed (23) +1628 / -225

Enhancement (11) +841 / -81
App.tsxWire guided-demo orchestration into the application shell +33/-0

Wire guided-demo orchestration into the application shell

• Detects demo or replay mode, initializes the guided workflow, and conditionally renders its launch button and status panel. Passes guided state into the approval queue and exposes scan cancellation for repeat takes.

apps/web/src/App.tsx

client.tsAdd cancellable card reads, execution streaming, and audit retrieval +94/-12

Add cancellable card reads, execution streaming, and audit retrieval

• Adds AbortSignal support for card refreshes, parses streamed execution SSE responses, and exposes audit-ledger retrieval. Scan EventSources now close after terminal events to prevent replay reconnections and duplicated history.

apps/web/src/api/client.ts

types.tsDefine audit record and verification response types +20/-0

Define audit record and verification response types

• Adds client types for append-only audit records and hash-chain verification results used by the guided ledger view.

apps/web/src/api/types.ts

ApprovalCardView.tsxDisable card actions during guided runs +12/-17

Disable card actions during guided runs

• Adds stable card element IDs for guided scrolling and an option to disable selection and decision controls while automation owns the queue.

apps/web/src/components/ApprovalCardView.tsx

ApprovalQueue.tsxLock and focus the approval queue in guided mode +26/-10

Lock and focus the approval queue in guided mode

• Suppresses manual bulk, execute, and per-card actions during guided runs. Automatically scrolls the currently targeted card into view for recording.

apps/web/src/components/ApprovalQueue.tsx

GuidedDemoPanel.tsxPresent guided progress, human gate, results, and ledger +220/-0

Present guided progress, human gate, results, and ledger

• Introduces the recording-focused overlay with stop and continue controls, phase messaging, protected-CI context, per-card execution outcomes, and audit-chain verification.

apps/web/src/components/GuidedDemoPanel.tsx

useGuidedDemo.tsOrchestrate the complete guided recording workflow +349/-0

Orchestrate the complete guided recording workflow

• Implements the paced scan-to-ledger state machine using real scan, decision, execution-stream, and audit APIs. Supports an indefinite human CI gate, minimum recording duration, cancellation, restart, and stale-run suppression.

apps/web/src/hooks/useGuidedDemo.ts

tools.tsRegister GitHub invitation MCP tool names +3/-0

Register GitHub invitation MCP tool names

• Adds constants for listing and deleting repository invitations so connector calls match the MCP schema.

packages/connectors/src/github/tools.ts

grant.tsModel active and pending grant access states +10/-25

Model active and pending grant access states

• Introduces the grant access-state type and carries optional pending-invitation state through grant construction without changing existing active grants.

packages/core/src/grant.ts

run.tsPreserve access state during reconciliation deserialization +4/-11

Preserve access state during reconciliation deserialization

• Carries optional grant access state through JSON revival so sandbox reconciliation retains pending invitation semantics.

packages/core/src/identity/run.ts

stub-model-server.tsSupport streamed model responses and reconciliation detection +70/-6

Support streamed model responses and reconciliation detection

• Adds OpenAI-compatible SSE completion chunks for streaming requests and tightens reconciliation-result recognition so the local harness can complete TrueForge-driven workflows.

scripts/stub-model-server.ts

Bug fix (4) +489 / -123
useScanSession.tsGuard scan sessions against stale asynchronous work +110/-17

Guard scan sessions against stale asynchronous work

• Adds a monotonic start coordinator, abortable card refreshes, scan identity checks, and explicit cancellation. Superseded starts and stale refreshes can no longer replace active queue or terminal state.

apps/web/src/hooks/useScanSession.ts

connector.tsInventory and revoke GitHub repository invitations correctly +162/-31

Inventory and revoke GitHub repository invitations correctly

• Discovers pending invitations separately from active collaborators and preserves invitation IDs in grant resources. Revocation calls the invitation deletion tool, provides restoration hints, handles already-absent invitations, and tolerates MCPs lacking optional inventory support.

packages/connectors/src/github/connector.ts

register-keyring-agent.tsHarden TrueForge registration and local endpoint compatibility +135/-54

Harden TrueForge registration and local endpoint compatibility

• Selects an available model, supports stub operation without sandboxing, rewrites local MCP URLs for Docker reachability, and validates the registered agent. Registration now reports response bodies and exits unsuccessfully when required resources fail.

scripts/register-keyring-agent.ts

live.tsManage pending GitHub invitations in live test setup +82/-21

Manage pending GitHub invitations in live test setup

• Distinguishes newly created invitations from active collaborators during seeding. Teardown now discovers and deletes matching pending invitations before removing collaborator access.

scripts/test-org/live.ts

Tests (3) +224 / -6
useScanSession.test.tsTest stale refresh and superseded scan protections +117/-1

Test stale refresh and superseded scan protections

• Adds reducer and coordinator coverage proving old card responses cannot overwrite a newer scan and only the latest start can own the SSE subscription.

apps/web/src/hooks/useScanSession.test.ts

github.contract.test.tsVerify pending invitations are inventoried as distinct grants +52/-5

Verify pending invitations are inventoried as distinct grants

• Adds connector contract coverage for invitation discovery, pending access state, invitation-specific resource IDs, and the correct revocation method.

packages/connectors/src/github.contract.test.ts

revoke.contract.test.tsTest invitation deletion requests +55/-0

Test invitation deletion requests

• Verifies pending invitation grants invoke the dedicated deletion tool with owner, repository, and invitation ID arguments.

packages/connectors/src/revoke.contract.test.ts

Documentation (3) +72 / -14
CONNECTORS.mdDocument GitHub pending invitation connector support +7/-6

Document GitHub pending invitation connector support

• Lists invitation inventory and deletion tools and clarifies that pending invitations require a different revocation endpoint than active collaborators.

docs/CONNECTORS.md

EXECUTE.mdAdd clean-machine setup instructions for live execution +54/-8

Add clean-machine setup instructions for live execution

• Documents database startup, migration, local stub model usage, API/UI startup, agent registration verification, and provider selection. Existing safety and result tables are reformatted.

docs/EXECUTE.md

UI.mdDocument the guided demo user journey +11/-0

Document the guided demo user journey

• Explains demo visibility, real replay scanning, staggered approvals, the protected CI pause, execution, ledger verification, stopping, and repeat takes.

docs/UI.md

Other (2) +2 / -1
manifest.jsonRefresh test organization fixture metadata +1/-1

Refresh test organization fixture metadata

• Updates the fixture generation timestamp for the regenerated test organization dataset.

fixtures/test-org/manifest.json

demo.tsExplicitly enable guided controls in demo mode +1/-0

Explicitly enable guided controls in demo mode

• Sets the demo-mode environment flag when launching the replay stack so recording controls are available without affecting normal UI sessions.

scripts/demo.ts

@qodo-code-review

qodo-code-review Bot commented Aug 30, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Cost-capped demo waits forever ✓ Resolved 🐞 Bug ☼ Reliability
Description
The guided scan wait predicate recognizes completed, partial, and failed states but omits the known
terminal cost_capped status, so when the server publishes scan.cost_capped and closes the SSE,
the demo remains in the scanning phase indefinitely instead of transitioning to its error/stopped
state. Stop is therefore the only way to recover from a cost-capped demo.
Code

apps/web/src/hooks/useGuidedDemo.ts[R188-195]

+      await waitFor(
+        () =>
+          activityRef.current.scanId === scanId &&
+          (activityRef.current.status === "completed" ||
+            activityRef.current.status === "partial" ||
+            activityRef.current.status === "failed"),
+        controller.signal,
+      );
Evidence
The guided flow's polling predicate only completes for completed, partial, or failed statuses, while
the client session reducer explicitly sets cost_capped on a scan.cost_capped event and both the
API stream subscription and server runner treat that event as terminal. Because the terminal SSE
event closes the stream, no later event can make the omitted predicate true.

apps/web/src/hooks/useGuidedDemo.ts[188-198]
apps/web/src/hooks/useScanSession.ts[406-417]
apps/web/src/api/client.ts[210-220]
packages/server/src/services/scan-runner.ts[171-197]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The guided demo never exits its scan completion wait when the scan reaches the terminal `cost_capped` state.

## Issue Context
Include `cost_capped` in the guided wait condition. The regular scan session, API stream subscription, and server treat this state as terminal, so the guided flow should wake and route it through the existing non-completed failure path with the activity error/recovery message.

## Fix Focus Areas
- apps/web/src/hooks/useGuidedDemo.ts[188-198]
- apps/web/src/hooks/useScanSession.ts[406-417]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Stop leaves decisions committed ✓ Resolved 🐞 Bug ☼ Reliability
Description
Stopping the guided demo aborts only its local controller; an already-started postDecision request
cannot be aborted and its result is applied without checking that this run is still current. A user
can therefore stop/reset the take while a safe-card approval or protected-card hold is still
recorded by the API, contradicting the reset behavior and leaving unintended decisions/audit
records.
Code

apps/web/src/hooks/useGuidedDemo.ts[R227-231]

+        const result = await postDecision(card.id, {
+          decision: "approve",
+          by: "guided-demo",
+        });
+        updateCard(result.card);
Evidence
stop aborts the controller and resets the scan, but postDecision has no signal parameter. The
decision route persists the decision and chained audit record independently once the request reaches
the server, while both guided call sites unconditionally apply the response after awaiting it.

apps/web/src/hooks/useGuidedDemo.ts[123-135]
apps/web/src/hooks/useGuidedDemo.ts[227-231]
apps/web/src/hooks/useGuidedDemo.ts[264-269]
apps/web/src/api/client.ts[75-87]
packages/server/src/api/routes.ts[235-251]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Stopping a guided run does not prevent an in-flight decision request from being committed or from updating local state when it later resolves.

## Issue Context
Pass the guided controller signal through the decision client request and verify the run is still current after every awaited decision before applying its returned card. Treat an aborted run as cancelled rather than continuing into later phases.

## Fix Focus Areas
- apps/web/src/hooks/useGuidedDemo.ts[227-231]
- apps/web/src/hooks/useGuidedDemo.ts[264-269]
- apps/web/src/api/client.ts[75-87]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Pending write invites appear read-only ✓ Resolved 🐞 Bug ≡ Correctness
Description
Invitation permission parsing checks only permissions.admin before defaulting to pull, so
invitations reporting { push: true } are emitted as read rather than write. This misstates
pending access for the newly added contract fixture and causes downstream policy, risk, and approval
logic—and revoke-generated undo invitations—to use pull-level access instead of the original push
permission.
Code

packages/connectors/src/github/connector.ts[R330-334]

+            const permission = String(
+              invitation.role_name ??
+                invitation.permission ??
+                (asObject(invitation.permissions).admin ? "admin" : "pull"),
+            );
Evidence
The contract fixture supplies permissions.push: true, but the fallback recognizes only the admin
flag and otherwise selects pull; the capability mapper then converts pull to read, whereas
push should map to write. Revoke derives the undo permission from that incorrectly stored
capability, so it also generates a pull-level invitation rather than restoring the original push
permission.

packages/connectors/src/github/connector.ts[330-344]
packages/connectors/src/github/tools.ts[26-42]
packages/connectors/src/github.contract.test.ts[123-130]
packages/core/src/policy/apply.ts[55-61]
packages/connectors/src/github.contract.test.ts[123-150]
packages/connectors/src/github/tools.ts[26-41]
packages/connectors/src/github/connector.ts[589-600]
packages/connectors/src/revoke-utils.ts[25-36]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Pending invitation permission objects are reduced to admin-or-pull, causing push-level and other supported permission levels to be lost and producing read-only grants.

## Issue Context
Normalize the effective invitation permission from supported string fields and object permission flags (`admin`, `maintain`, `push`, `triage`, and `pull`) with appropriate precedence before calling `githubPermissionToCapability`. Add an assertion that the existing push fixture produces `capability: "write"`, ensuring downstream policy, risk, approval, and revoke behavior retain the original permission level.

## Fix Focus Areas
- packages/connectors/src/github/connector.ts[330-344]
- packages/connectors/src/github.contract.test.ts[123-150]
- packages/connectors/src/github/tools.ts[26-42]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Invitation failures are suppressed ✓ Resolved 🐞 Bug ☼ Reliability
Description
The optional invitation inventory catches every McpToolError, even though that type also
represents authentication and permission failures, rate limiting, server or remote-service errors,
and missing connector configuration. These failures are silently treated as an unavailable optional
tool, removing all pending invitations from inventory and presenting an incomplete access audit as
successful.
Code

packages/connectors/src/github/connector.ts[R364-368]

+        } catch (error) {
+          // The shipped GitHub MCP may not expose invitations yet. Keep the
+          // existing collaborator inventory usable when this optional tool is
+          // unavailable.
+          if (!(error instanceof McpToolError)) throw error;
Evidence
The catch condition filters only by the broad McpToolError class, which has an optional status and
is not limited to unsupported-tool responses. The MCP caller uses the same error type for
unconfigured servers, HTTP failures such as 401/500, rate limiting, and JSON-RPC tool errors, so
failures from a configured invitation tool are indistinguishable at this catch site from an
unavailable optional tool and are discarded.

packages/connectors/src/github/connector.ts[364-369]
packages/connectors/src/mcp/types.ts[25-39]
packages/connectors/src/mcp/trueforge-caller.ts[31-39]
packages/connectors/src/mcp/trueforge-caller.ts[65-90]
docs/CONNECTORS.md[30-32]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Pending invitation inventory treats every MCP operational failure as if the optional invitation tool were unavailable, silently omitting invitation grants and representing an incomplete scan as successful.

## Issue Context
Only skip the optional invitation source for an explicit unsupported/not-found tool condition. Use available status and error details to distinguish that condition from missing configuration, authentication, authorization, rate-limit, timeout, server, and other remote-service failures, and propagate or surface those failures so the scan is not represented as complete.

## Fix Focus Areas
- packages/connectors/src/github/connector.ts[364-369]
- packages/connectors/src/mcp/trueforge-caller.ts[31-100]
- packages/connectors/src/mcp/types.ts[25-39]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: 🧠 Deep: This is a broad, behavior-heavy change spanning UI orchestration, streaming APIs, concurrency, connector inventory/revocation, registration scripts, and live-system teardown, creating many independent defect opportunities.

Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread packages/connectors/src/github/connector.ts Outdated
Comment thread apps/web/src/hooks/useGuidedDemo.ts Outdated
Comment thread apps/web/src/hooks/useGuidedDemo.ts
Comment thread packages/connectors/src/github/connector.ts Outdated
Discard superseded demo decisions, reset disposable demo audit state, handle every terminal scan status, preserve invitation capabilities, and surface real MCP inventory failures.

Co-authored-by: Cursor <cursoragent@cursor.com>
@GautamTalksDev
GautamTalksDev merged commit ba5b10a into main Aug 30, 2026
1 check passed
@GautamTalksDev
GautamTalksDev deleted the feat/guided-demo branch August 30, 2026 06:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant