feat: agent-native MVP public surface (WS1 + WS2 days 1–3)#44
Merged
Conversation
…WS2 day 2) Closes the agent loop end-to-end: the indexer now assembles agent.json per request from its own DB, an agent-sdk example fetches the manifest and submits a CreateRole through the per-org MeetingFactory, and the public org surface chips known agent addresses on members and role leads. "openProposals" stays empty and is documented as such — the proposal lifecycle isn't on-chain yet (Day 2 finding logged in the sprint doc), so the demo creates a role and the public view shows a Roles list instead of a proposals timeline. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…gent allowlist Extracts assembleOrgManifest / assembleOrgIndex into a pure manifest.ts so the assembly logic is testable offline (no ponder:api / ponder:schema imports). Adds 39 vitest specs covering bigint→decimal-string, the "most recent componentSet" join, null-not-undefined for absent factory addresses, JSON shape parity with agent-manifest-v1.md, deterministic ordering, large-bigint precision, and input immutability. Also adds 7 specs for isAgentAddress (case insensitivity, null/undefined/empty, known/unknown). Vitest configured on hollab-indexing using the workspace-hoisted vitest@2.1.9 — no new deps. Test totals across the workspace: 11 → 64 (manifest 39, agents 7, prior 18). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Day 2 finding (no proposal lifecycle on-chain yet) propagates into Day 3: PublicProposalView would render against an empty table. Day 3 deep-link surface becomes #/o/:orgId/r/:roleId — same flywheel mechanics on data that exists today. Proposal permalinks re-enter scope when GovernanceProcess proposal events ship. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Load-bearing prerequisite for the WS3 smoke test: deploys two wired demo orgs (paperclip, solarpunk) on top of DeployLocal. Each org gets its meeting components via MeetingComponentsFactory (wiring MeetingFactory as the governance process on the cloned RoleRegistry), the agent allowlist address added as a member, and roles created via MeetingFactory.executeGovernance(CreateRole). Paperclip elects the agent as lead on its first role so the public permalink renders a agent chip end-to-end from a clean seed. Roles live on circleId=0 because this contract set has no CircleRegistry — _createRole doesn't validate the circle id. Noted inline so it doesn't read as a bug next time. Verified end-to-end against a local anvil: ONCHAIN EXECUTION COMPLETE & SUCCESSFUL, paperclip=orgId 2, solarpunk=orgId 3. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… bigint query params
Three latent bugs that surfaced during the Day 3 WS3 smoke test, all
gating the public role-permalink flow end-to-end:
1. structural.ts was a stub — every RoleRegistry:Role* handler just
refreshed the org snapshot and never wrote to schema.role. The
role table was empty across every org, so listRolesByOrg and the
/agents/:orgId.json manifest always returned 0 roles. Pre-existing
— hidden because the authed StructureView reads roles from RPC via
useWorkspaceSnapshot, not from the indexer. Handlers now re-read
getRole + getRoleLeads from the clone and upsert, maintain
organization.roleCount, and handle RoleLeadAssigned /
RoleLeadUnassigned by updating only the leads array (with a fall-
back upsert if the role row hasn't been created yet due to replay
ordering).
2. The per-org manifest route was /agents/:orgId.json, which Hono 4.x
captures as an empty string — every request fell into the
BigInt("") → 0n → "org not found" branch. Replaced with
/agents/:file{[^/]+\.json} and strip the .json suffix manually.
3. LIST_ROLES_BY_ORG and LIST_CIRCLES_BY_ORG declared $orgId as
String! while the underlying column is bigint — same Day 1
episode as GET_ORGANIZATION (10824bb). Never surfaced before
because the role table was empty. Both now use BigInt!.
Added queries.spec.ts regression — asserts that GET_ORGANIZATION,
LIST_ROLES_BY_ORG, LIST_CIRCLES_BY_ORG, and
LIST_MEETING_COMPONENTS_BY_ORG all declare \$orgId: BigInt! as a
guard against future String! regressions.
Verified live: seeded orgs now show 3 roles on the public view with
the agent chip rendering on the Curator lead, and
/agents/2.json returns a complete manifest with roleCount: 3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Day 3 of the agent-native MVP sprint — the flywheel. Every governance object is now a wallet-less, shareable permalink an agent or a stranger can open cold. New routes on the public tree (all above the auth gate, zero wagmi, zero useAccount, zero RPC): - #/explore — global org directory backed by indexing-client.listOrganizations (not the manifest endpoint, so it stays on the typed GraphQL client the rest of the app already uses). Cards show ENS subname label, name, and member/circle/role counts. Sorted by updatedAt desc. - #/o/:orgId/r/:roleId — PublicRoleView renders name, purpose, domains, accountabilities, and leads with the agent chip when the lead is in the allowlist. Back-link to the parent org. useHashRouter gains explore and publicRole variants, with url-encoded roleId round-trip (encodeURIComponent preserves "-", survives "/" via %2F) and a malformed-fallback to public when /r has no roleId. 16 parse/stringify tests total (up from 7). PublicOrgView role cards now link to the permalink and gained a Join community CTA that navigates to #/join/:orgId — the join flow falls through the auth gate to Welcome where the existing WalletAuthControl/RainbowKit lives, keeping the public subtree literally zero-wagmi. Base OG + Twitter card metadata added to index.html so shared links preview as something other than a blank title. Sprint doc updated: Day 3 checkboxes closed with the call-outs on why explore fetches via listOrganizations vs. /agents/index.json (keeps the public tree on the typed client) and why the Join CTA delegates rather than mounting RainbowKit in-place. SeedDemoOrgs.s.sol struck from the carried-over list (shipped in the prior commit). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.
Summary
Day 2 of the agent-native MVP sprint (
docs/sprint-agent-native-mvp.md). Closes the agent read↔write loop end-to-end.apps/hollab-indexing/src/api/index.tsnow serves/agents/index.jsonand/agents/:orgId.jsonassembled per request from the Ponder DB via Drizzle (asc/eqre-exported from"ponder"— no extra dep). Per-org route returnsorg,contracts,circles,roles,memberswith stable ordering, joiningmeetingFactory+actionVotingfrommeetingComponentSet. BigInts serialized as decimal strings via a customJSON.stringifyreplacer.packages/agent-sdk/examples/propose-tension.ts— fetches the manifest, builds aHollabAgentagainstviem/chains.foundry, submitscreateRolethrough the per-orgMeetingFactory, polls the manifest until the role appears, and prints the SPA permalink. DefaultAGENT_PRIVATE_KEYis anvil account feat: add spec 08 for decentralized frontend deployment via IPFS + ENS #9.apps/hola-modern/src/config/agents.tsis the MVP allowlist (anvil account feat: add spec 08 for decentralized frontend deployment via IPFS + ENS #9).PublicOrgViewnow chips members and role leads when their address matches, plus a new Roles section with name / purpose / leads.packages/agent-sdk/docs/agent-manifest-v1.md(full v1 schema with rationale + known limitations) andpackages/agent-sdk/README.md(30-line quickstart).Day 2 finding —
openProposalsis empty by designThe Ponder schema has a
proposaltable, but no contract currently emits the events that populate it.MeetingFactory.executeGovernanceapplies role/policy changes directly toRoleRegistrywithout going through a tension/proposal lifecycle. Implications:propose-tension.ts) to match the sprint spec; the script header documents the gap.openProposals: []always — additive future-compat, populates without a version bump when the lifecycle ships.Logged inline in
docs/sprint-agent-native-mvp.md"Day 2 progress log".Test plan
pnpm check-types— clean across all 4 affected packagespnpm lint— 0 errors (4 pre-existing warnings, none mine)pnpm test— 11 passing (4 agent-sdk encoding + 7 useHashRouter)/agents/index.jsonregistered before/agents/:orgId.json, no Ponder reserved-route collision./scripts/dev-local.sh+pnpm --filter @hollab-io/agent-sdk tsx examples/propose-tension.ts <orgId>→ role appears on#/o/<orgId>with 🤖 chip. Held until the seed-orgs script lands so a fresh-clone test exercises the whole flow in one pass.🤖 Generated with Claude Code