The guides DAG is the knowledge backbone of Mental Wealth Academy: one definitive guide per topic, connected by prerequisite edges, with computed levels, jury verification, and onchain-bound diamond rewards. It is easy to corrupt from a distance — several queries must agree with each other exactly, and one of them pays money. Read this whole file before touching anything it names.
-
Acyclicity is enforced by the database. The guide_edges_cycle_check trigger (supabase/migrations/20260705090000_guides_dag.sql) rejects any edge that would create a cycle. If an insert fails with a cycle error, that is the system working: report it, do not drop, disable, or route around the trigger, and do not "fix" it by deleting other edges without being asked.
-
The published-only lockstep. These functions all traverse published-to-published edges only, and they must stay in agreement: completeGuide (gate), getWalkthrough (closure), awardGuideRewards (payout closure), getFrontierGuides, getKnowledgeMap — all in lib/guides-db.ts and lib/guide-rewards-db.ts. If you change status semantics in one, mirror it in all of them in the same commit, or bonuses strand / drafts leak into gating.
-
The reward path is a money path — fail-closed, idempotent, trustless toward its caller. awardGuideRewards re-checks inside its own transaction that the guide exists, is published, and is not authored by the completing user, and every payout is guarded by ON CONFLICT DO NOTHING against guide_diamond_claims UNIQUE (user_id, guide_id, claim_type). Never remove a re-check because "the route already checks it." Never credit users.shard_count outside that transaction. The exploit this design closed: a self-created draft with no prereqs paid all three tiers (710 diamonds) repeatably. Do not reintroduce that class.
-
Reward amounts have one source. GUIDE_COMPLETE_REWARD and the multipliers in lib/guide-rewards-db.ts are exported and consumed by the walkthrough reward preview. Change amounts there only; never hardcode a diamond number in UI or a second constant.
-
One definitive guide per topic. Duplicates are prevented by the UNIQUE topic_title and resolved by verifiers/disputes, with forks sharing a canonical_group_id. Never work around a title collision by tweaking the title ("Journaling Practice 2") — that defeats the entire model.
-
Forward refs are planning data, not edges. guide_forward_refs rows do not participate in levels, gating, or closures. They auto-resolve into real edges when a guide with a matching topic_title is inserted — if you touch guide creation, preserve the resolveForwardRefs call path.
-
Verification is transactional and fail-closed. submitGuideForVerification flips status and draws an odd-numbered panel in one transaction; an empty verifier pool throws before any write. Panels draw from verifier_credentials (earned via test, or seeded with scripts/seed-verifier-credentials.ts). The CRE score is ADVISORY, server-generated via Eliza — the Chainlink DON path is documented but unimplemented; do not build on it without asking.
-
Schema changes are additive migrations only. New migration file in supabase/migrations/, never ALTER existing constraints or the status CHECK enum casually, RLS enabled with no policies (app connects as postgres, per repo convention). Never modify tables owned by other systems (proposals, users) for a guides feature.
-
Untouchables. app/shadow-work: no changes, no new dependencies. components/course-renderers/ComponentRenderer.tsx is shared with courses: style guides only through scoped overrides (see the guide-wikipedia-style skill). Deployed contracts are immutable.