Skip to content

All Sheets — Column Reference

Full column definitions for every tab in the database sheet. Columns are listed in order — the index (0-based) is the positional contract the bot uses.

Warning: Column order is the code contract. Never reorder or insert columns in the middle. See Schema Overview.


01. Time Entries — A:H

Written by: log_time, log_sick_day, log_leave, approve_pto, approve_time_offUpdated by: edit_entry (full row rewrite A:H) Deleted by: delete_entry

ColIdxFieldTypeNotes
A0idintegerSequential, permanent. Never reused.
B1dateYYYY-MM-DDEntry date (not necessarily today).
C2personstringDeprecated — always "". Do not read.
D3user_idintegerFK → 03. Users.id
E4project_idintegerFK → 02. Projects.id
F5task_idinteger or ""FK → 05. Tasks.id. Empty for sick/leave/PTO entries.
G6hoursfloatDecimal hours, e.g. 4.5
H7notesstringOptional. Labels: "Sick day", "Leave", "PTO".

Example: 101, 2026-03-14, , 5, 12, 3, 2.5, Development work


02. Projects — A:L

Written by: create_project, create_sprint, ensureProtectedProjectsUpdated by: set_project_budget (D), set_project_deadline (I), rename_project (B), assign_pm (G), update_sprint_status (L) Deleted by: delete_project

ColIdxFieldTypeNotes
A0idintegerSequential, permanent.
B1namestringUnique per tenant.
C2client_idintegerFK → 04. Clients.id
D3budget_hoursfloat or ""Total hours cap. Set via set_project_budget.
E4alert_thresholdintegerPercent 0–100. Default 80.
F5statusstring"active" or "inactive" (lowercase).
G6pm_user_idinteger or ""FK → 03. Users.id. Set via assign_pm.
H7project_categorystring"client" | "internal" | "pto" | "sick" | "leave" | "time_off"
I8deadlineYYYY-MM-DD or ""Owner-only via set_project_deadline.
J9parent_project_idinteger or ""Sprints only. FK → 02. Projects.id.
K10sprint_numberinteger or ""Sprints only. Auto-incremented.
L11sprint_statusstring or ""Sprints only: "planned" | "active"

Example: 42, Nike Rebrand, 7, 200, 80, active, 3, client, 2026-06-30, , ,


03. Users — A:H

Written by: add_user, ensure_userUpdated by: update_user_role (D), rename_user (C), deactivate_user (E), reactivate_user (E)

ColIdxFieldTypeNotes
A0idintegerSequential, permanent.
B1slack_user_idstringSlack user ID, e.g. U01234567.
C2namestringDisplay name.
D3rolestring"owner" | "manager" | "user". Code normalises "pm""manager" on read.
E4statusstring"active" | "inactive"
F5discipline_idinteger or ""FK → 08. Disciplines.id
G6employment_typestring"fte" | "contractor" | "vendor"
H7hours_per_weekfloatWorking hours per week. Default 40. Set explicitly for contractors. Used for capacity and overbooking calculations. Vendor value is a placeholder only.

Example: 5, U01234567, Sarah Chen, manager, active, 2, fte, 40


04. Clients — A:D

Written by: create_client, create_project (auto-creates client), ensureProtectedProjects (auto-creates "Internal")

ColIdxFieldTypeNotes
A0idintegerSequential.
B1namestringUnique. "Internal" is auto-created.
C2statusstring"active" | "inactive"
D3notesstringOptional. Column D has no header in the sheet.

Example: 3, Acme Corp, active, Enterprise client


05. Tasks — A:C

Written by: create_task

ColIdxFieldTypeNotes
A0idintegerSequential.
B1namestringe.g. "Development", "Design", "QA"
C2is_billableboolean string"TRUE" or "FALSE"

Example: 3, Development, TRUE


06. Project_Tasks — A:D

Written by: enable_task_for_project — writes [project_id, task_id, budget_hours, alert_threshold]Deleted by: disable_task_for_project (row deletion), delete_project (cascade)

Presence = enabled. A row here means the task is enabled for that project. Absence means disabled. There is no enabled flag column — the CLAUDE.md documentation is incorrect on this point.

ColIdxFieldTypeNotes
A0project_idintegerFK → 02. Projects.id
B1task_idintegerFK → 05. Tasks.id
C2budget_hoursfloat or ""Task-level hours budget. Empty if none set.
D3alert_thresholdintegerPercent. Default 80.

Example: 42, 3, 40, 80


07. Budgets (Allocations / Envelopes) — A:I

Written by: allocate_hoursUpdated by: update_allocation (D, F), cancel_allocation (F)

Despite the name, this sheet stores per-person hour envelopes (allocations), not project budgets. The project-level budget lives in 02. Projects.budget_hours.

ColIdxFieldTypeNotes
A0idintegerSequential.
B1user_idintegerFK → 03. Users.id
C2project_idintegerFK → 02. Projects.id
D3allocated_hoursfloatHours for this person on this project.
E4transferred_from_allocation_idinteger or ""FK → 07. Budgets.id. Set when transferred; "" on initial creation.
F5statusstring"active" | "cancelled"
G6created_byintegerFK → 03. Users.id — the Manager/Owner who created it.
H7created_atYYYY-MM-DDCreation date.
I8task_idinteger or ""FK → 05. Tasks.id. Optional work-category tag.

Example: 17, 5, 42, 40, , active, 2, 2026-03-10, 3


08. Disciplines — A:C

Written by: add_discipline

ColIdxFieldTypeNotes
A0idintegerSequential.
B1namestringe.g. "Engineering", "Design"
C2statusstringAlways "active".

Example: 2, Design, active


09. Conversations — A:F

Written by: store_conversation (auto-called after every exchange) Read by: get_conversation (returns last N exchanges for current user today)

ColIdxFieldTypeNotes
A0conversation_idstringComposite: {slack_user_id}_{YYYY-MM-DD}
B1timestampISO 8601Full timestamp.
C2user_idstringSlack user ID (string, not integer DB id).
D3user_namestringDisplay name at time of message.
E4messagestringThe user's message.
F5responsestringVERA's response.

One row per exchange. Only today's rows are read (24-hour context window).


10. Pending_PTO — A:I

Written by: request_ptoUpdated by: approve_pto (status, approved_by, resolved_at)

ColIdxFieldTypeNotes
A0idintegerSequential.
B1user_idintegerFK → 03. Users.id
C2dateYYYY-MM-DDRequested date.
D3hoursfloatTypically 8h (one work day).
E4statusstring"pending" | "approved" | "rejected"
F5created_atYYYY-MM-DDWhen submitted.
G6approved_byinteger or ""FK → 03. Users.id. Set on resolution.
H7resolved_atYYYY-MM-DD or ""When approved/rejected.
I8notesstringUser-supplied reason.

Example: 8, 5, 2026-03-20, 8, approved, 2026-03-14, 2, 2026-03-14, Holiday trip


11. Unresolved_Items — A:I

Written by: createUnresolvedItem() — called by log_sick_day, log_leave, approve_pto, request_time_offUpdated by: resolve_item, dismiss_item (G:I)

ColIdxFieldTypeNotes
A0idintegerSequential.
B1typestring"sick_conflict" | "leave_conflict" | "pto_conflict" | "time_off_request"
C2descriptionstringHuman-readable, or JSON for time_off_request type.
D3affected_project_idinteger or ""FK → 02. Projects.id
E4affected_user_idinteger or ""FK → 03. Users.id
F5created_atYYYY-MM-DD
G6statusstring"open" | "resolved" | "dismissed"
H7resolved_atYYYY-MM-DD or ""
I8resolved_byinteger or ""FK → 03. Users.id

12. Company_Calendar — A:D

Written by: add_company_holidayDeleted by: remove_company_holiday

ColIdxFieldTypeNotes
A0idintegerSequential.
B1dateYYYY-MM-DD
C2namestringe.g. "Christmas Day"
D3typestring"holiday" | "company_event"

Only "holiday" entries block PTO requests and are excluded from leave date ranges.


13. Rate_History — A:D

Written by: set_rate (append-only — never updates existing rows)

ColIdxFieldTypeNotes
A0idintegerSequential.
B1user_idintegerFK → 03. Users.id
C2hourly_costfloate.g. 80.00 for $80/hr
D3effective_fromYYYY-MM-DDRate applies from this date forward.

Lookup rule: for an entry dated D, use MAX(effective_from) WHERE effective_from ≤ D. This preserves historical P&L accuracy when rates change.

Example:

1, 5, 75.00, 2025-01-01
2, 5, 85.00, 2026-03-01  ← rate increase from March 1st

14. Liabilities — A:H

Written by: create_liabilityUpdated by: cancel_liability (F — status)

ColIdxFieldTypeNotes
A0idintegerSequential.
B1project_idintegerFK → 02. Projects.id
C2vendor_user_idintegerFK → 03. Users.id (must be employment_type="vendor")
D3descriptionstringWhat this payment covers.
E4agreed_amountfloatFixed payment in USD.
F5statusstring"active" | "cancelled"
G6created_byintegerFK → 03. Users.id
H7created_atYYYY-MM-DD

Example: 15, 12, 9, Dev House - Auth module, 5000, active, 1, 2026-03-15


22. Projects Master — A:I

Written by: create_project, create_sprint, ensureProtectedProjects

This sheet is joined to 02. Projects by project name (not ID). The join is case-insensitive.

Bot-written columns (A–H):

ColIdxFieldTypeNotes
A0project_namestringJoin key to 02. Projects.name.
B1client_namestringDenormalised from 04. Clients.name.
C2typestring"T&M" | "Fixed Price" | "Retainer" | "Pro Bono" | "Agile" | "sprint"
D3is_billableboolean string"TRUE" or "FALSE". Auto: false for Pro Bono.
E4contract_valuefloat or ""Total contract value in USD.
F5hours_budgetfloat or ""Annual hours (Retainer) or total hours (others).
G6statusstring"Active" (capital A) — different from 02. Projects which uses lowercase.
H7notesstringFree text.
I8billing_ratefloat or ""Blended hourly billing rate. User-managed in the sheet.

Note: Columns I–O in the full reporting sheet version are user-formula columns (estimated hours, actual hours, variances). The bot writes only A–I. In reporting contexts, reference A:P for the full table.

Further reading

VERA by talktalkmake