Skip to content
cccskills
FreePublished on GitHub

site-database

Use when writing or applying a migration in site-os, adding a table or column, storing files, or when a migration fails with "must be owner of table" — covers the apply procedure, the postgres-ownership rule, money/rate units, and where files belong.

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md4.5 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Database and migrations

Postgres, self-hosted Supabase on your-vps, under Coolify.

Applying a migration

There is no migration runner. Migrations are applied by hand, in order, and nothing records that they were. Check first, never assume:

SELECT to_regclass('public.<a table it creates>') IS NOT NULL;
./scripts/coolify-tunnel.sh          # if not already up
scp supabase/migrations/0NN_x.sql your-vps:/tmp/
ssh your-vps "sudo docker cp /tmp/0NN_x.sql supabase-db-<service-uuid>:/tmp/ \
  && sudo docker exec supabase-db-<service-uuid> \
     psql -U postgres -d postgres -v ON_ERROR_STOP=1 --single-transaction -f /tmp/0NN_x.sql"

ON_ERROR_STOP=1 --single-transaction is not optional. Without it psql continues past an error and leaves the migration half-applied — worse than a clean failure.

Applying to production touches live data. Ask first unless the operator has already said to apply it.

must be owner of table — do not switch roles

Every table in public is owned by postgres. If a migration fails with must be owner of table X, something created X as another role. Fix the owner:

ALTER TABLE public.X OWNER TO postgres;

Do not reach for -U supabase_admin to get past it. That makes the one migration work and deepens the split, so the next one fails somewhere else. This exact situation happened on 2026-09-16 (migration 033); migration 034 normalised all 29 tables. Ownership decides only who may run DDL — not RLS, policies, grants, or application behaviour. supabase_admin legitimately owns the internal schemas (auth, storage, realtime).

Invariant, should return nothing:

SELECT tablename, tableowner FROM pg_tables
 WHERE schemaname = 'public' AND tableowner <> 'postgres';

Units: no float ever touches money

  • Money in paise. price_paise, amount_paise, tax_base_paise.
  • Rates in basis points. gst_rate_bp, discount_bp. 1800 = 18%.
  • Round the component and subtract, never round the result — so the parts always re-add to the total exactly. src/lib/gst.ts and src/lib/pricing.ts both have property tests asserting that.

Where files go

WhatWhereCeiling
Invoice PDFsinvoice_archive~4 KB each
Guides, blueprints, prompt librariesproduct_assets25 MB, CHECK-enforced
Installers, video, anything largenot the databaseGitHub release or external URL

Postgres over object storage for small durable files: it is already in the backup, it moves with the database, and there is no split-brain between a row and a bucket. Cloudflare R2 is not enabled on the account (checked 2026-09-16). The CHECK is what makes the boundary loud instead of a judgement call.

Conventions

  • CHECK the invariant, and mirror it in the server action. The constraint is the guarantee; the mirror is so the admin sees a message on the field rather than a database error they cannot act on.
  • Open-ended things get a kind + jsonb config, not a column per case. products.fulfilment_kind + fulfilment_config: a new delivery method is a new kind and a handler, not a migration.
  • Append-only where a record must not change. invoice_archive has a trigger refusing UPDATE — correcting an invoice is a credit note, not an edit.
  • Admin writes go through mutate() (admin/lib/db.ts), which runs the change and perform_action() in one transaction, so the capability check and the audit row cannot come apart.
  • RLS on every table. Service-role-only tables get REVOKE ALL ... FROM anon, authenticated and no policy. Remember public.app_config grants SELECT to anon — never put a secret there.

Verifying, not trusting

tsc cannot see inside a SQL string. A placeholder with no parameter compiles and fails at runtime — this has already happened twice here. After editing a parameterised statement, count them:

# highest $n in the SQL must equal the number of params passed

Then run pnpm build && pnpm test && pnpm env:check.

Related

  • docs/OPS.md §3 (which store holds which value) and §3.5 (this, in full).
  • [[site-mcp]] — the MCP write path for content and products.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

brand

Free

Apply this site's visual identity — colours, type, logo files and fixed brand strings. Use for any page, component, social asset or document that should look like the site.

The English summary is being prepared. Showing the original description.

aryannema/vigyan-site-os2Updated Oct 2, 2026

Use FIRST when someone asks for a new feature or a change to an existing one — before any schema, code or test. Works out what is actually being asked for, whether to build it or integrate a third party (Trustpilot, Stripe, Calendly and the like), where the data lives, who may do what, and what happens when the vendor is down. Produces a decision, then hands to feature-schema.

The English summary is being prepared. Showing the original description.

aryannema/vigyan-site-os2Updated Oct 2, 2026

Use when a new feature needs a table, column or capability — evolving the schema so the new thing joins the permission, audit and RLS contract instead of sitting outside it. Covers the DDL/DML split, the resource/capability wiring, and the self-verifying migration.

The English summary is being prepared. Showing the original description.

aryannema/vigyan-site-os2Updated Oct 2, 2026

Use after building a feature — explore it in a real browser to find what nobody predicted, then turn each finding into a deterministic test that guards it forever. Covers the agentic pass, what to generate vs hand-write, and how to skip honestly when a capability is missing.

The English summary is being prepared. Showing the original description.

aryannema/vigyan-site-os2Updated Oct 2, 2026

Publishing content or deploying site-os — what caches exist, what refreshes them, and how to prove a change is actually live. Use when publishing a post or product, launching a page, deploying, or when someone says a change "isn't showing".

The English summary is being prepared. Showing the original description.

aryannema/vigyan-site-os2Updated Oct 2, 2026

Handling secrets, keys and credentials for site-os — reading vigyan-secrets.md safely, moving keys into app_secrets, rotating, and responding to an exposure. Use whenever a task touches a key, token, password, .env file, app_secrets, encryption, or the secrets document.

The English summary is being prepared. Showing the original description.

aryannema/vigyan-site-os2Updated Oct 2, 2026

See all skills by aryannema

Report a problem with this skill