TOURWB-MOD-003 · Architecture ratified 2026-04-15 · Tour shipped 2026-04-15

Time & Attendance

Same employees. Same substrate. The hours that turn compensation into actual gross.

The Workforce Triangle: HR owns identity, Payroll owns comp events, Time owns the hours. Three modules reading from one substrate. No reconciliation because there is nothing to reconcile — the employee who clocks in is the same employee who gets paid, resolved from the same Dim_Employee row. Here's how.

← All modulesStart the tour →
Section 2

The Workforce Triangle

Time & Attendance closes the triangle. HR defines who works here. Payroll defines what they earn. Time defines when and how long they worked. All three read from the same substrate.

HR & PeopleDim_EmployeePayroll & CompFact_CompChangeTime & AttendanceFact_TimeAllocationreadsreadsreadsShared SubstrateOne identity layer · Bitemporal facts · No syncThree modules, one substrate. The triangle is now complete.
Same employee, three tours
This is Priya Shankar (EMP-003) — the same employee you saw in the HR & People tour and the Payroll & Comp tour. Not a copy. Not a sync. The same record, read through a third module's lens.
Section 3

Time Events

Every punch is an immutable Fact_TimePunch. Clock-ins, clock-outs, breaks, corrections — all events, never mutations. The provenance grain: where did this hour claim come from?

Priya Shankar's punch timeline
FTP-003-001CLOCK_IN
effectiveDate: 2026-03-05T14:00:00Z
source: HARDWARE
geo: 30.267, -97.743 ±5m
FTP-003-002CLOCK_OUT
effectiveDate: 2026-03-05T22:30:00Z
source: HARDWARE
geo: 30.267, -97.743 ±4m
FTP-003-003CORRECTIONCORRECTION
effectiveDate: 2026-03-05T23:15:00Z
source: MANAGER_ENTRY
corrects: FTP-003-002
Actual departure 5:15pm, original punch at 4:30pm was premature badge tap.
FTP-003-004CLOCK_IN
effectiveDate: 2026-03-17T14:00:00Z
source: HARDWARE
geo: 30.267, -97.743 ±5m
FTP-003-005CLOCK_OUT
effectiveDate: 2026-03-17T23:00:00Z
source: HARDWARE
geo: 30.267, -97.743 ±4m
Notice the two dates. effectiveDate is when the punch happened. recordedDate is when we knew about it. The correction (FTP-003-003) was effective on March 5 but recorded the next morning — that's bitemporality. See Section 7.
JSON · one toggle per event
FTP-003-001 · CLOCK_IN · 2026-03-05
{
  "factId": "FTP-003-001",
  "factType": "Fact_TimePunch",
  "employeeId": "EMP-003",
  "effectiveDate": "2026-03-05T14:00:00Z",
  "recordedDate": "2026-03-05T14:00:05Z",
  "sourceTimezone": "America/Chicago",
  "punchType": "CLOCK_IN",
  "source": "HARDWARE",
  "sourceRecordId": "hw-priya-20260305-in",
  "geolocation": {
    "lat": 30.267,
    "lng": -97.743,
    "accuracy": 5
  },
  "authoredBy": "EMP-003",
  "correction": {
    "isCorrection": false,
    "correctsFactId": null,
    "reason": null
  }
}
FTP-003-002 · CLOCK_OUT · 2026-03-05
{
  "factId": "FTP-003-002",
  "factType": "Fact_TimePunch",
  "employeeId": "EMP-003",
  "effectiveDate": "2026-03-05T22:30:00Z",
  "recordedDate": "2026-03-05T22:30:08Z",
  "sourceTimezone": "America/Chicago",
  "punchType": "CLOCK_OUT",
  "source": "HARDWARE",
  "sourceRecordId": "hw-priya-20260305-out-orig",
  "geolocation": {
    "lat": 30.267,
    "lng": -97.743,
    "accuracy": 4
  },
  "authoredBy": "EMP-003",
  "correction": {
    "isCorrection": false,
    "correctsFactId": null,
    "reason": null
  }
}
FTP-003-003 · CORRECTION · 2026-03-05
{
  "factId": "FTP-003-003",
  "factType": "Fact_TimePunch",
  "employeeId": "EMP-003",
  "effectiveDate": "2026-03-05T23:15:00Z",
  "recordedDate": "2026-03-06T15:00:00Z",
  "sourceTimezone": "America/Chicago",
  "punchType": "CORRECTION",
  "source": "MANAGER_ENTRY",
  "sourceRecordId": "cx-priya-20260305-out-fix",
  "geolocation": null,
  "authoredBy": "EMP-001",
  "correction": {
    "isCorrection": true,
    "correctsFactId": "FTP-003-002",
    "reason": "Actual departure 5:15pm, original punch at 4:30pm was premature badge tap."
  }
}
FTP-003-004 · CLOCK_IN · 2026-03-17
{
  "factId": "FTP-003-004",
  "factType": "Fact_TimePunch",
  "employeeId": "EMP-003",
  "effectiveDate": "2026-03-17T14:00:00Z",
  "recordedDate": "2026-03-17T14:00:03Z",
  "sourceTimezone": "America/Chicago",
  "punchType": "CLOCK_IN",
  "source": "HARDWARE",
  "sourceRecordId": "hw-priya-20260317-in",
  "geolocation": {
    "lat": 30.267,
    "lng": -97.743,
    "accuracy": 5
  },
  "authoredBy": "EMP-003",
  "correction": {
    "isCorrection": false,
    "correctsFactId": null,
    "reason": null
  }
}
FTP-003-005 · CLOCK_OUT · 2026-03-17
{
  "factId": "FTP-003-005",
  "factType": "Fact_TimePunch",
  "employeeId": "EMP-003",
  "effectiveDate": "2026-03-17T23:00:00Z",
  "recordedDate": "2026-03-17T23:00:06Z",
  "sourceTimezone": "America/Chicago",
  "punchType": "CLOCK_OUT",
  "source": "HARDWARE",
  "sourceRecordId": "hw-priya-20260317-out",
  "geolocation": {
    "lat": 30.267,
    "lng": -97.743,
    "accuracy": 4
  },
  "authoredBy": "EMP-003",
  "correction": {
    "isCorrection": false,
    "correctsFactId": null,
    "reason": null
  }
}
Section 4

Allocation Anatomy

Fact_TimeAllocation is the business-consumption grain. Punches are raw events; allocations are what the business consumed — duration, attribution, billable flag, and a citation chain back to the punches that produced them.

FTA-003-001EMP-0032026-03-05
Duration
555 min (9.25 hrs)
Billable
No
Category
internal
Retroactive days
1
Citation chain — sourceFacts.punchFactIds
See full allocations — all 13 entries
EmployeeemployeeIdDateDurationSourceCategoryBillableCited facts
Priya ShankarEMP-0032026-03-05555mHARDWAREinternalNoFTP-003-001, FTP-003-002, FTP-003-003
Priya ShankarEMP-0032026-03-17540mHARDWAREinternalNoFTP-003-004, FTP-003-005
Jordan WebbEMP-0042026-03-10480mHARDWAREinternalNoFTP-004-001, FTP-004-002, FTP-004-003, FTP-004-004
Theo GrantEMP-0092026-03-19480mHARDWAREinternalNoFTP-009-001, FTP-009-002
Noor Al-SayedEMP-0122026-03-21480mHARDWAREinternalNoFTP-012-001, FTP-012-002
Maya OkaforEMP-0012026-03-10480mSELF_REPORTinternalNoself-report
Daniel ReyesEMP-0022026-03-15420mSELF_REPORTinternalNoself-report
Clara NilssonEMP-0052026-03-12480mSELF_REPORTinternalNoself-report
Hana TakedaEMP-0072026-03-14450mSELF_REPORTinternalNoself-report
Elena DuarteEMP-0082026-03-11420mSELF_REPORTinternalNoself-report
Amara JohnsonEMP-0102026-03-10480mSELF_REPORTcontractorYESself-report
Luca FerraraEMP-0112026-03-10360mSELF_REPORTcontractorYESself-report
Luca FerraraEMP-0112026-03-17420mSELF_REPORTcontractorYESself-report
Allocations cite punches. Punches cite corrections. The citation chain is complete — every hour traced to its origin event.
Section 5

Labor Cost

Hours from Time. Rate from Payroll. Identity from HR. One query. Three modules. No sync.

Luca Ferrara (EMP-011) — contractor labor cost, March 2026
$1,430.00
13 hours (Time) × $110/hr (Payroll) × EMP-011 (HR)
Time
Hours worked
13 hrs
2 allocations, 780 minutes total
Payroll
Hourly rate
$110/hr
latest Fact_CompChange: FCC-011-03
HR
Identity
Luca Ferrara
Contractor · Engineering · EMP-011
See the SQL
-- Three modules. One query. No sync. No nightly job.
SELECT
  hr.employee_id,
  hr.first_name || ' ' || hr.last_name       AS employee_name,
  SUM(ta.duration_minutes) / 60.0             AS total_hours,
  comp.base_amount                            AS hourly_rate,
  (SUM(ta.duration_minutes) / 60.0)
    * comp.base_amount                        AS labor_cost
FROM   hr.dim_employee                        AS hr
JOIN   payroll.dim_current_comp               AS comp
  ON   comp.employee_id = hr.employee_id      -- same identity, no join key translation
JOIN   time.fact_time_allocation              AS ta
  ON   ta.employee_id   = hr.employee_id      -- same identity again
WHERE  hr.status_parent IN ('Active')
  AND  comp.base_cadence = 'HOURLY'
GROUP BY hr.employee_id, hr.first_name, hr.last_name, comp.base_amount;
Two JOINs, zero mapping tables. Identity is shared, not synced. hr.employee_id = comp.employee_id = ta.employee_id.
Three modules produced this number. No integration layer exists between them. The substrate is the integration.
Section 6

The Hours Explainer

Period-over-period hours delta, fully accounted for by cited facts.

Mar 1 – Mar 15
68.8 hrs
4125 minutes · 9 allocations
-36.8 hrs
Mar 16 – Mar 31
32.0 hrs
1920 minutes · 4 allocations
Delta drivers
NEW EMPLOYEENoor Al-Sayed (EMP-012) activated 2026-03-20, first allocation 2026-03-21.
CORRECTED PUNCHPriya's CLOCK_OUT correction (FTP-003-003) changed allocation from 510 min to 555 min on 2026-03-05.
RETROACTIVE ENTRYDaniel Reyes (EMP-002) self-reported 2026-03-15 hours three days late (retroactiveDays=3).
PERIOD MIXEmployee mix shift: different employees allocated hours in each half. Priya, Luca, Theo contributed hours only in the second half.
Every non-zero delta accounted for by cited facts.
Section 7

Attendance in the Wild

Real-world time scenarios the substrate handles. Three edge cases, three invariants, zero data loss.

Overnight shiftI9Timezone discipline
Theo GrantEMP-009

Theo clocks in at 10:00 PM CST on March 18 and clocks out at 6:00 AM CST on March 19. The shift crosses midnight.

CLOCK_IN effectiveDate
2026-03-19T04:00:00Z
CLOCK_OUT effectiveDate
2026-03-19T12:00:00Z
sourceTimezone
America/Chicago
employeeTimeZone
America/Chicago

effectiveDate is stored in UTC. The workday boundary resolves via employeeTimeZone (America/Chicago). The substrate preserves both the UTC instant and the local context — no ambiguity about which calendar date the shift belongs to.

Retroactive entryI10Retroactive provenance
Daniel ReyesEMP-002

Daniel self-reports his March 15 hours three days late, on March 18. The substrate preserves both dates and computes retroactiveDays automatically.

effectiveDate
2026-03-15T14:00:00Z
recordedDate
2026-03-18T16:00:00Z
retroactiveDays
3

retroactiveDays = floor(recordedDate − effectiveDate). The substrate never silently backdates — it preserves both timestamps and flags the gap.

Corrected punchI5No mutation of prior facts
Priya ShankarEMP-003

Priya's original CLOCK_OUT at 4:30 PM was a premature badge tap. Her manager enters a correction the next morning with the actual departure time of 5:15 PM. The original punch is preserved — never mutated, never deleted.

Original — preserved
factId
FTP-003-002
punchType
CLOCK_OUT
effectiveDate
2026-03-05T22:30:00Z
Correction — cites original
factId
FTP-003-003
correctsFactId
FTP-003-002
effectiveDate
2026-03-05T23:15:00Z

The allocation (FTA-003-001) resolves to the corrected duration of 555 minutes and cites all three punch facts in its citation chain. Nothing was rewritten.

Section 8

Build Receipts

Every decision that shaped this module is on the record.

Predecessor chain — what had to be ratified first
FactlayerModule CharterModule BuildHR & PeoplePayrollTime & Attendance (you are here)
Workforce Triangle complete. Substrate ratified. Tour shipped.