Skip to content

JUST FOR CI RUN: feat: custom proposal templates (authoring + fill + opt-in gating) - #997

Closed
frol wants to merge 3 commits into
mainfrom
feat/custom-proposal-templates
Closed

JUST FOR CI RUN: feat: custom proposal templates (authoring + fill + opt-in gating)#997
frol wants to merge 3 commits into
mainfrom
feat/custom-proposal-templates

Conversation

@frol

@frol frol commented Jun 29, 2026

Copy link
Copy Markdown
Member

It is a fork of #993 pulled into the repo to run CI for it.

Reusable templates for arbitrary FunctionCall proposals: a manifest DSL, Visual/Code authoring, a generated fill form, and per-treasury opt-in. ChangePolicy-gated writes; the produced proposal is an ordinary FunctionCall. Includes nt-be CRUD API + tests, nt-fe engine/UI + unit + e2e, docs and an authoring skill. Logging uses tracing to match the backend.
Copilot AI review requested due to automatic review settings June 29, 2026 14:27

Copilot AI left a comment

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.

Pull request overview

Introduces an opt-in “Custom Requests / Proposal Templates” feature that lets DAOs author manifest-driven reusable forms which file arbitrary SputnikDAO FunctionCall proposals, spanning new frontend authoring/fill UI, sidebar/navigation surfaces, and backend storage + CRUD/flag endpoints.

Changes:

  • Added a proposal-template manifest DSL (frontend validation + placeholder engine) and a Code/Visual authoring experience, plus a runtime form renderer to file proposals from templates.
  • Added an opt-in per-treasury “Custom Requests” flag (Settings → Developer), onboarding tour cue, and a sidebar “Request Templates” section with pinned templates.
  • Added backend migrations + endpoints for proposal template CRUD and the custom-requests flag, plus supporting test utilities and auth error mapping improvements.

Reviewed changes

Copilot reviewed 56 out of 64 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
skills/trezu-custom-proposal-template/SKILL.md Portable skill prompt for generating manifest JSON without repo access
skills/trezu-custom-proposal-template/README.md Installation/usage instructions for the portable skill
nt-fe/features/proposal-templates/types.ts Frontend ProposalTemplate DTO type
nt-fe/features/proposal-templates/manifest.ts Zod schema + placeholder extraction/substitution + manifest parsing utilities
nt-fe/features/proposal-templates/manifest.test.ts Unit tests for manifest validation/parsing/placeholder behavior
nt-fe/features/proposal-templates/hooks/use-proposal-templates.ts React Query hook for listing templates (session/guest gated)
nt-fe/features/proposal-templates/hooks/use-proposal-template-mutations.ts Create/update/delete mutations with targeted cache invalidation
nt-fe/features/proposal-templates/hooks/use-custom-requests-enabled.ts React Query hook + mutation for the opt-in feature flag
nt-fe/features/proposal-templates/form-schema.ts Builds RHF/Zod schema + default values from a validated manifest
nt-fe/features/proposal-templates/form-schema.test.ts Unit tests for form-schema validators/default coercion
nt-fe/features/proposal-templates/error-map.ts Maps path: message errors to inline UI messages / routing rules
nt-fe/features/proposal-templates/error-map.test.ts Unit tests for error routing/cleaning logic
nt-fe/features/proposal-templates/draft.ts Visual-builder draft model + lossless manifest round-tripping + normalization
nt-fe/features/proposal-templates/draft.test.ts Unit tests for draft round-tripping + normalization rules
nt-fe/features/proposal-templates/components/visual-builder.tsx Visual authoring UI sections + error gating/routing
nt-fe/features/proposal-templates/components/touched-gating.test.tsx Tests for “no errors until touched” behavior on initial render
nt-fe/features/proposal-templates/components/template-editor.tsx Shared create/edit editor with Code/Visual toggle + validation
nt-fe/features/proposal-templates/components/manifest-form.tsx Runtime manifest-to-form renderer for filing proposals
nt-fe/features/proposal-templates/build-proposal.ts Engine building { kind, description } from manifest + filled values
nt-fe/features/proposal-templates/build-proposal.test.ts Unit tests for interpolation + proposal building/tagging
nt-fe/features/proposal-templates/args-node.ts Args-tree editor helpers (type selection, dynamic arg detection, dupes)
nt-fe/features/proposal-templates/args-node.test.ts Unit tests for args-node helpers
nt-fe/features/proposal-templates/api.ts Axios API client for templates + custom-requests endpoints
nt-fe/features/onboarding/steps/page-tours.tsx Added Request Templates onboarding tour definition/IDs
nt-fe/features/onboarding/steps/index.tsx Registered the new tour in the global tours list
nt-fe/components/sidebar.tsx Added Request Templates nav section + pinned templates subtree
nt-fe/components/animate-ui/icons/code-xml.tsx Added animated CodeXml icon for the sidebar entry
nt-fe/app/(treasury)/[treasuryId]/settings/page.tsx Added Developer tab visibility logic tied to Custom Requests opt-in
nt-fe/app/(treasury)/[treasuryId]/settings/components/developer-tab.tsx Developer tab UI to enable/disable Custom Requests + trigger tour
nt-fe/app/(treasury)/[treasuryId]/custom-templates/layout.tsx Metadata/layout wrapper for the custom templates section
nt-fe/app/(treasury)/[treasuryId]/custom-templates/page.tsx Templates index page with pin/edit/delete actions
nt-fe/app/(treasury)/[treasuryId]/custom-templates/create/page.tsx Create template page using shared editor + create mutation
nt-fe/app/(treasury)/[treasuryId]/custom-templates/[slug]/page.tsx Fill-and-file page resolving template by manifest slug
nt-fe/app/(treasury)/[treasuryId]/custom-templates/[slug]/edit/page.tsx Edit/delete template page using shared editor + update/delete mutations
nt-be/src/handlers/proposal_templates.rs Backend CRUD handlers + structural manifest validation/normalization
nt-be/src/utils/test_utils.rs Added shared policy/treasury/auth test scaffolding helpers
nt-be/src/routes/mod.rs Wired proposal-templates and custom-requests routes
nt-be/src/handlers/treasury/policy.rs Centralized treasury policy cache key for reuse in tests
nt-be/src/handlers/treasury/mod.rs Registered the new custom_requests handler module
nt-be/src/handlers/treasury/custom_requests.rs Backend GET/PUT for Custom Requests flag + tests
nt-be/src/handlers/mod.rs Registered proposal_templates handler module
nt-be/src/handlers/address_book.rs Fixed sqlx nullability override for created_by_wallet + regression test
nt-be/src/auth/middleware.rs Added verify_dao_member_for_http helper + tests for policy/cache behavior
nt-be/src/auth/error.rs Added status_and_message helper for consistent HTTP mapping
nt-be/migrations/20260619000001_create_proposal_templates.sql Created proposal_templates table (manifest_id generated column, indexes)
nt-be/migrations/20260627000001_add_custom_requests_enabled.sql Added per-treasury custom_requests_enabled flag
nt-be/migrations/20260627000002_add_pinned_to_proposal_templates.sql Added pinned column to proposal_templates
nt-be/.sqlx/query-fd881f5e05e9494b1ca87f66d411ed04133e0d66df5d949b10f5eb78b0676c37.json sqlx query metadata for proposal template create
nt-be/.sqlx/query-f0909d35187099eca523e0e0a0be2547de92116765a206e148e3a80968c975c2.json sqlx query metadata for proposal template fetch-by-id
nt-be/.sqlx/query-e648a256a6492374d9c39511c1ecc63a03f5d653bd018e8a2d90b2951c09081c.json sqlx query metadata for proposal template update
nt-be/.sqlx/query-a999e96f58c9e0f5e0af26d859955ac7c5d65094b52d2944d2551eb735d4861d.json sqlx query metadata for proposal template list
nt-be/.sqlx/query-498093aafc01d2c8e39728c8edb132571cb34e99964883c0bf99eae3fdbe06f6.json sqlx query metadata for proposal template delete
nt-be/.sqlx/query-3abe05be828d94bd877028f9c5c269ec479c5a5b9dc94a1d1cf254236e6b7382.json sqlx query metadata for setting custom_requests_enabled
nt-be/.sqlx/query-0d0919b0bc0e734a5155a2766337c005bf863105549661b4889b74dc6e60c181.json sqlx query metadata for reading custom_requests_enabled
nt-be/.sqlx/query-3eb239014126a32c3fb4793277485fd23afb943625065102af61fd7432bbfb52.json sqlx query metadata update for address_book list nullability
nt-be/.sqlx/query-5f205588489df021222df4247d494a6ad21c0216728876d457954378fcaca1a3.json sqlx query metadata update for address_book export nullability
nt-be/.sqlx/query-36567cc2245450fcf3089d57db17d3c4a7fc68b261d3081a18728b80eb0142a9.json sqlx query metadata update for address_book list nullability
nt-be/.sqlx/query-57e6d33d9168ec4314e8db17f256b14eb9cfee654db2842fe5ac72731d3b0df9.json Removed stale sqlx query metadata (confidential_intents)
docs/CUSTOM_PROPOSAL_TEMPLATES.md End-user/dev documentation for DSL, UI, permissions, and architecture
CLAUDE.md Added domain knowledge pointer for Custom Proposal Templates
.agents/skills/author-custom-proposal-template/SKILL.md Repo-local agent skill for authoring/fixing manifests aligned to validator
Files not reviewed (8)
  • nt-be/.sqlx/query-0d0919b0bc0e734a5155a2766337c005bf863105549661b4889b74dc6e60c181.json: Generated file
  • nt-be/.sqlx/query-3abe05be828d94bd877028f9c5c269ec479c5a5b9dc94a1d1cf254236e6b7382.json: Generated file
  • nt-be/.sqlx/query-498093aafc01d2c8e39728c8edb132571cb34e99964883c0bf99eae3fdbe06f6.json: Generated file
  • nt-be/.sqlx/query-57e6d33d9168ec4314e8db17f256b14eb9cfee654db2842fe5ac72731d3b0df9.json: Generated file
  • nt-be/.sqlx/query-a999e96f58c9e0f5e0af26d859955ac7c5d65094b52d2944d2551eb735d4861d.json: Generated file
  • nt-be/.sqlx/query-e648a256a6492374d9c39511c1ecc63a03f5d653bd018e8a2d90b2951c09081c.json: Generated file
  • nt-be/.sqlx/query-f0909d35187099eca523e0e0a0be2547de92116765a206e148e3a80968c975c2.json: Generated file
  • nt-be/.sqlx/query-fd881f5e05e9494b1ca87f66d411ed04133e0d66df5d949b10f5eb78b0676c37.json: Generated file

Comment on lines +21 to +25
// The Developer tab only does anything for a signed-in member (its Enable action is
// ChangePolicy-gated), so hide it from guests and signed-out viewers — same idea as the
// feature-flag-gated Integrations tab.
const { isGuestTreasury } = useTreasury();
const showDeveloper = !isGuestTreasury;
Comment on lines +89 to +95
<Button
variant={enabled ? "outline" : "default"}
disabled={isLoading || setEnabled.isPending}
onClick={() => toggle(!enabled)}
>
{enabled ? "Disable" : "Enable"}
</Button>
Comment on lines +13 to +16
This doc is the canonical reference for the manifest DSL, the architecture, and the
authoring UI. The in-app `/<dao>/custom-templates/about` page and the
`author-custom-proposal-template` agent skill both derive from it.

Comment thread docs/CUSTOM_PROPOSAL_TEMPLATES.md Outdated
Comment on lines +184 to +188
[`skills/trezu-custom-proposal-template/`](../skills/trezu-custom-proposal-template/SKILL.md)
installs into Claude Code / Claude.ai / Codex (see its `README`) and produces a manifest
to paste into the **Code** tab — it needs no repo access. (The `.claude/skills/` skill,
by contrast, is for agents working *on* the trezu codebase.)

Comment thread CLAUDE.md Outdated

- **Subscription System (AI Guide):** [docs/AI_SUBSCRIPTION_GUIDE.md](docs/AI_SUBSCRIPTION_GUIDE.md) - Architecture, key files, payment flows, fee calculations, and implementation details
- **Pricing Reference:** [docs/PRICING.md](docs/PRICING.md) - Plan tiers, features, and database schema
- **Custom Proposal Templates:** [docs/CUSTOM_PROPOSAL_TEMPLATES.md](docs/CUSTOM_PROPOSAL_TEMPLATES.md) - the manifest DSL, architecture, args-first authoring, and ChangePolicy gating. An authoring agent skill lives at [.claude/skills/author-custom-proposal-template](.claude/skills/author-custom-proposal-template/SKILL.md)
- Translate the whole custom-templates feature (customTemplates namespace,
  13 locales) instead of leaving it English-only
- Tour copy via PageTourContent/pageTours.requestTemplates (was hardcoded JSX)
- Sidebar pinned templates use a real <Link> href, not onClick-only
- build-proposal docstring: correct the string-substitution vs typed-args contract
- Drop the agent/portable skills; the DSL is maintained in docs/
A member-filled value reached the contract as a string: a `{{field}}`
placeholder always stringified, so a bool toggle serialized as
{"approved":"true"} and a number as {"count":"5"} — which serde-based
contracts reject. That made the `bool` and `number` field types effectively
unusable for the methods they imply.

A lone `{{field}}` bound to a bool/number/json field now resolves to its
typed JSON value (true / 5 / parsed object), while u128 uint/amount and any
composed value stay strings. Covered by build-proposal tests.

Also folds in the review fixes made alongside it:
- validate binding.receiver_id (NEAR account) and method_name (identifier)
  in the manifest validator and backend validate_manifest
- drop the RegExp lookbehind in the placeholder matcher (Safari <16.4 throws
  at module load); handle escaped {{{{...}}}} via alternation instead
- cap the author regex pattern and the input fed to pattern.test (ReDoS)
- localize the Developer tab, the Request Templates sidebar entry, and
  member-facing form-validation messages across all 13 locales
- both sidebar navigations use a real <Link> href
- move the distributable skill from skills/ to docs/
- How It Works empty-state button -> outline; tour copy grammar; wrap the
  touched-gating SSR test in an intl provider
@frol frol closed this Jun 29, 2026
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