Forge Doctrine: Variable and Data-State Convention¶
URL: https://mkdocs.justinsforge.com/FORGE-DOCTRINE-VARIABLES/
Companion to FORGE-DOCTRINE.md Section 4. Defines the canonical variable naming convention, the data-state tags, the source-tag registry, and the process for adding new tags.
Convention¶
Variables, function parameters, type aliases, TypedDicts, and config keys follow snake_case [source]_[entity]_[state] reading left to right from broad to specific.
Three data-state tags (Section 4 of doctrine):
| Tag | When | Example identifier | Example value |
|---|---|---|---|
raw_ |
Data exactly as it leaves a third party. Untouched, including provider-side wrapping, pagination cursors, request metadata. | raw_notion_search_response |
{"object": "list", "results": [...], "next_cursor": "abc"} |
parsed_ or clean_ |
Internal, normalized form. Provider noise stripped, types coerced, ready for forge logic to operate on. | clean_task_array |
[{"id": "...", "title": "...", "due": "..."}] |
payload_ |
Outbound, formatted for a specific external endpoint. Serialized to that endpoint's schema. | payload_n8n_webhook |
{"workflow_id": "abc", "input": {...}} |
Forge-internal data with no external source drops the source tag and uses clean_<entity> as default.
Source Tag Registry¶
Source tags are extensible. The list below is the starting set; add new tags via the process documented in the next section. Eval harness check forge_eval_check_variable_tags.sh validates the registry.
| Tag | Service | Notes |
|---|---|---|
notion |
Notion API | Life OS + JWVR databases |
gmail |
Gmail API | personal + business accounts |
gcal |
Google Calendar API | event + calendar surfaces |
gdrive |
Google Drive API | file + folder operations |
telegram |
Telegram Bot API | inbox capture, lifeos coordinator, notify-out |
garmin |
Garmin Connect | HRV, sleep, body battery |
eight_sleep |
Eight Sleep | bed sensor data; native HA integration is canonical, this tag is legacy |
ha |
Home Assistant | smart home state, sensors, automations |
frigate |
Frigate NVR | camera events, recordings |
n8n |
n8n workflow engine | workflow trigger payloads |
cf |
Cloudflare API | DNS, tunnels, Access |
proxmox |
Proxmox VE | VM and LXC management |
Adding a New Source Tag¶
When wiring a new integration:
- Propose the tag in your branch's PR description or commit message.
- Add the tag to this file's registry table with the canonical service name and any notes.
- Add it to
forge/eval.jsonso the eval harness validates the tag pattern. - Auto-dream's nightly pass detects
<source>_<entity>_<state>patterns in code that don't match the registry; those land inLESSONS.mdas candidate additions for human review.
Naming rules¶
- Lowercase, snake_case, max 12 characters
- Prefer the integration's canonical short name (
notionnotnotion_api,garminnotgarmin_connect) - Never abbreviate further than the source's own brand short form (
hais fine because Home Assistant uses it;tgfor Telegram is not, usetelegram) - If two integrations share a domain (Gmail and Calendar both Google), keep them split by service not unified by provider
Anti-patterns¶
- Inventing tags inline without registering:
mystery_service_response = ...will fail the eval check - Reusing a tag for the wrong service: do not reuse
gmailfor "any-mail" data; that loses semantic precision - Using the source tag when the data is purely internal:
clean_dedup_tableis correct,forge_dedup_tableis not (the source slot is empty for internal data)
Examples per Data State¶
Raw¶
raw_notion_search = notion_client.search(query=q)
raw_garmin_hrv_response = httpx.get(garmin_url, headers=h).json()
raw_telegram_webhook = request.json
raw_gmail_thread = gmail_service.users().threads().get(userId="me", id=thread_id).execute()
Clean / Parsed¶
clean_task_array = [_normalize_task(t) for t in raw_notion_search["results"]]
clean_garmin_hrv = parse_hrv_payload(raw_garmin_hrv_response)
clean_telegram_message = TelegramMessage.from_webhook(raw_telegram_webhook)
clean_gmail_message_array = [_flatten(m) for m in raw_gmail_thread["messages"]]
clean_dedup_table = build_dedup_table(seen_ids)
Payload¶
payload_n8n_workflow_trigger = {"workflow_id": "abc", "input": clean_task_array}
payload_notion_page_create = {"parent": {"database_id": db_id}, "properties": props}
payload_telegram_message = {"chat_id": chat, "text": body, "parse_mode": "MarkdownV2"}
payload_gcal_event_insert = {"summary": title, "start": {"dateTime": iso}, "end": {...}}
Edge Cases¶
Multi-stage pipelines. Each stage gets its own variable with the appropriate tag. Never reuse a name across states.
raw_garmin_response = httpx.get(...).json()
clean_garmin_hrv = parse_hrv(raw_garmin_response)
payload_notion_wellness = build_wellness_page(clean_garmin_hrv)
Lists vs scalars. Plural entity for lists (clean_task_array), singular for scalars (clean_task). Tag stays singular; the array suffix carries the multiplicity.
Function parameters. Same convention.
Type aliases / TypedDicts. Name them after the canonical state.
Locally-cached external data. When data round-trips through a local cache, the cache stores the clean_ form (provider noise already stripped) under the clean_ name.
Enforcement Schedule¶
- All new code from Phase 2 onward: mandatory.
- Refactored code: rename when you touch the function. Do not refactor existing code purely to apply the convention; doctrine forbids autonomous restructuring without first-principles understanding (Section 2).
- Audit targets (one targeted refactor pass per file in Phase 4.3 alongside the bulk flat-file rename):
forge_telegram_inbox_brain.py,forge_telegram_lifeos_coordinator_brain.py,forge_dispatcher.sh,forge_notify.sh, all n8n payload constructors. - Eval harness check (Phase 4.5):
forge_eval_check_variable_tags.shgreps Python and shell for<word>_<word>_<word>triples that look like the convention but use unregistered source tags. Findings logged to LESSONS.md as candidates for either rename or registry-add.