Skip to content

Fix/pagination#395

Open
toadkicker wants to merge 38 commits into
mainfrom
fix/pagination
Open

Fix/pagination#395
toadkicker wants to merge 38 commits into
mainfrom
fix/pagination

Conversation

@toadkicker
Copy link
Copy Markdown
Contributor

Fixes pagination in the registry and removes extraneous files

Todd Baur and others added 30 commits May 6, 2026 16:43
Extract and visualize complete PAP codebase architecture as navigable knowledge graph.

Key outputs:
- graph.html: Interactive visualization (vis.js) with 99.3% connectivity
- GRAPH_REPORT.md: Deep architectural analysis with 5-layer spine
- .graphify_extract.json: Full graph data (141 nodes, 190 edges, 22 hyperedges)
- .graphify_communities.json: 10 communities via Louvain clustering
- .graphify_labels.json: Semantic community labels

Analysis reveals:
- Protocol-first design with Mandate/Session/Receipt core
- Two-boundary security model (SD-JWT + OS sandbox)
- Schema.org vocabulary driving UI rendering
- Multi-language bindings (C, C++, C#, Java, Python, TypeScript)
- Federation-ready architecture with Chrysalis registry

99.3% connectivity achieved through strategic bridge discovery across:
- Docs ↔ implementation
- FFI ↔ core protocol
- UI ↔ orchestrator
- Tests ↔ runtime

Extraction cost: 558,894 input / 68,938 output tokens

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Correct graph classification: six-phase handshake IS PAP itself, not a
transport-specific pattern. WebSocket and OHTTP are transport layers that
carry PAP sessions, not define them.

Changes:
- Moved SixPhaseHandshake from transport to protocol layer
- Removed from TransportAbstraction hyperedge
- Added Session -> SixPhaseHandshake (protocol implements handshake)
- Added WebSocketTransport -> Session (transport carries protocol)
- Added OHTTPRelay -> Session (alternative transport)
- Updated report to emphasize transport-agnostic design

The graph now correctly shows PAP as protocol-first with pluggable
transport implementations.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Key features:
- Tab bar for canvas navigation (4 visible + overflow)
- Workflow panel for agent curation + disclosure forms
- Toast notifications for approval requests
- Ghost blocks as aggregated result previews
- Dynamic form generation from AgentAdvertisement schema

Builds on existing CanvasState, IntentPlan, and approval flow infrastructure.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements browser-style tab navigation for canvases with:
- First 4 canvases displayed as visible tabs (MRU sorted)
- Overflow dropdown for remaining canvases
- Active state indication
- Tab close buttons with stop_propagation
- New canvas button
- Relative timestamp formatting in overflow menu

Component follows Leptos patterns from topbar.rs:
- Uses expect_context for CanvasState
- Reactive closures for For/Show
- Clone-before-closure for event handlers
- Memo::new() for derived state

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Active tab with purple bottom border
- Hover states with purple muted background
- Close button hidden by default, visible on hover
- Overflow dropdown with shadow and z-index

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Adds the CanvasTabBar component to the TopBar view, positioning it
between the header and backdrop elements as a sibling to the topbar.
This enables browser-style tab navigation for canvases directly below
the address bar.

Changes:
- Import CanvasTabBar component in topbar.rs
- Add <CanvasTabBar /> after </header> element
- Tab bar now appears in correct layout position

Part of Papillon Intent Browser UI implementation (Task 3).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Task 4: Workflow Panel State management.

- Added workflow_panel_open: RwSignal<bool> to CanvasState struct
- Initialized to false (closed) in Default impl
- Will be toggled by toast clicks and Ctrl+\ keyboard shortcut

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Task 5 complete. Created WorkflowPanel component with:
- Slide-in drawer from right (backdrop + panel)
- Header with title and close button
- Three sections with placeholders (chat, curation, disclosure)
- Footer with disabled execute button
- Reads workflow_panel_open from CanvasState

Exported in components/mod.rs. Ready for styling (Task 6).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Task 6: Workflow Panel Styles

- Backdrop: Fixed overlay with fade-in animation
- Panel: 400px right drawer with slide-in animation (250ms)
- Header: Flex layout with close button and purple hover states
- Body: Scrollable content area with section styling
- Sections: Agent info, disclosure list, returns schema, mandate info
- Footer: Approve/reject button styling with purple accent
- Animations: translateX(100%) → translateX(0) for panel slide
- Z-index: backdrop 999, panel 1000

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add WorkflowPanel component to canvas.rs as an overlay.
Panel appears/disappears based on workflow_panel_open signal.
Positioned as final child inside .canvas-page div for proper z-index layering.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- WorkflowChatThread component reads from canvas_state.canvas_messages
- ChatMessage subcomponent with user vs assistant styling
- Empty state when no messages exist
- Replaced placeholder in WorkflowPanel with actual component
- Border colors: purple for user, teal for assistant

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Checkbox selection for agents
- On-device trust badge (teal)
- DID truncation for readability
- Property count display
- Selected state with purple highlight

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Task 10 - dynamic form generation from selected agents' requirements.
- DisclosureForm component computes union of requires_disclosure from selected agents
- DisclosureField subcomponent with field type inference (email, date, url, tel, text)
- Property humanization (strip schema:, capitalize, handle dot notation)
- Form values stored in local HashMap signal
- Approve button shows agent count and disables when no agents selected
- Clean Wing spectrum styling with purple accent

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Integrated AgentCurationList and DisclosureForm into WorkflowPanel with shared state:
- Added placeholder active_plan signal (RwSignal<Option<IntentPlan>>)
- Created shared selected_agents signal that flows from WorkflowPanel to both child components
- Modified AgentCurationList to accept selected_agents as prop instead of managing internal state
- Wrapped workflow body in Show component with "No active plan" fallback
- Added .workflow-no-plan CSS style for empty state display
- All three components (chat, curation, disclosure) now render when plan exists

Task 17 will wire active_plan to real state from canvas/orchestrator.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Task 12 of the Papillon Intent Browser UI plan.

Created `ApprovalToastStack` component that renders fixed bottom-right toast
notifications for approval requests. Toasts read from `canvas_state.hitl_pending`
and provide two interaction modes:

- **Zero-disclosure agents**: Quick APPROVE button for instant approval
- **Multi-property disclosure**: VIEW DETAILS button opens workflow panel

Key features:
- Slide-in animation from bottom (250ms ease-out)
- Dismiss button (×) clears pending request
- Risk level badges (HIGH/CRITICAL) styled with semantic colors
- Humanized action display (strips "schema:" and "Action" suffix)
- Full unit test coverage for humanize_action logic

Styling follows established patterns from workflow panel (z-index 2000,
uses design system colors and spacing). Backend approval wiring deferred
to Task 18.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add ApprovalToastStack component to canvas page for workflow approval notifications.
- Import ApprovalToastStack component
- Place after WorkflowPanel to overlay canvas content
- Toast will be triggered by hitl_pending signal (backend wiring in Task 18)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Shows schema.org type icon and name
- Agent count indicator
- Type-specific skeletons (Flight, Weather, Article, Generic)
- Block character values (████) for privacy
- Dashed purple border for preview state

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement keyboard shortcuts to enhance canvas navigation UX:
- Ctrl+T: Create new canvas
- Ctrl+W: Close current canvas
- Ctrl+\: Toggle workflow panel
- Ctrl+Tab: Cycle to next canvas

Added cycle_canvas_forward helper that navigates through canvases
in sorted order (by updated_at), wrapping around to the first when
reaching the end. Keyboard handler attached to canvas-page div with
tabindex="0" to enable focus and key event capture.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add active_intent_plan signal to CanvasState and wire it to WorkflowPanel, replacing the local placeholder signal. This enables the workflow panel to display the real intent plan data that will be populated by the canvas_plan_prompt backend command (Task 18).

Changes:
- Add active_intent_plan: RwSignal<Option<IntentPlan>> to CanvasState
- Initialize active_intent_plan: RwSignal::new(None) in Default impl
- Replace local placeholder signal in WorkflowPanel with canvas_state.active_intent_plan
- Remove unused IntentPlan import from workflow_panel.rs

Backend integration (canvas_plan_prompt → active_intent_plan) comes in Task 18.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Create frontend command wrapper for canvas_approve_block Tauri command
and wire disclosure form approval button to call it.

Changes:
- Created frontend/src/commands/approval.rs with ApprovalPayload and SignedChallenge structs
- Created frontend/src/commands/mod.rs to export approval module
- Added commands module to frontend/src/lib.rs
- Updated disclosure_form.rs to call approve_intent_plan on button click
- On approval success: close workflow panel and clear active_intent_plan
- On error: log to console (proper error handling in follow-up)

Note: Block ID and challenge signature are placeholders for now - will be
properly wired in follow-up tasks when identity challenge flow is integrated.

Task 18 complete.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed 3 CSS integration bugs identified by EvidenceQA:

1. Canvas Tab New Button: Changed class from 'canvas-tab-new' to 'new-tab-btn'
   to match CSS definition in main.css (line 9381)

2. Canvas Tab Overflow Dropdown: Updated all overflow-related classes to match CSS:
   - canvas-tab-overflow → overflow-dropdown-container
   - canvas-tab-overflow-btn → overflow-dropdown-toggle
   - canvas-tab-overflow-menu → overflow-dropdown-menu
   - canvas-tab-overflow-item → overflow-dropdown-item

3. Redundant Disabled Button: Removed duplicate "Disclose & Execute" button
   from workflow_panel.rs footer. The functional button with proper wiring
   exists in disclosure_form.rs; the footer button was non-functional.

All component classes now align with CSS selectors, ensuring proper styling.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Replace create_memo with Memo::new (agent_curation_list.rs:48, disclosure_form.rs:18,38,133)
- Replace create_signal with signal (disclosure_form.rs:15)
- Resolves all 5 Leptos deprecation warnings from integration testing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add .canvas-tabs CSS with display: flex
- Tabs now display horizontally instead of stacking vertically
- Fixes layout bug where tabs appeared in vertical column
8-task implementation plan for multi-agent containers:
- Schema signatures for I/O contracts
- Block containers with visual ports
- Agent selector for multi-agent blocks
- BM25 intent routing (LLM-optional)
- Node-graph wiring with disclosure validation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- input_types and output_types define agent I/O contract
- can_wire_to() validates connections (subset check)
- matches() ensures agents share same signature in container
- from_agent() extracts signature from AgentInfo

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add three missing tests identified in code review:
- test_matches_identical_signatures: verifies matches() returns true for identical signatures
- test_matches_different_signatures: verifies matches() returns false when input_types or output_types differ
- test_cannot_wire_partial_overlap: verifies can_wire_to() returns false when source outputs only partial required inputs

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- BlockContainer holds N agents with same SchemaSignature
- BlockPosition for 2D canvas layout
- BlockConnection for wired data flow
- WiringError for connection validation
- CanvasBlock.container_id links to container

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- BlockInputPort and BlockOutputPort for visual wiring
- Port circles change color when connected
- Schema type labels stripped of 'schema:' prefix
- CSS positions ports on block edges with hover states

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- AgentSelector shows compatible agents for block
- Toggle selection via checkbox
- Selected state with purple border
- Provider name shown below agent name
- Scrollable list with max 300px height

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Todd Baur and others added 5 commits May 14, 2026 19:19
- BlockContainerView shows multi-agent block
- Input/output ports attached to edges
- Schema type badges with color coding (teal input, gold output)
- Agent count display
- Positioned absolutely for node-graph layout

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Canvas checks for block.container_id field
- BlockContainerView rendering path (fallback to legacy for now)
- Graph mode CSS with grid background
- Backward compatible with existing BlockRenderer

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Uses BM25 IntentIndex to classify prompt
- Derives SchemaSignature from action type
- Filters agents by matching signature
- Returns BlockContainer with compatible agents
- Default position (100, 100)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- connect_blocks validates signature compatibility
- disconnect_blocks removes connections
- Uses SchemaSignature::can_wire_to() for validation
- Prevents cross-canvas connections
- TODO: Database persistence for connections

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Comment thread graphify-out/graph.html
<html>
<head>
<title>PAP Knowledge Graph - 99.3% Connected</title>
<script src="https://unpkg.com/vis-network@9.1.2/dist/vis-network.min.js"></script>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 19, 2026

Greptile Summary

This PR fixes the registry agents pagination (adding explicit braces around Leptos closure props) and bundles a large set of canvas/block-container additions including new shared types, frontend components, and a Tauri command for creating block containers from prompts. It also introduces a credentials table migration and a CredentialEntry CRUD layer in RegistryStore.

  • Pagination fix (agents.rs): wraps disabled and on:click closures in {} to satisfy Leptos attribute parsing — targeted and correct.
  • Credentials layer (db/mod.rs, migrations): declares CredentialEntry/CredentialsPage types and RegistryStore dispatch methods, but never adds the corresponding method bodies to SqliteStore or PostgresStore, leaving the build broken; the payload column also stores secrets as plaintext.
  • Canvas block containers (block_container.rs, container.rs, new frontend components): introduces BlockContainer, wiring types, AgentCurationList/AgentSelector UI components, and canvas state signals — all new, with the main concern being that create_block_container hard-codes the canvas position to (100, 100) for every container.

Confidence Score: 1/5

Not safe to merge — the credential dispatch layer calls methods on SqliteStore and PostgresStore that do not exist, so the crate will not compile as written.

The RegistryStore credential methods delegate to SqliteStore/PostgresStore implementations that were never added. This is a build-breaking omission affecting every consumer of the registry crate. Additionally, the credentials migration stores secrets and tokens as cleartext, which is a meaningful security regression if the feature ships. The pagination fix itself is correct, but it is bundled with incomplete credential work that prevents the build from succeeding.

apps/registry/src/db/sqlite.rs and apps/registry/src/db/postgres.rs need the four credential method bodies added; apps/registry/src/db/migrations/sqlite/0004_add_credentials.sql needs an AFTER UPDATE trigger and application-layer payload encryption consideration.

Security Review

  • Plaintext credential storage (apps/registry/src/db/migrations/sqlite/0004_add_credentials.sql, postgres/0004_add_credentials.sql): The payload column stores secrets, API tokens, and verifiable credentials as raw TEXT with no encryption. Anyone with direct database read access can extract all stored credentials in cleartext.

Important Files Changed

Filename Overview
apps/registry/src/db/mod.rs Adds CredentialEntry/CredentialsPage types and RegistryStore dispatch methods for CRUD, but the delegate calls reference methods that don't exist on SqliteStore or PostgresStore — will not compile.
apps/registry/src/db/sqlite.rs Import updated to include CredentialEntry/CredentialsPage, but no credential method implementations are present; backing methods called by RegistryStore are missing entirely.
apps/registry/src/db/postgres.rs Same as sqlite.rs — imports updated but credential method bodies absent, leaving RegistryStore dispatch unresolved at compile time.
apps/registry/src/db/migrations/sqlite/0004_add_credentials.sql Adds credentials table for secrets/tokens/VCs with plaintext TEXT payload and no UPDATE trigger to refresh updated_at.
apps/registry/src/db/migrations/postgres/0004_add_credentials.sql Postgres counterpart of the credentials migration; same plaintext payload concern, though Postgres triggers are typically added separately.
apps/registry/src/ui/pages/agents.rs Core pagination fix: adds braces around closures in Leptos disabled/on:click props, correcting the original attribute-parsing issue.
apps/papillon/src/commands/canvas/container.rs New Tauri command to create block containers from an intent; hard-codes position at (100,100) causing all new containers to overlap.
crates/papillon-shared/src/block_container.rs New shared types for BlockContainer, BlockPosition, BlockConnection, and WiringError with unit tests; straightforward and correct.
apps/papillon/frontend/src/components/agent_curation_list.rs New Leptos component for multi-agent curation with checkbox selection; initialization logic is gated on is_empty() to avoid overwriting user choices.
apps/papillon/frontend/src/state/canvas.rs Adds workflow_panel_open and active_intent_plan signals to CanvasState, and container_id field to block construction sites.
crates/papillon-shared/src/types.rs Adds optional container_id field to CanvasBlock with serde default; all construction sites in tests updated to include the new field.

Sequence Diagram

sequenceDiagram
    participant UI as AgentsPage (Leptos)
    participant API as registry API
    participant Store as RegistryStore
    participant SQLite as SqliteStore
    participant PG as PostgresStore

    UI->>API: list_agents(query, page, per_page)
    API->>Store: search_agents(q, ver, page, per_page)
    Store->>SQLite: search_agents(...) [SQLite path]
    SQLite-->>Store: "AgentsPage {items, total, total_pages}"
    Store-->>API: AgentsPage
    API-->>UI: AgentsListResponse
    UI->>UI: render pagination (fixed braces)

    Note over Store,SQLite: Credential methods declared in RegistryStore...
    Store->>SQLite: list_credentials(...) method missing
    Store->>PG: list_credentials(...) method missing
Loading

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
apps/registry/src/db/mod.rs:188-223
**Missing method implementations on backing stores**

`RegistryStore` dispatches `list_credentials`, `insert_credential`, `update_credential`, and `delete_credential` to `SqliteStore` and `PostgresStore`, but neither store implements those methods — only the import line was updated in each file. Rust will fail to compile because `s.list_credentials(...)` and friends are called on types that have no such method defined.

### Issue 2 of 4
apps/registry/src/db/migrations/sqlite/0004_add_credentials.sql:4-11
**Plaintext secret storage**

The `payload` column stores secrets, API tokens, and verifiable credentials as plaintext `TEXT`. Any user with read access to the SQLite file or Postgres database can extract all credentials without any additional secret. Consider encrypting the payload at the application layer (e.g., with a key derived from a master secret) before writing to this column, and never log or serialize its contents directly.

### Issue 3 of 4
apps/registry/src/db/migrations/sqlite/0004_add_credentials.sql:9
**`updated_at` never refreshed on UPDATE**

The `updated_at` column defaults to the creation timestamp but there is no trigger to update it when a row is modified. On SQLite, `DEFAULT (strftime(...))` only fires on `INSERT`. Add an `AFTER UPDATE` trigger so `update_credential` actually reflects the modification time; otherwise every consumer of this column will see stale data.

### Issue 4 of 4
apps/papillon/src/commands/canvas/container.rs:88-91
**All containers placed at the same hard-coded position**

Every call to `create_block_container` places the new container at `x: 100.0, y: 100.0`. When a user creates two or more containers they will be stacked exactly on top of each other with no way to distinguish them visually. Pass the desired position in from the caller or at minimum apply a small randomised or incrementing offset.

Reviews (1): Last reviewed commit: "cleanup of formatting/unneccesary files" | Re-trigger Greptile

Comment on lines +188 to 223

// ── Credentials ──────────────────────────────────────────────────────────

pub async fn list_credentials(
&self,
q: Option<&str>,
page: u32,
per_page: u32,
) -> Result<CredentialsPage> {
match self {
RegistryStore::Sqlite(s) => s.list_credentials(q, page, per_page).await,
RegistryStore::Postgres(p) => p.list_credentials(q, page, per_page).await,
}
}

pub async fn insert_credential(&self, entry: &CredentialEntry) -> Result<i64> {
match self {
RegistryStore::Sqlite(s) => s.insert_credential(entry).await,
RegistryStore::Postgres(p) => p.insert_credential(entry).await,
}
}

pub async fn update_credential(&self, entry: &CredentialEntry) -> Result<bool> {
match self {
RegistryStore::Sqlite(s) => s.update_credential(entry).await,
RegistryStore::Postgres(p) => p.update_credential(entry).await,
}
}

pub async fn delete_credential(&self, id: i64) -> Result<bool> {
match self {
RegistryStore::Sqlite(s) => s.delete_credential(id).await,
RegistryStore::Postgres(p) => p.delete_credential(id).await,
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Missing method implementations on backing stores

RegistryStore dispatches list_credentials, insert_credential, update_credential, and delete_credential to SqliteStore and PostgresStore, but neither store implements those methods — only the import line was updated in each file. Rust will fail to compile because s.list_credentials(...) and friends are called on types that have no such method defined.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/registry/src/db/mod.rs
Line: 188-223

Comment:
**Missing method implementations on backing stores**

`RegistryStore` dispatches `list_credentials`, `insert_credential`, `update_credential`, and `delete_credential` to `SqliteStore` and `PostgresStore`, but neither store implements those methods — only the import line was updated in each file. Rust will fail to compile because `s.list_credentials(...)` and friends are called on types that have no such method defined.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

Comment on lines +4 to +11
name TEXT NOT NULL,
kind TEXT NOT NULL DEFAULT 'api_token',
payload TEXT NOT NULL DEFAULT '{}',
schema_type TEXT,
issuer_did TEXT,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now')),
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now')),
expires_at TEXT
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Plaintext secret storage

The payload column stores secrets, API tokens, and verifiable credentials as plaintext TEXT. Any user with read access to the SQLite file or Postgres database can extract all credentials without any additional secret. Consider encrypting the payload at the application layer (e.g., with a key derived from a master secret) before writing to this column, and never log or serialize its contents directly.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/registry/src/db/migrations/sqlite/0004_add_credentials.sql
Line: 4-11

Comment:
**Plaintext secret storage**

The `payload` column stores secrets, API tokens, and verifiable credentials as plaintext `TEXT`. Any user with read access to the SQLite file or Postgres database can extract all credentials without any additional secret. Consider encrypting the payload at the application layer (e.g., with a key derived from a master secret) before writing to this column, and never log or serialize its contents directly.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

payload TEXT NOT NULL DEFAULT '{}',
schema_type TEXT,
issuer_did TEXT,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now')),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 updated_at never refreshed on UPDATE

The updated_at column defaults to the creation timestamp but there is no trigger to update it when a row is modified. On SQLite, DEFAULT (strftime(...)) only fires on INSERT. Add an AFTER UPDATE trigger so update_credential actually reflects the modification time; otherwise every consumer of this column will see stale data.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/registry/src/db/migrations/sqlite/0004_add_credentials.sql
Line: 9

Comment:
**`updated_at` never refreshed on UPDATE**

The `updated_at` column defaults to the creation timestamp but there is no trigger to update it when a row is modified. On SQLite, `DEFAULT (strftime(...))` only fires on `INSERT`. Add an `AFTER UPDATE` trigger so `update_credential` actually reflects the modification time; otherwise every consumer of this column will see stale data.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

Comment on lines +88 to +91
mod tests {
use super::*;

#[test]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 All containers placed at the same hard-coded position

Every call to create_block_container places the new container at x: 100.0, y: 100.0. When a user creates two or more containers they will be stacked exactly on top of each other with no way to distinguish them visually. Pass the desired position in from the caller or at minimum apply a small randomised or incrementing offset.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/papillon/src/commands/canvas/container.rs
Line: 88-91

Comment:
**All containers placed at the same hard-coded position**

Every call to `create_block_container` places the new container at `x: 100.0, y: 100.0`. When a user creates two or more containers they will be stacked exactly on top of each other with no way to distinguish them visually. Pass the desired position in from the caller or at minimum apply a small randomised or incrementing offset.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 19, 2026

Benchmark Regression Report

PAP Protocol Benchmark Regression Check
========================================
Baseline: .bench-baseline/baseline.json
Threshold: 55%

  ed25519_keypair_generation                19.6 µs  (baseline: 19.7 µs, -0.4%)  [ok]
  did_key_derivation                         1.5 µs  (baseline: 1.5 µs, +1.7%)  [ok]
  mandate_create_sign                       23.9 µs  (baseline: 23.9 µs, +0.0%)  [ok]
  mandate_chain_verify_depth3              157.0 µs  (baseline: 150.0 µs, +4.6%)  [ok]
  sd_jwt_issue_5claims                      28.1 µs  (baseline: 27.8 µs, +1.0%)  [ok]
  sd_jwt_verify_disclose_3of5               54.1 µs  (baseline: 50.9 µs, +6.1%)  [ok]
  session_open_full_lifecycle              108.2 µs  (baseline: 114.1 µs, -5.2%)  [ok]
  receipt_create_cosign                     48.2 µs  (baseline: 48.1 µs, +0.1%)  [ok]
  federation_announce_local                 68.6 µs  (baseline: 62.2 µs, +10.3%)  [ok]


P99 Tail-Latency Check
----------------------
Results: target/p99_results.json
Threshold: 50%

  session_open_full_lifecycle(p99)         120.1 µs  (baseline: 500.0 µs, -75.9%)  [ok]
  mandate_chain_verify_depth3(p99)         163.7 µs  (baseline: 480.0 µs, -65.9%)  [ok]
  receipt_create_cosign(p99)                57.8 µs  (baseline: 210.0 µs, -72.4%)  [ok]

All benchmarks within 55% of baseline.

Threshold: 10% regression vs baseline from main

Todd Baur and others added 3 commits May 19, 2026 12:08
- Implement missing credential CRUD methods on SqliteStore and PostgresStore
  (list_credentials, insert_credential, update_credential, delete_credential)
- Add security note to credential migrations: payload must be ciphertext,
  encrypted at the application layer before insertion
- Add AFTER UPDATE trigger (SQLite) and BEFORE UPDATE trigger + function
  (Postgres) to auto-refresh credentials.updated_at on every modification
- Change create_block_container to accept optional position parameter;
  default now applies a small randomised offset so containers no longer
  stack exactly at (100, 100)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- cargo fmt --all (import ordering, line wrapping in papillon-shared)
- Fix unused vars in container.rs (prompt -> _prompt)
- Fix unused vars/imports in wiring.rs (state/_state, super::*, params)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Remove non-existent e2e/package.json from hashFiles cache keys
  (file lives at apps/papillon/e2e/package.json, not repo root)
- Add missing Playwright browser cache to federation-e2e job
- Switch federation-e2e Node cache to package-lock.json for consistency

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@luisllaver
Copy link
Copy Markdown

"Related to this issue, we should consider integrating the AURA Open Protocol's trust-check primitive (GET https://agent.auraopenprotocol.org/check) to provide a more robust trust assessment for agents in the registry. This can be done by calling the trust-check endpoint from the beforeSettle hook, as described in the AURA Open Protocol documentation (https://dev.auraopenprotocol.org). By doing so, we can ensure that agents with a strong reputation baseline are prioritized, and trust is established through real interaction history. Would love to discuss further how this can beEnvelope integrated into the fix."

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.

3 participants