Skip to content

feat(pop): mirror on-chain POP tasks into the KB (read-only catalog plugin) - #195

Open
hudsonhrh wants to merge 2 commits into
mainfrom
hudsonhrh/poa-cli-pr32-integration-plan
Open

feat(pop): mirror on-chain POP tasks into the KB (read-only catalog plugin)#195
hudsonhrh wants to merge 2 commits into
mainfrom
hudsonhrh/poa-cli-pr32-integration-plan

Conversation

@hudsonhrh

Copy link
Copy Markdown
Contributor

Type of Change

  • Feature skill - adds a channel or integration (source code changes + SKILL.md)
  • Utility skill - adds a standalone tool (code files in .claude/skills/<name>/, no source changes)
  • Operational/container skill - adds a workflow or agent skill (SKILL.md only, no source changes)
  • Fix - bug fix or security fix to source code
  • Simplification - reduces or simplifies source code
  • Documentation - docs, README, or CONTRIBUTING changes only

Description

Adds a pop catalog plugin that mirrors a POP (Perpetual Organization Protocol) org's on-chain tasks into context/tasks/POP-<chain>-<org>-<id>.md, so the reminder engine, PM orchestrator, digests and the kb-ui /projects page all work on on-chain work for free — it is read-only by construction, with POP_READONLY=1 hardwired into the spawned pop process so it cannot sign or broadcast even if a key were present, and no private key is involved anywhere. It depends on @poa-box/cli rather than @poa-box/core because the CLI declares ethers as a direct dependency (npm nests 5.7.2 under it and the host keeps ethers v6), whereas core declares it as a peer that npm would hoist to v6 and silently break. Three properties do the design work: chain is truth and each file carries its own pop_digest so there is no new table; absence is never deletion (POP has no change cursor, the subgraph lags 30+ ids, and its query is capped at 50 projects / 1000 tasks with no truncation signal — so a possibly-capped listing deletes nothing and a missing task is tombstoned across several exhaustive pulls first); and human edits survive, with only chain-owned frontmatter overwritten. Reads are two-tier — one task list per org per tick (~1.8s for 575 tasks) covers everything existing KB consumers need, while per-task task view adds description/submission/rejections/applications under a bounded, live-work-first budget whose remainder is logged rather than silently dropped, and terminal tasks are immutable on chain so each is deep-read exactly once. Chain-authored prose is fenced as untrusted input, since anyone who can create a task controls its title and description and real POP descriptions read like work orders.

Off by default (policy-closed catalog plugin) and dormant without config. Verified end-to-end against a live org (575 tasks, idempotent re-runs, failed and truncated pulls delete nothing, human edits survive a real merge); 110 plugin tests, 2137 repo-wide, typecheck clean. Eight review findings were validated against the bundled CLI's own source and fixed, each with a regression test confirmed by reverting the fix and watching the suite go red.

For Skills

  • SKILL.md contains instructions, not inline code (code goes in separate files)
  • SKILL.md is under 500 lines
  • I tested this skill on a fresh clone

…lugin)

Adds a `pop` catalog plugin that mirrors a POP (Perpetual Organization
Protocol) org's on-chain tasks into `context/tasks/POP-<chain>-<org>-<id>.md`,
so the reminder engine, PM orchestrator, digests and the kb-ui /projects page
all work on on-chain work for free.

Read-only by construction: `POP_READONLY=1` is hardwired into the spawned
`pop` process, which refuses to sign or broadcast even with a key present
(capability removal, not policy). No private key is involved anywhere.

Depends on `@poa-box/cli`, not `@poa-box/core` — the CLI declares ethers as a
direct dependency so npm nests 5.7.2 under it and the host keeps ethers v6;
core declares it as a peer, which npm would hoist to v6 and silently break its
427 v5-only call sites. Verified by install, and by driving the binary from an
ESM host.

Design (three properties do the work):

- Chain is truth, and the FILE is the state. Each POP-*.md carries its own
  `pop_digest`, so there is no new table and the KB survives a DB restore.
- Absence is never deletion. POP has no change cursor (updateTask/edit-meta/
  cancelTask/claimTask bump no timestamp), the subgraph lags 30+ ids, and its
  query is capped at 50 projects / 1000 tasks with no truncation signal. So a
  possibly-capped listing deletes nothing, and a genuinely missing task is
  tombstoned for N consecutive exhaustive pulls before removal.
- Human edits survive. Chain-owned frontmatter is overwritten; priority, custom
  tags, visibility and prose below the managed marker are preserved, following
  the mergeFrontmatter contract from discord-members-sync.

Two read tiers: one `task list` per org per tick (~1.8s for 575 tasks) supplies
everything existing KB consumers need; per-task `task view` adds description,
submission, rejections and applications under a bounded, live-work-first budget
whose remainder is logged rather than silently dropped. Terminal tasks are
immutable on chain, so each is deep-read exactly once — that is what makes
mirroring a 575-task archive affordable.

Chain-authored prose is fenced as untrusted input. Anyone who can create a task
controls its title and description, real POP descriptions read like work orders,
and they land where an agent reads them; markers smuggled inside the text are
neutralised rather than dropped.

Off by default (policy-closed catalog plugin) and dormant without config.
Verified end-to-end against a live org: 575 tasks, idempotent re-runs, failed
and truncated pulls delete nothing, and human edits survive a real merge.

Co-Authored-By: Claude <noreply@anthropic.com>
@hudsonhrh
hudsonhrh force-pushed the hudsonhrh/poa-cli-pr32-integration-plan branch from a592436 to eb9a42c Compare August 4, 2026 17:25
The new-org guide never mentioned catalog plugins, so there was no written
answer to "does adopting labor.fun mean adopting POP?" (it does not — the
catalog is dark until an org lists an id in enabledPlugins). Adds that section
plus the POP example, and calls out the gating footgun: adding `enabledPlugins`
at all — even as `[]` — flips gating ON, so an org with existing profile-dir
plugins must list those too or they silently stop registering.

Also warns at startup when an org omits `orgId`. It is optional today and falls
back to the org NAME in the task identity URL, but that URL is meant to be
immutable — it becomes the Linear attachment join key, and `attachmentsForURL`
is stateless precisely because it never changes. Setting the real hex id later
would rewrite every task's `pop_url` and orphan any link already attached.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant