This is the complete operational contract for inserting one GTB event. Read only this file for an add-entry request; no parent, SDK, README, or repository brief is needed. Keep this file synchronized when GTB's schema or selection changes.
Target: plusjade/source-gtb, val-scoped SQLite, table
flea_medication_reminders. Use Val Town MCP SQLite directly. This operation is
authorized by an explicit add-entry request. Never call reminders.rebuild,
edit code/schema, create branches, fetch credentials, send notifications, or
inspect the parent as part of this path.
Preferred prompt (JSON after the source name):
add-entry: source-gtb {"starts_at":"2026-09-12T17:00:00-07:00","duration_seconds":3600,"main_text":"Facetime with Bron","sub_text":"healthcheck"}
Required: offset-qualified ISO start, integer duration 1..86400 seconds, nonblank main_text. sub_text defaults to ""; preserve supplied text exactly. Reject unknown fields rather than silently ignoring them. Duration is elapsed seconds, including across DST. The one-day maximum is an intentional limit of this shortcut.
Natural-language equivalents are accepted when unambiguous: "today at 5pm, expires 1 hour" means a one-hour duration, in America/Los_Angeles unless the user specifies a different timezone. Capture the current clock once for relative dates; do not use a stale conversation date. Resolve calendar dates using an IANA timezone with a timezone-aware runtime, not mental UTC arithmetic. Ask once if required information is missing or a local time is nonexistent/ambiguous at a DST transition. "Today" stays today even if the requested time has passed; do not roll to tomorrow. Restate the resolved date/time and offset in the result.
Use a local runtime (Python standard library datetime/zoneinfo is sufficient). Convert the supplied start to an aware datetime; require an explicit offset in canonical input, whole seconds, and year 2000..2100. Validate by parsing and round-tripping the supplied calendar date/time (reject invalid dates). Require duration_seconds to be an integer, not a boolean, and main_text/sub_text to be strings. Check nonblank main_text without trimming the stored text.
Compute deterministically:
The start and expiry must be finite, with expiry strictly greater than start. Do not compute timestamps in the language model or send milliseconds. Do not interpolate user text into SQL, shell commands, or executable code: pass it as data.
Discover only val_town_sqlite_batch if it is not already callable. Call it with:
{"database":{"type":"val","val":"plusjade/source-gtb"},"mode":"write","statements":[ {"sql":"<insert below>","args":"<normalized named parameter object>"}, {"sql":"<verification below>","args":"<its named parameter object>"} ]}
The placeholders above describe tool arguments, not literal strings to submit. Use named args objects containing exactly the parameters each statement references. Run these two statements in order in the same transaction:
INSERT INTO flea_medication_reminders
(date_key, main_text, sub_text, starts_at, expires_at, fetched_at)
VALUES (:date_key, :main_text, :sub_text, :starts_at, :expires_at, :fetched_at)
ON CONFLICT(date_key) DO NOTHING;
SELECT date_key, main_text, sub_text, starts_at, expires_at,
CASE WHEN main_text = :main_text AND sub_text = :sub_text
AND starts_at = :starts_at AND expires_at = :expires_at
AND typeof(starts_at) = 'integer' AND typeof(expires_at) = 'integer'
AND expires_at > starts_at AND expires_at - starts_at <= 86400
THEN 1 ELSE 0 END AS matches_request
FROM flea_medication_reminders WHERE date_key = :date_key;
Success requires a successful transaction, exactly one verification row, and
matches_request = 1. Insert rowsAffected=1 means added; 0 with matching read-back
means already present (safe retry). fetched_at is not part of duplicate identity.
A different event on the same date produces rowsAffected=0 and matches_request=0: report the date conflict, make no further writes, and ask whether the user wants to replace that entry as a separate request. Never silently upsert or manufacture a different date_key. SQL errors roll back the batch; a missing table/column is schema drift, not permission to migrate. Report the error and stop the shortcut. If the tool outcome is uncertain, repeat the exact normalized request; do not reinterpret relative time or change dates. The unique key prevents duplicates.
After success, finish immediately: "Added [text] to source-gtb: [resolved start] for [duration]. Stored row verified." Say "Already present" for an exact retry. No production smoke-test fixtures, broad checks, logs in repository docs, or additional MCP reads are needed.