@happyvertical/smrt-timesheets 0.60.9 → 0.60.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,13 +3,13 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-timesheets",
6
- "packageVersion": "0.60.9",
6
+ "packageVersion": "0.60.10",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "5ea54e0fd4dff9986d38c26d2d52014f12be795d3265ed6a5a50addfdafc587c",
11
- "packageJson": "caddeb6b1eaaa400416ec6e0e6bb9f8e8118f6be0fbb461bae5363874bc2d69d",
12
- "agents": "ef4aff8756a13f4014b4d87c881710ad0605100f6ee76db3ab6e6e2fea84cf0b",
10
+ "manifest": "59018d79cda5fce41fcfe77072446a253e9f4b21ff864203682b8d4e4dda0a05",
11
+ "packageJson": "d92f2db8311d6953b24da9f4a88ce8d38a1615fbc6855aed2ff427c7689b4f39",
12
+ "agents": "92ed16c442c03b88854c03e8296c49106f7bb7712b14032fa55d2ae5afe44918",
13
13
  "moduleDoc:agents/period-rollup.md": "68a7427e9d15815e04e8e8e77f52041224c96dd82f93712f741d471c81feada6"
14
14
  },
15
15
  "exports": [
@@ -2695,7 +2695,7 @@
2695
2695
  "polymorphicAssociations": 0,
2696
2696
  "uuidColumns": 94
2697
2697
  },
2698
- "agentDoc": "# smrt-timesheets\n\nShared time entries: who (a `smrt-profiles` Profile, or an agent) worked how\nlong on what (a generic work reference), whether it was approved, and the\nimmutable charge / compensation snapshots taken at approval. Moved out of\n`smrt-projects` / `smrt-support` in #3288 so shops, builders, and service\ndesks share one model instead of re-implementing it.\n\nThis package contains **layer 1 (entry)**, **layer 2 (attendance)**, and\n**layer 3 (period rollup)**.\n`src/attendance/` owns punches, breaks, atomic transitions and durable offline\nreceipts; `./attendance` is the public subpath. Period rollup belongs in this\npackage too, never projects or support. See the README attendance contract.\nAttendance services accept trusted session actors, serialize mutations per\nactor, and bind every write to one executor. The new tables use non-null unique\nopen slots, not nullable unique keys. A linked entry is draft evidence, and\nrollups must deduplicate `AttendancePunch.serviceTimeEntryId`.\n`AttendanceSegment` (`attendance_segments`) is a running work interval inside a\npunch: queryable `workRef*`/`taskRef*`/`kind` columns, one open per punch\n(`openSlot`), closed by `startBreak`/`punchOut`/`autoClose`, never overlapping\nbreaks or each other, immutable once closed. It creates no `ServiceTimeEntry`.\nClosed punches, breaks and segments change only through\n`AttendanceCorrectionService` (`attendance/correction-service.ts`), which sets a\nper-instance capability (`attendance/write.ts`, not exported) that lifts the\nmodels' closed-interval checks for one save, validates the whole punch layout,\nrecomputes durations and appends an `AttendanceCorrection` (append-only ledger,\nno generated operations; only a first acknowledgement by the subject may update\na row). An open punch accepts only a `startedAt` correction, closed breaks/segments inside it, and `addPunch({ open: true })`; the open break/segment and closing stay `AttendanceService`'s. Never export `write.ts`. Punch `source`/`deviceRef*` and break `kind` are immutable except through corrections; `autoCloseReason` is write-once via `persistAutoClose`. A confirmed `PeriodTimecard` is honoured by the\nopt-in `confirmedTimecardGuard` (`rollup/correction-guard.ts`), which refuses\nwhile a card is submitted or confirmed. `PeriodRollupService.reopen()` returns\nsuch a card to `open` and appends a `TimecardReopening` (`timecard_reopenings`,\nappend-only, service-only via `rollup/write.ts`); `submit()` is the optional\nopen -> submitted step (`requireSubmission` makes it mandatory) (#3950).\n`PeriodRollupOptions.attribution` (`'clip'` default | `'start'`) decides crossing\nsources: `'start'` keeps a punch (breaks and segments included) or bounded entry\nwhole in the period containing its start, unclipped, and excludes it from the\nnext (`attributeSource` in `rollup/calculation.ts`) (#4028).\nSegment transitions live in `attendance/segments.ts`; compose them with other\nattendance writes via `AttendanceService.withTransaction`. Any new attendance\ntable must be added to the `includeObjects` lists in the `*.optional.test.ts`\nfiles.\n\n## Models\n\n| Model | Table | Notes |\n|---|---|---|\n| `ServiceTimeEntry` | `service_time_entries` | `workRefType` + `workRefId`, `participantKind` (`human` \\| `agent`), `participantProfileId` / `agentRef`, `source`, `startedAt` / `endedAt` / `durationSeconds`, status machine, `correctionOfId` |\n| `ServiceChargeSnapshot` | `service_charge_snapshots` | Client charge, integer minor units (#2401), one per entry |\n| `ServiceCompensationSnapshot` | `service_compensation_snapshots` | Provider earning, integer minor units, one per entry |\n\n`ServiceEvidenceService` records, submits, approves (writing both snapshots\nthrough a `ServiceCommercialResolver`), and corrects. `smrt-projects` supplies\n`SubscriptionServiceCommercialResolver` for #1925 pricing.\n\n## Generated write actions (#4001)\n\nGenerated CRUD writes stay closed. `ServiceTimeEntryCollection` hosts the only\ngenerated writes, `record` / `submit` / `approve` (`POST\n/servicetimeentries/<action>`, body `{ entryId }` for submit/approve), on REST\nonly; MCP and CLI stay read-only until per-operation MCP authorization (#4004).\nA consumer that closes a model also closes its collection's generated CRUD;\n`ServiceTimeEntryCollection`'s own `forWork` / `pendingApproval` stay open\nuntil the collection class is closed with `registerOverride` too. Surface tests\nmatch `<model>collection_` tool names as well as `<model>_` (#3468).\n`services/time-entry-actions.ts` delegates each to `ServiceEvidenceService`, so\nthe trail and snapshots are set in one place. `services/action-principal.ts`\ntakes identity from the tenancy ALS, then the smrt-users session ALS, never the\nbody; authorizes `servicetimeentries.<action>` with `assertOperationPermission`\n(system context never bypasses; `approve` refuses super-admin bypass); and\nstamps the user's linked profile. Only the participant records/submits their\nown time unless the caller holds `approve`. `approve` prices with the host's\n`setServiceTimeEntryCommercialResolver()` — a factory called per request with\nthe collection's options, so a writing resolver shares the request executor;\nwithout one it is a 409 and writes nothing. The runtime REST transport gates\na collection-hosted action on the ITEM's `api.include`, SvelteKit on the\ncollection's, so both list the actions;\na consumer closing them overrides both registrations. The collection and both\nsnapshot collections carry their own `@smrt()` surface: undecorated, they\npublished generated create/update/delete MCP tools. `reject` waits for #3994.\n\n## Recipe\n\n`src/recipes.ts` declares one `SmrtRecipe`, `timesheets.time-entries`\n(label \"Time entries\", section `work`), emitted into the built\n`manifest.json` and `smrt-knowledge.json` `recipes`. It shows\n`ServiceTimeEntry` alone, with the generic work reference, evidence, agent\nand approval-trail fields hidden; attendance and rollup models stay out of\nit. `status` and its trail are hidden and locked, and the help never says to\nset them: `save()` accepts a bare transition, so submit and approve must go\nthrough `ServiceEvidenceService` (the recipe declares no surface for that). Help lives in `src/time-entries.recipe.md` (#3591); keep it and the recipe\nin plain language. See [core recipes](../core/agents/recipes.md).\n\n## Invariants\n\n- **No domain foreign key on the base.** Work context is the generic\n `workRefType` (qualified class name) + `workRefId`. A domain adds its own\n context by subclassing over the same table — smrt-support's\n `ServiceTimeEntry` adds `caseId` / `specialistId` and extends\n `frozenFieldNames()` so they freeze on approval.\n- **Entries carry no rate.** Money lives only on the snapshots, `= 0`\n integer initializers (INTEGER columns). Never `= 0.0`. Snapshot `save()`\n rejects an `amount` that is not a safe integer (`VALIDATION_INVALID_VALUE`),\n so SQLite cannot silently store `19.99`; fix a fractional resolver at its\n source rather than rounding here.\n- **Approved evidence never changes.** `save()` enforces\n `SERVICE_TIME_ENTRY_STATUS_TRANSITIONS` and compares the frozen fields with\n the stored row. A correction is a new row (`correctionOfId`), the original\n flips to `corrected`. Snapshots are immutable after their first save.\n- **Old qualified names are aliases.** Each model declares its pre-#3288\n `@happyvertical/smrt-projects:*` name in `previousQualifiedNames` (#3338;\n `packages/core/agents/model-moves.md`). smrt-projects must never list these\n classes in its manifests again (`CONFIG_QUALIFIED_NAME_ALIAS_COLLISION`),\n and no subtype (smrt-support's, or a consumer's closing subclass) may\n redeclare an alias (two claimants). Known gap: a same-named subtype that\n replaces a moved class drops that class's alias, and the replaced class's\n own `@happyvertical/smrt-timesheets:*` name stops resolving too — guidance\n for apps with smrt-support must name `@happyvertical/smrt-support:ServiceTimeEntry`.\n Support's vitest environment keeps both classes registered, so verify this\n against built packages, not package tests. `projects/src/__tests__/legacy-qualified-identity.test.ts` pins\n the projects-only contract.\n- **Table names are a compatibility contract.** `service_time_entries` and the\n two snapshot tables hold existing production rows from smrt-projects /\n smrt-support; renaming any of them, or adding a discriminator, is a data\n migration.\n\n## One table family\n\nClasses may share a table only as one inheritance chain. Core resolves\nsame-named subclasses by constructor ancestry at runtime and qualified manifest\nancestry at build registration; the deepest subtype wins across N levels.\nSibling or unrelated types cannot share the table. `smrt-projects` re-exports\nthe base; `smrt-support` subclasses it directly.\n\nConsumers closing package routes should call\n`ObjectRegistry.registerOverride(qualifiedName, { api: false, mcp: false,\ncli: false, tenancy: { mode: 'required' } })` after importing the model and before\nconstructing transports. This tightens the existing registration without a new\nsubtype. See core's README for startup order, static route generation, and reset\nsemantics. `src/__tests__/consumer-closed-surface.test.ts` also covers existing\nmanifest subclass consumers. The snapshots' explicit `time_entry_id` conflict\nkey is not rewritten when a consumer requires tenancy; widening it remains a\nconsumer schema change.\n\nSubclasses restate their fields: package-isolated manifest scanning does not\nread dependency sources, so an unrestated subclass manifest has no columns.\n\n## Svelte UI\n\n`./svelte` exports `TimeEntryCard`, `TimeEntryList`, `TimeSummary`,\n`DurationDisplay`, and `TimeEntryApprovalQueue` (props-driven, plain view\nobjects, `smrt-ui` primitives, `smrtRawPrimitives: \"strict\"`). smrt-projects\nand smrt-support re-export them from their own `./svelte` subpaths.\nCard/list/summary message keys keep their `projects.*` namespace: tenant\nlanguage overrides are stored by key, and renaming would orphan them. New\nkeys use `timesheets.*`.\n\nDisplay components keep the legacy one-decimal hours format by default. A\nconsumer that retains more precise source evidence supplies `hoursFormatter`;\ncard/list/queue detail snippets carry domain evidence without widening the base\nview. ApprovalQueue's optional action snippet replaces its callback controls so\nthe host can own a native form, request identity, authorization and retry flow.\nDisplay currency codes are open ISO strings and amounts remain integer minor\nunits; never put private rates into a view that should not expose them.\n\n## Validation\n\n```sh\npnpm --filter @happyvertical/smrt-timesheets test\npnpm --filter @happyvertical/smrt-timesheets test:postgres\npnpm --filter @happyvertical/smrt-timesheets typecheck\npnpm --filter @happyvertical/smrt-timesheets build\npnpm --filter @happyvertical/smrt-projects test\npnpm --filter @happyvertical/smrt-support test\n```\n\nSchema-affecting edits must keep `service-time-entry-compat.test.ts` (projects,\nsupport) and `service-time-entry-migration-plan.test.ts` (support) green: a\npre-move table must plan no DDL.\n\n## Period rollup\n\nLayer 3 lives under `src/rollup/` and the `./rollup` subpath. See\n[period-rollup.md](agents/period-rollup.md) for resolver, source clipping,\nconfirmation, attributed adjustments, isolation, and transaction contracts.\nTimecards and adjustments have closed generated surfaces and service-only\nmodel writes; never bypass the instance capability with generic persistence.\n\nReplay hashes the tap AS SENT (action, device `at`, paid, work, and `source`/`device`/`kind` only when present, so older receipts still match) so retries match;\n`replay(taps, { resolveAt, now, refusal })` only changes the stored time, reason and the\npunch's `recordedOffline`/`reviewRequired` (both default true and stay set across\nlater taps; false never clears an earlier flag). A reviewed correction clears\n`reviewRequired`. Never hash the resolved time. Work actions (`startWork`, `switchWork`,\n`stopWork`, `resumeWork`) run in the tap's transaction via `apply()`. Refusals store\n`code` from `AttendanceConflict.code`; give new conflicts a code.\n",
2698
+ "agentDoc": "# smrt-timesheets\n\nShared time entries: who (a `smrt-profiles` Profile, or an agent) worked how\nlong on what (a generic work reference), whether it was approved, and the\nimmutable charge / compensation snapshots taken at approval. Moved out of\n`smrt-projects` / `smrt-support` in #3288 so shops, builders, and service\ndesks share one model instead of re-implementing it.\n\nThis package contains **layer 1 (entry)**, **layer 2 (attendance)**, and\n**layer 3 (period rollup)**.\n`src/attendance/` owns punches, breaks, atomic transitions and durable offline\nreceipts; `./attendance` is the public subpath. Period rollup belongs in this\npackage too, never projects or support. See the README attendance contract.\nAttendance services accept trusted session actors, serialize mutations per\nactor, and bind every write to one executor. The new tables use non-null unique\nopen slots, not nullable unique keys. A linked entry is draft evidence, and\nrollups must deduplicate `AttendancePunch.serviceTimeEntryId`.\n`AttendanceSegment` (`attendance_segments`) is a running work interval inside a\npunch: queryable `workRef*`/`taskRef*`/`kind` columns, one open per punch\n(`openSlot`), closed by `startBreak`/`punchOut`/`autoClose`, never overlapping\nbreaks or each other, immutable once closed. It creates no `ServiceTimeEntry`.\nClosed punches, breaks and segments change only through\n`AttendanceCorrectionService` (`attendance/correction-service.ts`), which sets a\nper-instance capability (`attendance/write.ts`, not exported) that lifts the\nmodels' closed-interval checks for one save, validates the whole punch layout,\nrecomputes durations and appends an `AttendanceCorrection` (append-only ledger,\nno generated operations; only a first acknowledgement by the subject may update\na row). An open punch accepts only a `startedAt` correction, closed breaks/segments inside it, and `addPunch({ open: true })`; the open break/segment and closing stay `AttendanceService`'s. Never export `write.ts`. Punch `source`/`deviceRef*` and break `kind` are immutable except through corrections; `autoCloseReason` is write-once via `persistAutoClose`. A confirmed `PeriodTimecard` is honoured by the\nopt-in `confirmedTimecardGuard` (`rollup/correction-guard.ts`), which refuses\nwhile a card is submitted or confirmed. `PeriodRollupService.reopen()` returns\nsuch a card to `open` and appends a `TimecardReopening` (`timecard_reopenings`,\nappend-only, service-only via `rollup/write.ts`); `submit()` is the optional\nopen -> submitted step (`requireSubmission` makes it mandatory) (#3950).\n`PeriodRollupOptions.attribution` (`'clip'` default | `'start'`) decides crossing\nsources: `'start'` keeps a punch (breaks and segments included) or bounded entry\nwhole in the period containing its start, unclipped, and excludes it from the\nnext (`attributeSource` in `rollup/calculation.ts`) (#4028).\nSegment transitions live in `attendance/segments.ts`; compose them with other\nattendance writes via `AttendanceService.withTransaction`. Any new attendance\ntable must be added to the `includeObjects` lists in the `*.optional.test.ts`\nfiles.\n\n## Models\n\n| Model | Table | Notes |\n|---|---|---|\n| `ServiceTimeEntry` | `service_time_entries` | `workRefType` + `workRefId`, `participantKind` (`human` \\| `agent`), `participantProfileId` / `agentRef`, `source`, `startedAt` / `endedAt` / `durationSeconds`, status machine, `correctionOfId` |\n| `ServiceChargeSnapshot` | `service_charge_snapshots` | Client charge, integer minor units (#2401), one per entry |\n| `ServiceCompensationSnapshot` | `service_compensation_snapshots` | Provider earning, integer minor units, one per entry |\n\n`ServiceEvidenceService` records, submits, approves (writing both snapshots\nthrough a `ServiceCommercialResolver`), and corrects. `smrt-projects` supplies\n`SubscriptionServiceCommercialResolver` for #1925 pricing.\n\n## Generated write actions (#4001)\n\nGenerated CRUD writes stay closed. `ServiceTimeEntryCollection` hosts the only\ngenerated writes, `record` / `submit` / `approve` (`POST\n/servicetimeentries/<action>`, body `{ entryId }` for submit/approve), on REST\nonly; MCP and CLI stay read-only until per-operation MCP authorization (#4004).\nA consumer that closes a model also closes its collection's generated CRUD;\n`ServiceTimeEntryCollection`'s own `forWork` / `pendingApproval` stay open\nuntil the collection class is closed with `registerOverride` too. Surface tests\nmatch `<model>collection_` tool names as well as `<model>_` (#3468).\n`services/time-entry-actions.ts` delegates each to `ServiceEvidenceService`, so\nthe trail and snapshots are set in one place. `services/action-principal.ts`\ntakes identity from the tenancy ALS, then the smrt-users session ALS, never the\nbody; authorizes `servicetimeentries.<action>` with `assertOperationPermission`\n(system context never bypasses; `approve` refuses super-admin bypass); and\nstamps the user's linked profile. Only the participant records/submits their\nown time unless the caller holds `approve`. `approve` prices with the host's\n`setServiceTimeEntryCommercialResolver()` — a factory called per request with\nthe collection's options, so a writing resolver shares the request executor;\nwithout one it is a 409 and writes nothing. The runtime REST transport gates\na collection-hosted action on the ITEM's `api.include`, SvelteKit on the\ncollection's, so both list the actions;\na consumer closing them overrides both registrations. The collection and both\nsnapshot collections carry their own `@smrt()` surface: undecorated, they\npublished generated create/update/delete MCP tools. `reject` waits for #3994.\n\n## Recipe\n\n`src/recipes.ts` declares one `SmrtRecipe`, `timesheets.time-entries`\n(label \"Time entries\", section `work`), emitted into the built\n`manifest.json` and `smrt-knowledge.json` `recipes`. It shows\n`ServiceTimeEntry` alone, with the generic work reference, evidence, agent\nand approval-trail fields hidden; attendance and rollup models stay out of\nit. `status` and its trail are hidden and locked, and the help never says to\nset them: `save()` accepts a bare transition, so submit and approve must go\nthrough `ServiceEvidenceService` (the recipe declares no surface for that). Help lives in `src/time-entries.recipe.md` (#3591); keep it and the recipe\nin plain language. See [core recipes](../core/agents/recipes.md).\n\n## Invariants\n\n- **No domain foreign key on the base.** Work context is the generic\n `workRefType` (qualified class name) + `workRefId`. A domain adds its own\n context by subclassing over the same table — smrt-support's\n `ServiceTimeEntry` adds `caseId` / `specialistId` and extends\n `frozenFieldNames()` so they freeze on approval.\n- **Entries carry no rate.** Money lives only on the snapshots, `= 0`\n integer initializers (INTEGER columns). Never `= 0.0`. Snapshot `save()`\n rejects an `amount` that is not a safe integer (`VALIDATION_INVALID_VALUE`),\n so SQLite cannot silently store `19.99`; fix a fractional resolver at its\n source rather than rounding here.\n- **Approved evidence never changes.** `save()` enforces\n `SERVICE_TIME_ENTRY_STATUS_TRANSITIONS` and compares the frozen fields with\n the stored row. A correction is a new row (`correctionOfId`), the original\n flips to `corrected`. Snapshots are immutable after their first save.\n- **Old qualified names are aliases.** Each model declares its pre-#3288\n `@happyvertical/smrt-projects:*` name in `previousQualifiedNames` (#3338;\n `packages/core/agents/model-moves.md`). smrt-projects must never list these\n classes in its manifests again (`CONFIG_QUALIFIED_NAME_ALIAS_COLLISION`),\n and no subtype (smrt-support's, or a consumer's closing subclass) may\n redeclare an alias (two claimants). Known gap: a same-named subtype that\n replaces a moved class drops that class's alias, and the replaced class's\n own `@happyvertical/smrt-timesheets:*` name stops resolving too — guidance\n for apps with smrt-support must name `@happyvertical/smrt-support:ServiceTimeEntry`.\n Support's vitest environment keeps both classes registered, so verify this\n against built packages, not package tests. `projects/src/__tests__/legacy-qualified-identity.test.ts` pins\n the projects-only contract.\n- **Table names are a compatibility contract.** `service_time_entries` and the\n two snapshot tables hold existing production rows from smrt-projects /\n smrt-support; renaming any of them, or adding a discriminator, is a data\n migration.\n\n## One table family\n\nClasses may share a table only as one inheritance chain. Core resolves\nsame-named subclasses by constructor ancestry at runtime and qualified manifest\nancestry at build registration; the deepest subtype wins across N levels.\nSibling or unrelated types cannot share the table. `smrt-projects` re-exports\nthe base; `smrt-support` subclasses it directly.\n\nConsumers closing package routes should call\n`ObjectRegistry.registerOverride(qualifiedName, { api: false, mcp: false,\ncli: false, tenancy: { mode: 'required' } })` after importing the model and before\nconstructing transports. This tightens the existing registration without a new\nsubtype. See core's README for startup order, static route generation, and reset\nsemantics. `src/__tests__/consumer-closed-surface.test.ts` also covers existing\nmanifest subclass consumers. The snapshots' explicit `time_entry_id` conflict\nkey is not rewritten when a consumer requires tenancy; widening it remains a\nconsumer schema change.\n\nSubclasses restate their fields: package-isolated manifest scanning does not\nread dependency sources, so an unrestated subclass manifest has no columns.\n\n## Svelte UI\n\n`./svelte` exports `TimeEntryCard`, `TimeEntryList`, `TimeSummary`,\n`DurationDisplay`, and `TimeEntryApprovalQueue` (props-driven, plain view\nobjects, `smrt-ui` primitives, `smrtRawPrimitives: \"strict\"`). smrt-projects\nand smrt-support re-export them from their own `./svelte` subpaths.\nCard/list/summary message keys keep their `projects.*` namespace: tenant\nlanguage overrides are stored by key, and renaming would orphan them. New\nkeys use `timesheets.*`.\n\nDisplay components keep the legacy one-decimal hours format by default. A\nconsumer that retains more precise source evidence supplies `hoursFormatter`;\ncard/list/queue detail snippets carry domain evidence without widening the base\nview. ApprovalQueue's optional action snippet replaces its callback controls so\nthe host can own a native form, request identity, authorization and retry flow.\nDisplay currency codes are open ISO strings and amounts remain integer minor\nunits; never put private rates into a view that should not expose them.\n\n## Validation\n\n```sh\npnpm --filter @happyvertical/smrt-timesheets test\npnpm --filter @happyvertical/smrt-timesheets test:postgres\npnpm --filter @happyvertical/smrt-timesheets typecheck\npnpm --filter @happyvertical/smrt-timesheets build\npnpm --filter @happyvertical/smrt-projects test\npnpm --filter @happyvertical/smrt-support test\n```\n\nSchema-affecting edits must keep `service-time-entry-compat.test.ts` (projects,\nsupport) and `service-time-entry-migration-plan.test.ts` (support) green: a\npre-move table must plan no DDL.\n\n## Period rollup\n\nLayer 3 lives under `src/rollup/` and the `./rollup` subpath. See\n[period-rollup.md](agents/period-rollup.md) for resolver, source clipping,\nconfirmation, attributed adjustments, isolation, and transaction contracts.\nTimecards and adjustments have closed generated surfaces and service-only\nmodel writes; never bypass the instance capability with generic persistence.\nPayroll and jurisdiction policy are not built in: a consumer supplies a `PeriodRulesResolver`, and this package records time evidence and snapshots, not payroll.\n\nReplay hashes the tap AS SENT (action, device `at`, paid, work, and `source`/`device`/`kind` only when present, so older receipts still match) so retries match;\n`replay(taps, { resolveAt, now, refusal })` only changes the stored time, reason and the\npunch's `recordedOffline`/`reviewRequired` (both default true and stay set across\nlater taps; false never clears an earlier flag). A reviewed correction clears\n`reviewRequired`. Never hash the resolved time. Work actions (`startWork`, `switchWork`,\n`stopWork`, `resumeWork`) run in the tap's transaction via `apply()`. Refusals store\n`code` from `AttendanceConflict.code`; give new conflicts a code.\n",
2699
2699
  "moduleDocs": [
2700
2700
  {
2701
2701
  "path": "agents/period-rollup.md",
@@ -0,0 +1,101 @@
1
+ ---
2
+ type: "Component"
3
+ title: "@happyvertical/smrt-timesheets"
4
+ description: "Shared time entries for the SMRT framework: who worked how long on what, approval and correction, with immutable charge and compensation snapshots"
5
+ status: "stable"
6
+ generated:
7
+ by: "smrt/scripts/generate-knowledge-okf.ts"
8
+ at: "2026-11-05T15:48:19Z"
9
+ audience: "internal"
10
+ evidence_level: "source-inspected"
11
+ source_revision: "sha256:33b3f89bd3d41c7ae0d84d09b08b8d3566ab274ea8138882f952ac2ae402ef8e"
12
+ sources:
13
+ - id: "manifest"
14
+ resource: "https://github.com/happyvertical/smrt/blob/main/packages/timesheets/package.json"
15
+ title: "@happyvertical/smrt-timesheets package manifest"
16
+ - id: "agents"
17
+ resource: "https://github.com/happyvertical/smrt/blob/main/packages/timesheets/AGENTS.md"
18
+ title: "@happyvertical/smrt-timesheets packages/timesheets/AGENTS.md"
19
+ - id: "agents-2"
20
+ resource: "https://github.com/happyvertical/smrt/blob/main/packages/timesheets/agents/period-rollup.md"
21
+ title: "@happyvertical/smrt-timesheets packages/timesheets/agents/period-rollup.md"
22
+ ---
23
+
24
+ # @happyvertical/smrt-timesheets
25
+
26
+ ## Package
27
+
28
+ - Directory: `packages/timesheets`
29
+ - Private: no
30
+ - Object source: `domain-artifact`
31
+ - Evidence level: `source-inspected`
32
+
33
+ ## Documentation
34
+
35
+ - `packages/timesheets/AGENTS.md`
36
+ - `packages/timesheets/agents/period-rollup.md`
37
+
38
+ ## Framework dependencies
39
+
40
+ - [smrt-core](/components/smrt-core.md)
41
+ - [smrt-tenancy](/components/smrt-tenancy.md)
42
+ - [smrt-types](/components/smrt-types.md)
43
+ - [smrt-ui](/components/smrt-ui.md)
44
+ - [smrt-users](/components/smrt-users.md)
45
+ - [smrt-vitest](/components/smrt-vitest.md)
46
+
47
+ ## SDK dependencies
48
+
49
+ - `@happyvertical/sql`
50
+
51
+ ## Exports
52
+
53
+ - `.`
54
+ - `./attendance`
55
+ - `./manifest`
56
+ - `./manifest.json`
57
+ - `./rollup`
58
+ - `./svelte`
59
+ - `./ui`
60
+
61
+ ## Models
62
+
63
+ - `AttendanceBreak` (collection `attendancebreaks`, extends `SmrtObject`)
64
+ - `AttendanceBreakCollection` (collection `attendancebreaks`, extends `SmrtCollection`)
65
+ - `AttendanceCorrection` (collection `attendancecorrections`, extends `SmrtObject`)
66
+ - `AttendanceCorrectionCollection` (collection `attendancecorrections`, extends `SmrtCollection`)
67
+ - `AttendancePunch` (collection `attendancepunches`, extends `SmrtObject`)
68
+ - `AttendancePunchCollection` (collection `attendancepunches`, extends `SmrtCollection`)
69
+ - `AttendanceReplay` (collection `attendancereplays`, extends `SmrtObject`)
70
+ - `AttendanceReplayCollection` (collection `attendancereplays`, extends `SmrtCollection`)
71
+ - `AttendanceSegment` (collection `attendancesegments`, extends `SmrtObject`)
72
+ - `AttendanceSegmentCollection` (collection `attendancesegments`, extends `SmrtCollection`)
73
+ - `PeriodTimecard` (collection `periodtimecards`, extends `SmrtObject`)
74
+ - `PeriodTimecardCollection` (collection `periodtimecards`, extends `SmrtCollection`)
75
+ - `ServiceChargeSnapshot` (collection `servicechargesnapshots`, extends `SmrtObject`)
76
+ - `ServiceChargeSnapshotCollection` (collection `servicechargesnapshots`, extends `SmrtCollection`)
77
+ - `ServiceCompensationSnapshot` (collection `servicecompensationsnapshots`, extends `SmrtObject`)
78
+ - `ServiceCompensationSnapshotCollection` (collection `servicecompensationsnapshots`, extends `SmrtCollection`)
79
+ - `ServiceTimeEntry` (collection `servicetimeentries`, extends `SmrtObject`)
80
+ - `ServiceTimeEntryCollection` (collection `servicetimeentries`, extends `SmrtCollection`)
81
+ - `TimecardAdjustment` (collection `timecardadjustments`, extends `SmrtObject`)
82
+ - `TimecardAdjustmentCollection` (collection `timecardadjustments`, extends `SmrtCollection`)
83
+ - `TimecardReopening` (collection `timecardreopenings`, extends `SmrtObject`)
84
+ - `TimecardReopeningCollection` (collection `timecardreopenings`, extends `SmrtCollection`)
85
+
86
+ ## MCP tools
87
+
88
+ - `attendancebreak_get`: get on `@happyvertical/smrt-timesheets:AttendanceBreak`
89
+ - `attendancebreak_list`: list on `@happyvertical/smrt-timesheets:AttendanceBreak`
90
+ - `attendancepunch_get`: get on `@happyvertical/smrt-timesheets:AttendancePunch`
91
+ - `attendancepunch_list`: list on `@happyvertical/smrt-timesheets:AttendancePunch`
92
+ - `attendancesegment_get`: get on `@happyvertical/smrt-timesheets:AttendanceSegment`
93
+ - `attendancesegment_list`: list on `@happyvertical/smrt-timesheets:AttendanceSegment`
94
+ - `servicechargesnapshot_get`: get on `@happyvertical/smrt-timesheets:ServiceChargeSnapshot`
95
+ - `servicechargesnapshot_list`: list on `@happyvertical/smrt-timesheets:ServiceChargeSnapshot`
96
+ - `servicecompensationsnapshot_get`: get on `@happyvertical/smrt-timesheets:ServiceCompensationSnapshot`
97
+ - `servicecompensationsnapshot_list`: list on `@happyvertical/smrt-timesheets:ServiceCompensationSnapshot`
98
+ - `servicetimeentry_get`: get on `@happyvertical/smrt-timesheets:ServiceTimeEntry`
99
+ - `servicetimeentry_list`: list on `@happyvertical/smrt-timesheets:ServiceTimeEntry`
100
+ - `servicetimeentrycollection_forwork`: forWork on `@happyvertical/smrt-timesheets:ServiceTimeEntryCollection`
101
+ - `servicetimeentrycollection_pendingapproval`: pendingApproval on `@happyvertical/smrt-timesheets:ServiceTimeEntryCollection`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-timesheets",
3
- "version": "0.60.9",
3
+ "version": "0.60.10",
4
4
  "smrtJsdoc": "strict",
5
5
  "description": "Shared time entries for the SMRT framework: who worked how long on what, approval and correction, with immutable charge and compensation snapshots",
6
6
  "type": "module",
@@ -39,12 +39,12 @@
39
39
  }
40
40
  },
41
41
  "dependencies": {
42
- "@happyvertical/smrt-core": "0.60.9",
43
- "@happyvertical/smrt-tenancy": "0.60.9",
44
- "@happyvertical/smrt-types": "0.60.9",
45
- "@happyvertical/smrt-ui": "0.60.9",
46
- "@happyvertical/smrt-users": "0.60.9",
47
- "@happyvertical/sql": "^0.111.3"
42
+ "@happyvertical/smrt-core": "0.60.10",
43
+ "@happyvertical/smrt-tenancy": "0.60.10",
44
+ "@happyvertical/smrt-types": "0.60.10",
45
+ "@happyvertical/smrt-ui": "0.60.10",
46
+ "@happyvertical/smrt-users": "0.60.10",
47
+ "@happyvertical/sql": "^0.111.5"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "svelte": "^5.57.0"
@@ -55,7 +55,7 @@
55
55
  }
56
56
  },
57
57
  "devDependencies": {
58
- "@happyvertical/smrt-vitest": "0.60.9",
58
+ "@happyvertical/smrt-vitest": "0.60.10",
59
59
  "@sveltejs/package": "^2.5.8",
60
60
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
61
61
  "@types/node": "24.13.6",