Readme

Sol-37 Services Desk and Household Operations Platform Public weekend IT practice and intake (October 1, 2026)

Executive Summary

Sol-37 Services Desk and Household Operations Platform Public weekend IT practice and intake (October 1, 2026) /services is David Lones's independent, appointment-based technology practice, initially Saturday/Sunday only. It is not an LLC or a continuously staffed MSP.

Sol-37 Services Desk and Household Operations Platform

Public weekend IT practice and intake (October 1, 2026)

/services is David Lones's independent, appointment-based technology practice,
initially Saturday/Sunday only. It is not an LLC or a continuously staffed MSP.
On-site and suitable remote work are offered; drop-off is not advertised.
Pricing is confirmed before work begins. The walnut/paper/orange public page
keeps the embedded intake above projects and the printable résumé.

text
PUBLIC CUSTOMER /services
    | service request + appointment preference
    v
Cloudflare Worker
    +--> D1 canonical source request (SR-...), committed before transport
    +--> browser acknowledgement; owner documentation email
    +--> minimal Queue event (no full problem description)
              v
        signed local bridge
              | Owner-authenticated cloud source lookup
              | Owner-authenticated local intake import
              v
        local IT projection in existing service_requests queue
              +--> customer receipt email (SMTP acceptance tracked separately)
              +--> private notifications
              +--> owner review / information request / appointment offer
              +--> explicit customer acceptance + owner confirmation
                        v
                 linked private IT work item

D1 and local SQLite are not interchangeable. Migration 0026 adds structured
intake_json, source identity, import time, synchronization status, appointment
state and work link to the existing table, plus a service-request audit table.
source_type=public_service_request, source_id=SR-... and the unique source
index identify the projection. The local row retains the same visible SR ID.
Replayed imports never overwrite local handling or create another case.

Owner-only GET /api/service-requests/admin/cloud-intake[/SR-...] reads D1
before ordinary household forwarding. It supplies only source fields to the
bridge; the list shows imports still pending. Owner-only local
POST /api/service-requests/admin/intake-import accepts that authenticated
snapshot and cannot set handling, appointment confirmation or work state.
This endpoint is unavailable in the cloud runtime. No public credentials exist.

Bridge acknowledgment requires the local import to succeed. Queue failures do
not undo cloud acceptance. Existing queue retries and the 15-minute cron requeue
pending projections, including after queue-send failure or dead-letter exhaustion.
The Owner IT queue displays synchronization checks and pending SR identifiers.
Cloud notify_status=delivered means the bridge accepted the projection, not
that a phone banner appeared. Customer email sent means SMTP accepted it;
neither a queue nor SMTP acknowledgment establishes recipient delivery.

src/appointment-policy.js owns available days, blocked dates, time windows,
service modes, timezone and pricing wording. Hours, duration and capacity are
reserved configuration slots, not enforced booking guarantees. This is request
scheduling, not automatic reservation. Dates are validated server-side and
client-side. Administrative wall times resolve in America/Chicago; ambiguous
and nonexistent DST times are rejected. No date passage changes lifecycle state.

Existing statuses remain supported. New appointment states are requested,
offered, confirmed, rescheduled, cancelled, declined and completed. Review uses
triaged; information requests use waiting_customer; follow-up is explicit.
Owner confirmation requires customerConfirmed:true, a valid weekend time and
expectedUpdatedAt. It creates or updates one deterministic private IT work
item, IT-SR-.... Cancellation, rescheduling, work start and completion update
that linked item through the same transaction. Customer communication arranging
the appointment remains manual; changing a status does not send an offer.

Validation: npm test includes SQLite-backed Worker integration tests in
test/public-intake.test.js; npm run check checks the Worker syntax. Apply
0026 to Wrangler local D1, remote D1 and authoritative local SQLite separately.
Restart sol37-household-local.service and service-request-bridge.service for
their code changes; deploy the Worker and sync this README mirror. Private
pre-change backups for this deployment are on the root filesystem under
~/.local/state/sol37/services-rework-20261001/, explicitly chosen because the
SOL data drive was full. This is narrower than a complete runtime backup.

Deployment verification (October 1): Worker version
1bbcbfdb-4965-417e-bb50-88d691a526fe; all 42 Node tests passed. Real browser
checks covered 390, 412, 768, 1366 and 1920 pixel widths, touch targets, print,
reduced motion, conditional contact fields, weekday rejection and preserved
text/idempotency after a simulated transport error. The existing household
command-view regression passed four viewport sizes. Production Turnstile and
the public form created controlled test SR-20261001-15A7098FC1; D1 showed
requested/synced, one local projection existed, and a signed replay left the
count at one with zero linked work items. The browser displayed the SR and the
not-confirmed acknowledgement. Bridge popup/private-push receipts succeeded;
customer receipt email was SMTP-accepted. The test was then archived through
the normal request API without implying an appointment or completed work.
Unauthenticated private endpoint checks returned 401. Both D1 migration lists
were current; local SQLite integrity checks passed before and after.
The final full-runtime backup attempt failed with ENOSPC before producing a
new manifest. The separate before/after SQLite backups and remote D1 SQL
exports succeeded; they do not establish a complete runtime snapshot.

Household dashboard guide

Current household console

The lower command surface is rendered by household-console.js and its scoped
stylesheet, mounted below the existing sale-preparation appointment row.
operations.householdConsole owns phase, room categories, hosting checks and
explicit TV-safe team/status context. Update it through revision-bound
PATCH operations. Its action IDs reference canonical work_items; completing
an action does not automatically certify room readiness. Staff references resolve
existing profiles without exposing contact details. src/household-console.js
projects only presentation fields through the existing GET command-display.
The project tax record remains the source for current full payoff, separately
confirmed fees, enforcement-hold amount and payment-plan status; historical quotes
are never substituted for unknown current amounts.

appointment-state.js applies the operations document's explicit
appointmentPresentation.pastDefault policy against the current Central instant.
Canceled, rescheduled, missed and other recorded non-completion states win over
the past-attendance default. High-priority upcoming medical records drive the
Motherly duties card and prominent timeline label; rendering has no date-specific
appointment rules. Care preparation references canonical pending work items.

QR and Vision remain body-level floating overlays. Shared Hide cards, per-overlay
manual hide, pointer fade/eight-second idle return, QR rotation, and real local
Vision snapshots remain independent of the grid. Browser positions are not saved.
The fleet compositor retains its existing single-camera presence gate and
motion-triggered temporary hide/fade-back cycle; browser pointer-idle behavior is
distinct from fleet camera behavior. Camera frames remain LAN-only.

Verify with npm test, the presence/attention Python suites and
node scripts/verify_household_console.cjs. The browser probe uses the LAN site
for real camera frames, checks four viewport widths, decodes the current QR
without logging its content, and checks read-only guest authorization.

Transition presentation

An Owner-provided reconciliation can replace the active clearing headline with
operations.householdConsole.transition (version 1), saved through the existing
revision-bound operations API. The explicit projection in
src/transition-dashboard.js drops unlisted fields before command-display
exposes the presentation. The browser's transition-dashboard.js renders the
mission, Chicago calendar-day countdowns, milestones, workstreams, and detailed
reconciliation. Private project records and canonical work remain separate.

The October 1 clearing percentage stays a historical target. This presentation
does not show the old automatic schedule as current verified completion, and
its Halloween countdown does not establish a property closing or vacancy date.
Opening hours and appointments marked as planning targets remain unconfirmed.
Owner-authored content retains its source date and independent-verification
limit. Updates do not change employment, assignments, timekeeping, or settlement.

The October extension uses transition.timelineEnd and date-derived SVG positions.
Halloween remains anchored to October 31; the estate sale is an inclusive three-day
interval ending at midnight after its last day. The date-only axis is independent
of daylight-saving-hour length. Historical percentages never certify readiness.
The Record Player renderer receives the SVG viewBox, date range and bar geometry
with each captured frame rather than drawing against the former October endpoint.
Appointment controls remain available as compact dots and expandable history.

command-display defaults to a shared projection. Only a non-display Owner request
receives private transition rows, document access records, care and appointments.
Shared transition rows require explicit visibility: "display"; shared schedules
require staff/public visibility. Display loads use only the shared command view,
not the private project, clinical, contact or timeline collections. Clinical source
links belong in the Owner document-access section, never in public static files.
Access findings record the attempted operation, account, time and actual result;
an empty listing is distinct from denial and modification time alone is not proof
of a content revision. Project extensions retain earlier state and evidence.
Inventory uses migration 0027_inventory_sale_fields.sql for nullable integer-cent
asking prices, tag colors and independently selected sale states. Unknown prices
stay unknown; green requires a price; yellow/red do not infer a disposition or
payment. Inventory offers checkout lookup by immutable printed ID, item or location.
This does not process payments. No new dependency or background-check cadence
change is needed.

Guide reviewed: October 19, 2026. This is the operating and maintenance
README for the household dashboard. The guide describes the current interface
and implementation; it is not a live record of staffing, appointments, payments,
care status, completed work, or service health. Read those facts in the authenticated
dashboard with their source dates.

The desk brings daily priorities, work, people, documents, schedules and handoffs
into one place. It also presents the house-transition plan, its projected progress,
and the separate evidence needed to declare actual readiness. It shares a shell
with the IT service desk, while retaining role-specific access and workflows.

Open the Services Desk with your assigned
access profile. On the workstation, the local entry is
http://127.0.0.1:8888/services/desk/. An available local page does not make every
connected service available offline: cloud intake, remote messages, mail sync and
remote display transport have their own dependencies.

This file is canonical at services-platform/README.md. The website copy at
www/services/desk/README.md is synchronized with npm run sync:readme.
Both copies must remain suitable for public reading: no credentials, access QR
images, private communications, clinical records or private evidence exports.

Guide contents

Daily operating workflow

1. Open Household Ops → Command brief. Check the displayed date, priority,
latest operating brief, next action, appointments and information freshness.
2. Open Plan & updates for the dated transition plan, progress correction,
staffing planning, readiness checklist, communications audit and event log.
3. Use Work board for actionable work: record ownership, status, due time,
dependencies and explicit staff assignments. A planning scenario is not an
assignment, and a scheduled visit is not evidence that work started.
4. Use Schedule for appointments, planned focus and routines. Record a
cancellation or reschedule explicitly; do not leave a cancelled visit looking
like an active booking or erase its prior history.
5. Record actual progress with its source. A percentage estimate, a completed
task, a timer entry and an independently verified readiness condition are
different records; update the one the evidence actually supports.
6. End the work period with a Handoff: what changed, what remains pending,
decisions needed, dependencies and the next responsible person. Stop any
active work timer and preserve the actual outcome of scheduled work.

Use Help & tour for the controls available to your role. On smaller screens,
the top action rail scrolls sideways. Command brief content scrolls beneath the
section navigation on desktop; on phones, normal document scrolling is used.
Changing sections returns the section to its top.

Dashboard sections

| Section | What belongs here | Important distinction |
| --- | --- | --- |
| Command brief | Current operating picture, next action, transition card, appointments and recap entry | The dated priority and operating brief have separate sources |
| Housing | Housing and move-related operating context | A possible option is not a confirmed placement |
| Work board | Actions, owners, statuses, due dates, dependencies, recurring work and assignments | Work records are the action source; schedule prose does not replace them |
| Time clock | Actual start/stop entries and estimated pay | A timer or rate calculation is not payroll settlement |
| Timeline | Dated events and source-qualified updates | An event records what was reported or occurred; it is not a future plan |
| Schedule | Appointments, daily focus, routines, cancellations and rescheduling | Scheduled, arrived and completed remain distinct |
| Staff & growth | Staff and candidate profiles, lifecycle and permitted assignments | Creating a profile or issuing access does not hire someone |
| People | Private external contacts and interactions | Contacts are distinct from staff and credentials |
| Projects | Durable project context, relationships, dependencies and evidence | Proposed terms remain proposed until supported by accepted records |
| Inventory | Persistent items, containers, identifiers and disposition | Unknown ownership or disposition stays under review |
| Protocols | Shared working rules and escalation guidance | Follow the version visible to the current role |
| Handoffs | Continuity notes for another person or work period | A handoff is not a substitute for updating canonical task status |

Visibility and available actions vary by role. The interface does not expose all
of these sections to every user.

Roles and shared views

| Role or view | Intended access |
| --- | --- |
| Owner | Full administration, operational decisions, credentials, readiness verification and permitted private records |
| Operator | Broad operational editing, including total progress; no Owner-only credential or ledger administration |
| Coordinator | Explicitly granted task/material work and attributed updates; broader private collections are not granted automatically |
| Active Staff Guest | Assigned work and permitted task actions; own eligible work timer; role-visible schedule and handoffs |
| Candidate or preview guest | Read-only planning context permitted by that credential; no implied employment or work assignment |
| Shared command/display view | Fleet-visible brief, transition and appointment presentation; viewer controls do not grant administrative writes |

The shared command display is an explicit read-access path. It does not mean
that every underlying private collection or mutation endpoint is available.
Likewise, a browser's display_viewer presentation is not a new server credential
role. Managed displays may use a separately revocable Operator display credential;
the audience-facing generic QR uses a distinct preview credential.

Authorization is enforced by the API, not just by hiding buttons. Adjust total
progress
is available to Owner and Operator. A staff, coordinator or display
view must not be assumed to have that permission merely because it shows the bar.
Do not put tokens into documentation, screenshots or command-line arguments.

Progress, targets and pauses

The dashboard has three different progress concepts:

| Measure | Meaning | What changes it |
| --- | --- | --- |
| Scheduled total | Owner-adjustable projected progress along the transition schedule | Manual total correction and automatic active-hour increases |
| Daily focus progress | A dated estimate for a particular day's focus, when supplied | An explicitly recorded daily estimate |
| Checklist-verified readiness | Readiness conditions actually checked off | Verification of individual criteria and the Owner's final walkthrough action |

The prominent bar currently says “verified total,” but its numeric value follows
the scheduled-total model.
Read the adjacent schedule note with that label.
Automatic movement is not new evidence of work, and entering 100% does not mark
the house sale-ready. Final readiness is a separate Owner verification action.

The saved schedule verified on September 30, 2026 has these targets:

| Setting | Value |
| --- | --- |
| Time zone | America/Chicago / Central time |
| Daily automatic increase window | 5:00 PM inclusive to 10:00 PM exclusive |
| Recurring hold | 10:00 PM through 5:00 PM the following day |
| Intermediate target | 73% by October 1, 2026 at 10:00 PM Central |
| Final target | 100% at midnight starting October 31, 2026 |
| Final active window before Halloween | October 30, 5–10 PM; 100% holds from 10 PM through the midnight target |

These are the configured dates, not a claim that the work will actually finish by
then. The live saved schedule takes precedence over this dated documentation.

Only time inside the active window advances the curve. Overnight, daytime and
weekend holds use the same rule; there is no weekday-only exception. The times
follow the Chicago calendar, including daylight-saving changes. At 5 PM the
curve resumes continuously from the held value, without a catch-up jump. The
recurrence is evaluated from saved schedule data; it does not depend on a cron
job, a reminder message, or a browser staying open overnight.

Below a future intermediate target, the remaining active hours are distributed
linearly toward that target. The final segment is exponential in active time.
For an ordinary positive baseline p, final growth is
p × (100 / p) ^ fraction, where fraction is elapsed active time divided by
remaining active time through the final deadline. A zero baseline after the
intermediate deadline uses a normalized exponential curve that can rise from
zero. A baseline of 100% remains at 100%.

The main card floors the percentage to a whole number; the correction form
shows the current value rounded to two decimals. A stored fractional value may
therefore appear differently in those two places without being a data conflict.

Making a manual progress correction

1. Open Command brief → Plan & updates → Adjust total progress.
2. Enter a value from 0 through 100, including decimals if useful.
3. Enter a short reason explaining the correction.
4. Read the preview beneath the fields. It explains whether the new baseline
will approach the intermediate target or use the exponential final segment.
5. Select Save total progress and check the saved confirmation.

Saving changes the current baseline immediately, even during a daily hold.
Automatic movement still obeys the daily active window and any existing future
one-time hold. Ordinary percentage edits preserve the milestone percentage,
milestone date and final deadline. The reason, actor, time and change are retained
in the audit history; checklist state and labor records are not changed.

| Example correction | Expected automatic behavior |
| --- | --- |
| 50% before the future 73% milestone | Hold until the next active window if paused, then rise toward 73% using remaining active hours |
| 74% before that milestone | Treat 73% as already exceeded; grow exponentially from 74% during active hours toward the final target |
| 90% before that milestone | Grow exponentially from 90%; do not raise the 73% target or stall until its date |
| Exactly 73% before its date | Begin the final exponential segment during active hours; retain the target record |
| Downward correction before the target | Recalculate the remaining linear rise from the corrected value |
| Any correction after the intermediate date | Use the exponential final segment; do not invent another intermediate milestone |
| 100% | Hold the total at 100%; readiness conditions and final verification remain separate |

Edits above the target do not imply that the target's underlying work was
verified. “Target reached” describes the scheduled-total value only.

Unsaved percentage and reason fields survive live refreshes within the current
page session. This is not a persistent draft guarantee across browser reloads or
sign-out. A draft retains the revision against which editing began. If another
write wins first, the server rejects the stale save rather than overwriting it.
The form retains the entry and explains the conflict. Refresh saved total
discards the unsaved correction and loads the latest value for review.

The form edits the total and reason; it does not expose target dates or pause
settings as editable controls. Authorized maintenance can update those settings
through the same revision-bound API. The current timed-hold API requires an
explicit resumeAt to be future-dated and before the intermediate milestone.
Percentage edits after the final completion date are rejected until the schedule
is replanned. An indefinite emergency pausedAt is a separate low-level setting,
not the recurring daily pause.

Keeping priorities and records consistent

A single visible sentence can have several neighboring sources. A priority change
is complete only when the affected records agree in meaning:

| Surface | Authoritative source |
| --- | --- |
| Transition card's dated priority | Bundled src/sale-prep-config.json tasks plus saved state.planOverrides fields |
| Owner operating headline and next action | operations.brief |
| Staff-safe operational instructions | operations.staffBrief |
| Dated staff-focus plan | operations.schedule, including focus and status |
| Transition staffing summary | Effective dated task fields and src/sale-prep-staffing.json fallback |
| Task completion and assignments | Canonical work records / transition state as appropriate |
| Saved total schedule | sale_prep_state.state_json.totalProgressSchedule, overriding the bundled default |

These surfaces need consistent facts, not necessarily identical wording: an
Owner decision and a staff-safe action can be expressed differently. Manual Owner
brief overrides remain authoritative over automatic brief generation.

For a cancellation, retain the previous plan in history and record the cancelled
outcome with its source. For carryover, update the destination day's plan and
matching schedule; leave displaced work pending without inventing another date.
Do not turn a reported stop time into exact billable hours or a completed room.

Communications and evidence

The dashboard can reconcile email, messages, household channels and connected
feeds. A manual review starts with a fresh mailbox sync and a bounded audit of the
named sources. Search terms must include the people and subjects relevant to that
day. Review the candidates before publishing summaries into the private desk.

Record source identity, source time, capture time and completeness. Keep the
original communication in its private archive and publish only the necessary,
source-qualified summary. An email notification excerpt is partial coverage, not
the whole conversation. A draft is not sent; an attachment reference is not proof
that someone received or reviewed it.

Use explicit distinctions throughout the desk:

Calendar-relative labels are derived from dated events in America/Chicago.
Keep original “today” or “tomorrow” wording in source history, while the current
dashboard resolves the date at render time. A passed deadline does not silently
complete, cancel or accept anything.

Displays, recaps and notifications

Display mode presents the same desk through managed browser/Cast surfaces.
The page owns the floating guest QR and local camera card. Presentation controls
include theme, hiding/showing cards and display mode. Display credentials are
separately revocable; the bootstrap Owner secret is not a display credential.

Events recap provides a transcript and browser playback. Owner broadcast
controls are available from the top Broadcast / Broadcast audio action and
Command brief → Events to date → Broadcast audio. Choose the permitted scope
and destinations before broadcasting. Play recap plays in the current browser;
Stop latest broadcast stops the identified recap job, not unrelated media.
Generating or playing a recap does not update its underlying records.

Notifications have independent delivery states. A queue acceptance, local ledger
entry, receiver session or healthy service does not by itself prove that a person
saw a message or that a screen is displaying the expected content. Verify the
channel-specific receipt. For screens, inspect a fresh frame and playback progress.
Do not restart a healthy display fleet merely to refresh documentation or inspect
the dashboard.

Architecture and authoritative records

text
Browser / managed display
  ├─ local site ───────────────────────────┐
  └─ public site → Worker admin forwarding ┤
                                          ▼
                         local household API on 127.0.0.1:19848
                         existing authorization + API handlers
                                          ▼
                              local household SQLite database

Public IT intake → cloud Worker → D1 / queue → signed local bridge → linked local IT projection
(separate path; not an automatic household database merge)

Household/admin authority is local SQLite behind sol37-household-local.service.
The public Worker forwards admin requests to that authority. Responses on this
path identify storage as x-sol37-storage: local-sqlite. Cloud D1 remains part of
the separate public intake path; it is not the current household source of truth.
Do not overwrite new local state with an old cloud export.

| Responsibility | Main implementation |
| --- | --- |
| Dashboard shell, navigation, operations editing | www/services/desk/index.html |
| Progress, calendar and priority calculations | www/services/desk/sale-prep-model.js |
| Transition card, correction preview and save UX | www/services/desk/sale-prep-card.js, sale-prep.css |
| Transition reads/writes and revision checks | services-platform/src/sale-prep.js |
| Dated task override validation and shared configuration resolution | services-platform/src/sale-prep-plan.js |
| Shared read-only presentation payload | services-platform/src/command-display.js |
| Default dated plan and staffing configuration | services-platform/src/sale-prep-config.json, sale-prep-staffing.json |
| API routing and authorization | services-platform/src/index.js and policy modules |
| Local service adapter and transactional SQLite access | services-platform/scripts/local-desk.mjs |
| Local authoritative database | ~/.local/state/sol37/household-local/household.sqlite |

The API prefix is /api/service-requests/admin/. Relevant endpoints are
GET/PATCH operations, GET/POST sale-prep, and GET command-display.
An operations write uses expectedRevision; a transition write uses revision
and an eventId. total-progress accepts the percentage and reason, with optional
authorized scheduling fields. A stale revision returns HTTP 409. Unauthorized
role access is rejected server-side. The progress schedule is saved data and is
shared by both normal and display views.

# Request routing and runtime boundaries

The browser is a static HTML/JavaScript application, not a server-rendered app.
Its authenticated fetch wrapper attaches a Bearer credential, parses JSON and
retains HTTP errors for the interface. Operations refresh normally runs every
60 seconds while the panel is visible, with editing/dialog and display-interaction
guards. The transition card recalculates time-derived values locally; its frequent
render ticks do not write a new percentage into SQLite.

The cloud Worker enables LOCAL_HOUSEHOLD_AUTHORITY=enabled. For the admin prefix,
it forwards the original method, body and authorization to the same-origin
/api/household-local/admin/ path. Site routing takes that request to the loopback
Node service. The local adapter normalizes the path back to the Worker API prefix
and calls the same src/index.js handler. It deliberately leaves that forwarding
flag unset to avoid a loop. This is one implementation of policy and business
logic running behind two entry points, not two independently writable household
databases. If local authority is unavailable, cloud admin access does not silently
fall back to stale D1 household data.

At service startup, scripts/local-desk.mjs bundles the handler and JSON defaults
with esbuild. It opens the existing SQLite database using Node DatabaseSync,
enables WAL, foreign keys and a five-second busy timeout, and adapts the D1-style
prepare/bind/first/all/run interface. DB.batch() uses BEGIN IMMEDIATE with
commit/rollback. Requests are limited to 1 MiB at the adapter. A configuration
file edit needs a service restart to rebuild that bundle; an API state edit does
not. The adapter refuses to silently create an uninitialized database.

| Store | Responsibility and write boundary |
| --- | --- |
| operations_state | One household document, revision and updater metadata; briefs, strategic schedule, protocols and handoffs |
| sale_prep_state | Transition checklist, achieved state, schedule override, dated plan overrides, labor and communications summaries; one revision |
| sale_prep_events | Revision-bound transition action history, including plan before/after values |
| work_items, staff_profiles, time_entries | Canonical actions, people lifecycle and actual timekeeping; do not replace these with schedule prose |
| Contact, project and inventory tables | Separate persistent objects and relationships, with their own endpoint validation |
| coordinator_task_grants, materials and audit tables | Explicitly scoped collaboration; a grant is not general administrative access |
| Admin token and QR-session tables | Credential hashes, scope, expiry and revocation; a person record alone grants nothing |
| local_notification_outbox | Local queue acceptance; not recipient delivery evidence |
| Cloud D1 and queues | Public intake and cloud transport state; not an automatic merge into household authority |

There is no transaction spanning separate operations and transition HTTP calls.
When one decision affects both, read both current revisions, perform each intended
write, reread both, and reconcile any conflict. Do not declare consistency after
only the first successful response. Schema changes belong in numbered migrations;
the new plan override uses existing state JSON and requires no schema migration.

# Authentication and visibility

Use Authorization: Bearer …. If supplied, the browser Origin must be allowlisted.
The handler hashes credentials, resolves their principal, checks revocation and
expiry, and enforces role, profile and capability restrictions. Bootstrap Owner
authentication is a separate fallback. Even a read can update token last-use
metadata. Never infer current database authority from legacy principal metadata
named d1; inspect routing and x-sol37-storage instead.

GET command-display intentionally exposes the authorized fleet presentation to
authenticated roles. It combines the mapped operating brief, schedule, transition
configuration/state and selected home-health display fields. It is not a write API
and is not equivalent to access to all private collections. Dated plan fields
are display-visible:
put only appropriate shared operational summaries there,
not raw medical records or private message transcripts.

Dashboard modification API

All paths below are relative to /api/service-requests/admin/. Send JSON for
writes. Reuse the existing endpoint for its object; there is no general-purpose
“modify arbitrary dashboard HTML or database row” endpoint.

| Surface | Endpoint | Editing contract |
| --- | --- | --- |
| Briefs, schedule, protocols, handoffs | GET, PATCH operations | Owner/Operator; full operations document and expectedRevision |
| Total progress and transition checks | GET, POST sale-prep | Action payload, current revision, unique eventId; action-specific roles |
| Existing dated priority and daily details | POST sale-prep, action plan-task | Owner; allowlisted field patch, reason, revision and event ID |
| Remove a dated task override | POST sale-prep, action plan-task-reset | Owner; restores current bundled task fields, audited |
| Shared display payload | GET command-display | Read-only; normal and display views resolve the same plan overrides |
| Canonical work | GET/POST work-items, PATCH work-items/:id | Object validation and role checks; read the collection to locate current work |
| Assigned staff activity | POST work-items/:id/actions | Explicit staff capability and assignment checks |
| Timekeeping | GET/POST time-entries, PATCH time-entries/:id | Actor/source constraints; Owner corrections, server-derived durations |
| People and lifecycle | GET/POST staff, PATCH staff/:id | Creation, hire, archive and restore have separate lifecycle checks/actions |
| Contacts, projects, inventory | Collection GET/POST; item GET/PATCH | Separate private object APIs, not staff-visible collections |
| Coordinator workspace | GET coordinator; routes below | Explicit subject scope and grants |

Other specialized routes in the technical reference cover incoming events,
home-health presentation, credential management and recap transport. Saving a
record does not implicitly send email, fax, messages or a broadcast.

# Read, change, compare and reread

This standard-library Python example defines a client and reads current state.
It loads the credential inside the process. Keep returned objects private; do not
print or save them under the public website directory.

python
import copy
import json
import uuid
from pathlib import Path
from urllib.request import Request, urlopen

base = "https://sol.system42.one/api/service-requests/admin/"
token = Path.home().joinpath(".config/sol37-services-admin-token.txt").read_text().strip()

def api(path, method="GET", payload=None):
body = None if payload is None else json.dumps(payload).encode()
request = Request(base + path, data=body, method=method, headers={
"Authorization": "Bearer " + token,
"Content-Type": "application/json",
})
with urlopen(request, timeout=30) as response:
return json.load(response)

operations_snapshot = api("operations")
transition_snapshot = api("sale-prep")

The following functions perform writes only when called. Substitute reviewed
values and use a fresh snapshot. An HTTP error should stop the workflow; catch it
in your application to display the API's error code without logging credentials.

python
def save_brief(snapshot, headline, next_action):
    document = copy.deepcopy(snapshot["operations"])
    document["brief"]["headline"] = headline
    document["brief"]["nextAction"] = next_action
    return api("operations", "PATCH", {
        "expectedRevision": snapshot["revision"], "operations": document,
    })

def correct_total(snapshot, percent, reason):
return api("sale-prep", "POST", {
"action": "total-progress", "revision": snapshot["revision"],
"eventId": str(uuid.uuid4()), "percent": percent, "note": reason,
})

def edit_daily_plan(snapshot, task_id, fields, reason):
return api("sale-prep", "POST", {
"action": "plan-task", "revision": snapshot["revision"],
"eventId": str(uuid.uuid4()), "id": task_id,
"patch": fields, "note": reason,
})

Locate task_id in transition_snapshot["config"]["tasks"]. For example,
fields can be {"title": "Document collection", "dailyCompletionPercent": 25,
"dailyCompletionStatus": "Owner estimate"}
. This records a daily estimate, not
overall verified completion. Reread sale-prep and command-display after a save
and check the expected field, revision and rendered date. Reread operations
after a brief/schedule save. A successful API response alone does not establish
that a remote physical display has refreshed.

# Operations contract

GET operations returns ok, revision, updatedAt, updatedBy and
operations. PATCH replaces the whole operations document; it is neither
JSON Patch nor recursive merge. Always preserve unedited keys and send the exact
revision just read. The handler checks an integer expectedRevision when supplied
and uses a database compare-and-swap; clients must not omit that field and assume
they have protected themselves from an old local snapshot.

Required sections are brief, staff, schedule, workstreams, staffingPlan,
protocols and handoffs; staff/schedule/workstreams must be arrays. The document
has a 120,000-character encoded limit. Schedule rows have unique IDs, date, title,
time, focus and validated status, with at most 200 rows. The time field is display
text, not an executable scheduling expression. Preserve manual brief override and
provenance fields when editing existing brief content.

# Transition actions and concurrency

GET sale-prep returns effective config, saved state, revision and the latest
30 events. Normal POST actions require an integer current revision and an event
ID matching [A-Za-z0-9_-]{8,100}; a UUID works. State and event updates share one
transaction. A revision conflict returns 409 and leaves the competing edit intact.

| Action | Fields beyond revision/event ID | Role and effect |
| --- | --- | --- |
| total-progress | percent, nonblank note; optional resumeAt, milestoneAt, dailyWindow | Owner/Operator; changes baseline, not checklist |
| task / criterion | Configured id, boolean done | Owner/Operator; blocked while achieved until reopened |
| achieve | Nonblank note | Owner; all readiness criteria must already be checked |
| reopen | Nonblank note | Owner; retains previous achievement timestamp |
| plan-task | Existing task id, patch, nonblank note up to 1,000 characters | Owner; field-level saved override with before/after audit |
| plan-task-reset | Existing task id, nonblank note up to 1,000 characters | Owner; removes the entire override for that task, retaining history |
| staffing-record | Validated labor record | Owner; preserves proposed versus actual hours; rejects future completed work |
| audit | channels, dated communications | Owner; source-qualified summaries, no message sending |
| announcement | status, delivery messageId or failure detail | Owner; records receipt evidence, does not transmit |

cue-event is a separate telemetry action using event ID and person/motion
trigger; it does not use the normal state revision. Do not treat it as an editing
escape hatch.

The normal duplicate-event check occurs after the revision check. If a request
times out, do not blindly send a new event ID: reread state/history first. A repeat
with an old revision can return 409 even if its original write succeeded. Compare
the intended change, then consciously rebase against the latest revision. The GET
history is limited to 30 entries; older audit events remain in the database.

total-progress accepts only the current daily window (America/Chicago, hours
17 and 22), not arbitrary working hours. An explicit resumeAt must be in the
future and before the intermediate target; milestoneAt must be future and before
completion. Ordinary edits preserve target values/dates and future one-time holds.
The action clears an indefinite pausedAt, while retaining the recurring window.
It does not expose arbitrary milestonePercent or completionAt changes.

# Detailed daily-plan fields

plan-task edits existing configured tasks only. Allowed keys are:

| Field | Validation / semantics |
| --- | --- |
| title | Nonblank text, at most 240 characters |
| detail | Nonblank text, at most 6,000 characters |
| staffingSummary, paymentSummary | Text, at most 2,000 characters each; preserve evidence distinctions |
| startTime | Display text, at most 120 characters; does not start a work timer |
| startTimeStatus, dailyCompletionStatus | Text, at most 240 characters each |
| dailyCompletionPercent | Number 0–100, or null to clear the daily estimate |
| status | planned, tentative, confirmed, in_progress, pending, cancelled, completed |
| sourceEventIds | At most 100 nonblank source IDs, each at most 240 characters |

The patch must be a nonempty object. Unknown keys are rejected. Arrays replace the
field; omitted fields retain their existing values. Empty optional text clears
that text. Task identity/date, impact weights, critical flags, checklist criteria,
global staffing scenarios and target deadlines are deliberately outside this
action. A completed planning status does not set checklist done. Both plan
actions reject edits while the transition is achieved; Owner must reopen first.

Only changed fields are stored in state.planOverrides. Defaults still supply
all other fields. Saved fields continue to take precedence after later source
deployments until changed or reset. A reset returns to the current bundled
task, which may differ from the bundle at the time of the original edit. The
event stores before/after effective tasks and the caller's reason. This expansion
adds API editing; it does not add a new visual plan editor to the dashboard.

# Coordinator edits and error handling

The narrower Coordinator API uses an assigned subject. Owner requests specify
?subject=PROFILE_ID; Coordinator requests derive it from credential scope.
POST coordinator/grants is Owner-only and grants an eligible staff/public,
non-IT work item. PATCH coordinator/tasks accepts only itemId,
expectedUpdatedAt, title, description, status and dueAt; the grant and
timestamp comparison are enforced. Materials use POST coordinator/materials
to create and PATCH coordinator/materials with materialId and
expectedRevision to update. POST coordinator/timeline records an attributed
summary. These routes do not grant private collection browsing or file uploads.

| HTTP status | Client response |
| --- | --- |
| 400 | Correct the invalid payload; inspect the error code |
| 401 | Credential is missing; authenticate |
| 403 | Role, credential, scope or origin is denied; do not retry with the same assumptions |
| 409 | Revision/lifecycle conflict; reread and reconcile before retrying |
| 413 / 415 | Reduce request size / send the required JSON content type |
| 503 | Required transition state is uninitialized or service unavailable; investigate before writing |

Maintainer workflow

Work from /home/david/random/services-platform. Read current code, service
configuration and authenticated API state before changing anything. Keep private
payloads and backups outside www and load credentials inside the probe process.

| Change | Required propagation |
| --- | --- |
| Manual total or ordinary operational record | Save through the authorized API; reread the saved revision and rendered result |
| Existing dated task fields | Use plan-task; reconcile neighboring brief/schedule records; no restart |
| New task/date, readiness definition or bundled staffing configuration | Edit source and reconcile affected operations records; restart the local household service to rebuild its bundle; inspect saved overrides |
| Progress UI/model/CSS | Update static assets and their cache versions; verify browser behavior and shared display data |
| API code | Restart the local service; deploy the public Worker when its code/bundle changes; verify the forwarded route |
| Schema | Add a numbered migration and apply it deliberately to each relevant store; do not assume a cloud migration updated local authority |
| README only | Edit this canonical file, synchronize its mirror, and verify the served text; no API restart or Worker deployment is needed |

Useful targeted checks:

bash
cd /home/david/random/services-platform
npm run check
node --check ../www/services/desk/sale-prep-card.js
node --check ../www/services/desk/sale-prep-model.js
node --test test/sale-prep.test.js test/sale-prep-progress.test.js test/sale-prep-labor.test.js test/sale-prep-plan.test.js
node scripts/verify_command_brief_scroll.cjs
node scripts/audit_relative_dates.cjs

The progress tests cover unchanged targets, 74%/90% corrections, edits after the
milestone, zero/100%, holds, active windows, daylight-saving boundaries, revision
conflicts and role restrictions. Browser verification must additionally check
preview text, draft preservation, failed-save feedback, actual scrolling and
phone/tablet/desktop overflow. Validate notification transport only when changing
that transport; do not send real messages just to test a documentation update.

For a code/configuration deployment, restart the local service and deploy the
Worker as applicable, then verify both local and public routes. npm run deploy
also synchronizes this README. Restarting the API does not independently prove
that every display receiver has refreshed.

bash
systemctl --user status sol37-household-local.service --no-pager
journalctl --user -u sol37-household-local.service -n 50 --no-pager
npm run sync:readme

Troubleshooting the household dashboard

| Symptom | Check first |
| --- | --- |
| Percentage is not increasing | Local time, the 5–10 PM active window, future baseline hold, emergency pause and whether it is already 100% |
| Percentage differs slightly between card and form | Whole-number flooring on the card versus two-decimal rounding in the form |
| 74% or 90% appears stalled beyond a normal hold | Confirm current model asset and saved targets; an old implementation raised the intermediate target and must not be used |
| Save reports another update | The draft revision is stale; use Refresh saved total, review and re-enter the intended correction |
| Banner and operating brief disagree | Dated config task, Owner brief, staff brief and schedule focus are separate sources |
| Staffing text is stale | Check both dated task fields and sale-prep-staffing.json |
| New config is on disk but not in the API | The local service bundles config at startup; confirm the correct service was restarted |
| Local desk works but public admin route fails | Public forwarding, site routing and reachability of the local authority |
| Public intake is absent from local IT records | Inspect Owner cloud-intake pending synchronization, queue retries and signed bridge; D1 acceptance does not prove local processing |
| A viewer can see a card but cannot edit | Shared viewing does not grant mutation capability |
| A screen shows older text | Inspect fresh feed/frame timestamps and receiver playback, not just the fleet service's active flag |
| Delivery says queued/accepted | Inspect the relevant recipient/transport receipt before calling it delivered |
| Backup fails or reports missing sources | Inspect storage, permissions and manifest coverage; a valid SQLite copy is not a full runtime snapshot |

Backup and recovery expectations

Take a consistent SQLite backup before material state changes and check its
integrity afterward. Preserve reviewed source changes and the post-change state.
A live SQLite database should be backed up through SQLite's backup mechanism;
copying only its main file can omit uncheckpointed WAL writes.

The broader sol37-backup.service snapshot covers more than the database. Inspect
its result and required-source coverage; do not call it complete unless the
required sources are present and missing_sources is empty. A source archive or
an integrity-checked local database backup is useful but is a narrower result.
If the preferred SOL drive is full, report that limitation explicitly rather than
silently switching destinations or presenting a partial snapshot as complete.

Restoration should be deliberate: retain the current state, inspect the selected
snapshot and its date, stop conflicting writes, restore the reviewed authoritative
store, and verify permissions and current records before resuming operations.
Do not restore old credentials or reimport cloud state over newer household edits
as a side effect of routine troubleshooting.

The sections below retain the detailed implementation inventory and specialized
workflows. Historical dates describe the observation or rollout named there; the
current household authority and progress behavior are defined above and in the
running implementation.

Implementation notes and specialized workflows

scripts/capture_command_brief.cjs OUTPUT.png captures only the blue command
brief and a scoped source snapshot, excluding surrounding private panels.
The explicitly authorized one-time Nicole update uses
scripts/nicole_brief_delivery.py: preview never sends; send-test sends to
the verified self-chat; prepare captures current content and uses local Gemma
for changes since the baseline; send checks the scheduled window and sends to
the verified direct recipient. Separate private ledgers prevent ambiguous
retries, with Apple Messages receipts checked for both text and image.
sol37-nicole-brief-delivery.service runs the local deadline daemon for the
authorized September 27, 11 AM Chicago send. It captures, drafts and reviews
updates through local llama.cpp on port 18080, sends once, and reconciles
receipts after a restart. Its private daemon-status.json records each stage.
The separate Codex 11 AM task only monitors; it cannot invoke the send workflow.
The local model receives the current draft paragraph, the baseline paragraph,
before/after changes to the relevant source records and the blue box's rendered
wording, plus layout and page-source change signals. Clock and automatic progress
movement are distinguished from actual work. The private context baseline and
llm-context.json make the comparison inspectable without exporting the page.

Owner and Operator can adjust the total percentage under Plan & updates →
Adjust total progress
, with a required reason. Revision-bound writes preserve
an audit event and rebase automatic increases from the saved percentage. The
saved schedule in SQLite overrides the bundled default for both the dashboard
and command display. Checklist verification is independent. A stale edit is
rejected; refresh the plan before retrying. Staff and display viewers cannot edit.
The form previews each correction and preserves unsaved entries during refresh.
Percentage edits never raise the 73% target or create replacement milestones.
An edit at/above the milestone, or after its date, immediately uses the exponential
final stretch during active hours. Edits below a future target use the remaining
active hours to reach it. A 100% value holds without checking off readiness.
The action also accepts an optional future resumeAt before the next milestone.
It holds the supplied total until that instant, then increases continuously toward
the existing milestone and exponential completion target. This lives in the saved
schedule and needs no timer job; the dashboard labels the hold and refreshes at resume.

The default total progress bar uses totalProgressSchedule in sale-prep-config.json:
37% from September 27, 2026 at 1:40 AM Chicago, linear growth to 73% October 1
at 10 PM Central, then exponential growth to 100% October 31 at midnight. Values are
floored to whole percentages, and refresh in place and on resume. Manual edits
can exceed a target early; active-hour scheduling can reach the final value at
the last active window before a midnight deadline.
The owner-set scheduled total is separate from verified checklist conditions.
The saved schedule now counts only daily 5–10 PM America/Chicago active hours.
It holds continuously from 10 PM through 5 PM the next day, including across
midnight and daylight-saving changes. Linear and exponential fractions both use
active time. A target outside the active window is reached at the last window's
end before that deadline: the Halloween target reaches 100% October 30 at 10 PM
and remains there through October 31 at midnight. dailyWindow and a revised
milestoneAt can accompany the revision-bound total-progress action; manual
percentage corrections preserve the recurring window.
The timeline now extends to a pumpkin at Halloween, including appointment nodes
through October 31. October switches the heading to “Home Before Halloween”
with orange and purple accents, using Chicago dates on each render.
For an emergency, set pausedAt to an ISO timestamp and restart the local
household service; the curve freezes there. Clearing it catches up to the
original schedule; revise milestones instead when the deadline has changed.

Local household authority

The September 24 source handoff is applied by
scripts/import_deer_valley_handoff.py PRIVATE_PAYLOAD.json to the existing local
SQLite authority. It takes a consistent backup, checks the reviewed project and
brief versions, and applies one transaction. Stable source IDs make repeat imports
no-ops; no notification or delivery queue is written. The input remains a private
archive artifact, not a runtime truth store. --dry-run uses an in-memory copy.
scripts/verify_deer_valley_import.py PAYLOAD BASELINE_SQLITE checks repeat-import,
proposal, valuation-versus-cash and privacy invariants against a disposable copy.

Migration 0025 adds home_health_visit.private_care_json. The existing home-health
read returns it only for Owner, and omits it for display presentation. It is never
part of the shared Command display, operations timeline, recaps or notifications.
The existing private care section renders that record; Projects renders the
source-linked property status with the existing design. Sources retain original
offsets separately from verification times. Deadline passage changes presentation
to outcome-unconfirmed, never contract or enforcement state. Property state accepts
up to 120 KB so preserved source histories remain editable.

Deploy this local-authority change by applying the reviewed local transaction,
restarting sol37-household-local.service (which bundles the existing Worker API),
and verifying the existing static files through both local and public routes.
The cloud Worker is still the forwarding transport; no D1 reimport or cloud
database write is required. Keep the private payload and backups outside www.

September 23 viewing authorization: every authenticated desk role, including
Staff Guest, display preview and Nicole's coordinator, may view information
shown on the display fleet. GET /admin/command-display supplies the shared
brief, sale preparation, appointments and home-health presentation. Recap
transcript/audio reads use the same fleet recap across roles. The shared
Command brief reuses the dashboard renderers with mutation/broadcast controls
removed for viewers. Existing work assignments, coordinator task/material
grants and all administrative write restrictions remain independent. Private
project/contact/IT collections and credentials are not granted by this route.
Coordinator refresh runs every minute while visible and not editing.

Since September 23, 2026, household/admin reads and writes use the local
SQLite database ~/.local/state/sol37/household-local/household.sqlite.
sol37-household-local.service runs the existing authorization/API code through
scripts/local-desk.mjs on loopback port 19848. Caddy routes the local dashboard
directly there. The public Worker forwards admin requests to the same local
service; it does not query D1 for those requests. x-sol37-storage: local-sqlite
identifies this path. The local dashboard is available at
http://127.0.0.1:8888/services/desk/ without Internet or cloud-database access.

The initial source was the preserved September 23 12:52 PM D1 export; SOURCE.json
records its hash and time. No automatic cloud merge or reimport is allowed over
new local writes. Existing cloud public intake/queue processing is still separate;
new cloud intake records are not automatically imported into local IT records.
Local admin notification events are retained in local_notification_outbox
pending a dedicated delivery integration; do not describe them as delivered.
Shared household task and material changes are immediate and durable locally.

Consistent SQLite backups are under household-local/backups, mode 0600. This
directory is also included in the existing Sol backup source tree. The SOL data
drive was full at cutover, so a separate-drive backup remains unverified.
Keep this directory, credentials, database and snapshots outside the public site.

Household coordinator access uses the main /services/desk/ shell and its shared
navigation, theme, cards, forms, and responsive layout. Existing coordinator.html
personal links redirect there without rotating credentials; the explicit Owner
?subject= review remains available. The existing Staff Guest view is unchanged.
The coordinator UI retains the deny-by-default
/admin/coordinator API. Owner explicitly shares staff/public, non-IT work
through /coordinator/grants?subject=PROFILE_ID. The coordinator edits only
those canonical tasks, separate working documents, and attributed timeline
updates. Private notes, attachments, project/contact collections, IT records,
personnel, payroll, and credential administration remain excluded. The separate
read-only Command brief includes the fleet-visible sale/financial and household
information explicitly authorized above.
Materials are editable text documents, not file uploads or original evidence.
Owner can review the same workspace with coordinator.html?subject=PROFILE_ID.
Migrations 0023–0024 store grants, revisioned materials, audit history, and
mirror coordinator updates into the owner timeline. Task/material edits use
optimistic concurrency. Archiving the linked profile invalidates access;
issuing this coordination role does not set employment or payment status.

This project is the private operating system behind the Sol-37 Services Desk at
https://sol.system42.one/services/desk/. It began as an IT service-request
queue and now provides a shared control surface for IT work, household work,
staff records, editable schedules and routines, onboarding, property transition
tasks, inventory, administration, handoffs, timekeeping with estimated pay,
guided role-aware help, and notification-derived communication events.

The platform is deliberately local-first at its notification and mailbox
boundaries. Cloudflare hosts public intake backed by D1 and forwards household
admin requests to the authoritative local SQLite service. The workstation owns
mail access, desktop notifications, private phone pushes,
access-QR presentation, and deduplicated backups.

Implementation and connected-system review: September 15, 2026. The package
inventory below retains its August 19 observation date unless explicitly updated;
it is not a claim that every installed package was re-inventoried. Runtime status
is a dated observation, not a permanent availability guarantee. This README is
mirrored to www/services/desk/README.md with npm run sync:readme; keep both free
of credentials, raw messages, private contact details, and clinical records.

Current dashboard and connected systems

September 16 display policy: selected fleet screens mirror the Record Player
composition, including its timeline, camera/QR presentation and motion flash.
SOL37_DESK_MIRROR_RECORD_PLAYER=1 selects this mode. Record Player retains its
audio/video feed. Living Room receives a video-only rendition with its own
attention-sound status label; Mac receives the shared composition as MJPEG with
a Mac-specific muted label. Both silent variants retain the shared flashes,
cooldown, camera/QR effects and timeline. The feature is called attention
sound
; motion/presence is a trigger, not its name. MJPEG carries no audio and
the Living Room rendition has no audio track. Receiver buffering can introduce timing offsets.
The Mac mirror is a presentation surface; interactive administration remains at
the authenticated dashboard URL. This policy does not expand the fleet's target
selection or bypass excluded screens. Earlier descriptions of the independent
Mac iframe and base video-only composition below describe the non-mirror mode.

| Surface | Authoritative implementation | Connections and boundaries |
| --- | --- | --- |
| Command brief | www/services/desk/index.html, scripts/household_operations_inference.py | Manual brief overrides remain authoritative; local inference does not hire staff or complete tasks |
| Events recap | scripts/generate_operations_digest.py, local bridge, Early voice, midnight user timer | Owner and staff-safe recaps are generated and voiced locally; playback never mutates source records |
| Sale preparation | src/sale-prep.js, src/sale-prep-config.json, sale-prep-model.js, sale-prep-card.js | Calendar countdown, verified readiness, staffing scenarios, presence attention; calendar progress is not completed work |
| Appointment days | Operations schedule, lee-appointments.js, sale-prep-card.js | One node per in-range day; same-day entries grouped; recorded completion persists; unknown ETA remains unknown |
| Housing and property | Projects, contact links, dependencies, evidence, work items | Buyer terms, legal/tax evidence and housing availability remain separate claims |
| Work board | Generic work_items and relationship tables | Priority, owner, status, due time, recurrence, explicit staff assignments, audit history |
| Time clock | Server-owned time entries and rate snapshots | Actual starts/stops and corrections; estimates are not payroll |
| Timeline and schedule | operations_timeline, operations_state | Events are facts with provenance; schedule rows are plans or recorded dispositions; not interchangeable |
| Staff and people | Staff/candidate profiles; private contact profiles | Candidate ≠ hired staff; person record ≠ credential; contacts do not create messaging authority |
| Projects and inventory | Project dependencies/evidence; persistent inventory IDs | Unknown ownership/disposition stays unresolved; attachment references are not stored media |
| Protocols and handoffs | Operations document and sanitized work handoffs | Role-aware visibility; private operational context is stripped from guest responses |
| Authentication and QR | Worker token scopes, qr-login.js, access CLI/panel | Owner, Operator and scoped Staff Guest; independently expiring/revocable display credentials |
| Mail and messages | Solmail/mailwatch; BlueBubbles bridge on Linux plus Messages on Mac | Source completeness retained; explicit-reply handling remains separate from AI scheduling functions |
| Local scheduling AI | scripts/appointment_eta_monitor.py | Exact contact/appointment matching; validated function dispatcher; ambiguous updates become private review tasks |
| Private notifications | Unified Clock /api/push/send | Deduplicated update/review outbox; opens private Master Log; public-push endpoint is never used |
| Display and recording | sol37_desk_screen_fleet.py, Mac Chrome, HLS, sale_prep_record_player.py | Shared desk rendering; approved target scope; transient HLS transport is not a recording archive |
| Camera and attention | Local dashboard camera gateway, vision tracking/presence/audio modules | Source freshness and user-hidden cards are separate states; hardware audibility requires direct observation |
| Recovery | sol37_backup.py, remote D1 exports, user service definitions | Private config and ledgers must be covered; missing-source count alone does not prove restore success |

mermaid
flowchart LR
  Desk[Private dashboard] <--> Worker[Worker authorization and APIs]
  Worker <--> D1[(D1 records and history)]
  Mail[Solmail and Care notifications] --> Worker
  Mac[Mac Messages / BlueBubbles] --> Ledger[Local incoming-message ledger]
  Ledger --> Replies[Existing explicit reply prompts]
  Ledger --> AI[Local model: scheduling intent]
  AI --> Functions[Validated update or review functions]
  Functions --> Worker
  Functions --> Outbox[Private push outbox]
  Outbox --> Clock[Unified Clock / Master Log]
  Desk --> Fleet[Mac browser and approved Cast fleet]
  Camera[Local camera / presence] --> Desk
  Camera --> Fleet
  D1 --> Export[Private SQL export]
  Export --> Backup[Deduplicated runtime backup]
  Ledger --> Backup

The September 15 review exercised all 12 household sections at desktop, tablet
and phone sizes, read the protected API groups, checked Owner/Operator/Guest
boundaries, reviewed local services, decoded a current stream segment, and
checked a private push delivery receipt. Detailed findings and verification
limits are in ../docs/household-operations-review-20260915.md in the local
repository. The audit does not imply a fresh email send, hire, timer start/stop,
physical visit, or full disaster restore was performed.

Contents

The sale-preparation transition is implemented by src/sale-prep.js,
src/sale-prep-config.json, migration 0022_sale_prep_transition.sql, and the
desk's sale-prep- / presence-attention.js modules. It pins the September 30
nominal objective and October 12 outer bound above existing operations tools.
The existing local service-request bridge exports only bounded, identity-free
presence signals from bin/sale_prep_presence.py. Read
../docs/sale-prep-transition.md for state semantics, verification, and the
browser-audio and TV capability boundaries. The existing fleet now owns a
Record Player-specific H.264/AAC attention feed through
bin/sale_prep_record_player.py; Living Room retains its video-only feed.

Live relative-date summaries use America/Chicago calendar days and refresh in
place at midnight and tab resume. The renderer preserves original source text
and event statuses. Run node scripts/audit_relative_dates.cjs with the local
owner credential for a read-only desktop/tablet/phone and midnight rollover
audit; npm test includes DST and year-boundary checks. The maintained standard
is ../docs/relative-date-standard.md; a twice-daily Codex heartbeat reviews
date-language regressions and corrects only unambiguous rendering defects.

  1. System overview
  2. End-to-end ticket and data lifecycle
  3. Software inventory
  4. Repository and runtime layout
  5. Custom code summaries
  6. Cloud architecture
  7. Dashboard and access model
  8. Local event and notification architecture
  9. Care notification email integration
  10. Data stores
  11. Security and privacy boundaries
  12. Development and deployment
  13. Operations
  14. Verification
  15. Adding people and devices
  16. Backups and recovery
  17. Known constraints

System overview

This older diagram describes the cloud intake/notification deployment. For
current household admin routing and SQLite authority, use the architecture
section above; D1 labels here do not supersede that boundary.

text
Public intake or private desk
             |
             v
Cloudflare Worker API
  |          |             |
  v          v             v
D1         Queue       Send Email binding
             |
             | signed minimal event
             v
Caddy -> local service-request bridge (127.0.0.1:8914)
             |
             +--> local SQLite delivery ledger
             +--> XFCE notify-send
             +--> custom_notify history
             +--> Unified Clock private web push -> iPhone
             +--> Solmail SMTP customer confirmation

iCloud IMAP -> Solmail Maildir + SQLite
|
v
Care notification mail watcher
|
+--> protected D1 operations timeline
+--> XFCE notify-send
+--> custom_notify history
+--> Unified Clock private web push

The public request path and the private household path use the same Worker and
authentication boundary. Household work is represented by generic work items
instead of a parallel ticket application.

End-to-end ticket and data lifecycle

This is the public cloud ticket intake lifecycle. Household admin mutations
follow the local SQLite path documented above; import/synchronization between
these stores must be explicit.

This section defines the inclusive path from ticket entry through every
database and operational surface involved in retaining or acting on it.

1. Browser intake

The customer opens /services and submits the static request form. Browser
code creates an idempotency key and sends JSON to the Worker. Turnstile runs in
the browser, but its result is accepted only after server-side verification.
The public page does not write directly to a database.

2. Worker validation

src/index.js validates content type, origin, source path, idempotency key,
contact fields, category, location, description, consent, Turnstile response,
and rate limit. The Worker normalizes fields and hashes the source IP; it does
not store the raw IP address.

3. Authoritative D1 commit

The Worker generates the SR- request ID and inserts the complete validated
request into the D1 service_requests table. D1 is authoritative for request
status, internal notes, schedule, resolution, retention time, email state, and
queue-notification state. The Worker acknowledges the browser only after the
database write succeeds.

4. Cloud event publication

The Worker sends a deliberately minimal ticket.created event to Cloudflare
Queues. The queue item contains the request ID, first name, category, coarse
location, creation time, and event ID. It omits the detailed problem text,
email address, phone number, internal notes, and resolution.

5. Signed cloud-to-local handoff

The queue consumer signs the minimal event with the configured HMAC secret and
POSTs it through Caddy to the loopback service-request bridge. The bridge
validates signature, timestamp, nonce, event type, and body size before it
accepts the event.

6. Local bridge database commit

service_request_bridge.py commits the event to
~/.local/state/service_request_bridge/bridge.db before attempting secondary
delivery. SQLite records popup, phone push, customer confirmation, history,
attempt counts, timestamps, and last errors independently. Nonces are stored
separately to prevent replay.

7. Notification and confirmation fan-out

The bridge attempts XFCE notification, custom-notifier history, Unified Clock
private push, and customer confirmation email as independent channels. For a
new request that includes customer email, the bridge retrieves protected
ticket detail from D1 using the owner credential, then asks Solmail to send a
minimal plain-text confirmation through authenticated iCloud SMTP.

8. Operator work

An Owner or Operator loads the private request through the Worker. Dashboard
edits to status, schedule, internal notes, or resolution are PATCH requests;
the browser never updates D1 directly. The Worker validates the mutation,
attributes it to the authenticated principal, updates D1, and emits a minimal
ticket.updated event only when tracked fields actually change.

9. Retry and retention

Cloudflare Queue retry handles retryable bridge failures. The local two-minute
retry timer revisits only incomplete local channels. Stable event IDs and push
tags prevent duplicate delivery. Worker cron and local pruning apply the
configured 90-day request/event target, while backups retain verified
snapshots according to the Sol-37 backup policy.

10. Household and staff data

Household operations use the same authenticated Worker and D1 database.
operations_state, work_items, staff_profiles, contact_profiles,
projects, inventory_items, time_entries, and operations_timeline hold
the strategic plan, generic work, personnel records, external associates,
project contexts, physical containers, the time ledger, and the audit/event
stream. Normalized relationship tables link projects, external contacts, work,
and inventory without copying private contact details into tasks. Work items
carry explicit staff assignees, validated daily/weekly/monthly recurrence,
follow-up and decision state, sanitized handoff fields, and append-only public
staff activity. Staff-visible versus private data is selected in Worker
queries and sanitized mapping functions before it reaches a Staff guest
browser. Each staff mutation also requires a named capability on that active
profile.

The private North Richland Hills Housing Search is a canonical projects
record with active_search lifecycle state. Its versioned state_json holds
the Marlette Court anchor, six manually ranked configurations, per-listing
working figures and freshness notes, accessibility facts, full listing URLs,
and 0-5 component scores. Migration
0018_nrh_housing_owner_status_and_asymmetric_pairs.sql adds the asymmetric
two-residence budget model, the right-sized-unit score, source-qualified
availability, and Brenda's waiting email intake. Direct owner or realtor
confirmation outranks listing metadata; 6300 Mark is confirmed available while
6302 remains leased / watching and has no actionable combined rent. The search
targets roughly $4,000 combined, uses about $5,000 as the outer working ceiling,
and does not require equivalent unit sizes. It deliberately has no hidden
composite score.
Verification, medical-drive, contact, and tour actions remain linked private
work_items; unresolved listing facts remain project dependencies; snapshot
and spot-check provenance remain project evidence and timeline events. Owner or
Operator can update rank, candidate state, rents, URLs, verification notes, and
last-checked time from the Housing panel. Staff Guest cannot fetch the project
or open the Housing panel.

11. Timekeeping and estimated pay

The Worker is authoritative for active timers, elapsed seconds, rate snapshots,
estimated pay, corrections, voids, and audit attribution. Staff Guests can
start and stop only their own timer and only against work explicitly assigned
to their linked active profile. Owners can clock their own generic work or IT
tickets, inspect all entries, record missed time, correct entries, and void an
entry without deleting its audit record. Only one active timer is allowed per
actor. Estimated pay is an operational estimate, not a payroll, tax, overtime,
or legal wage record.

12. Mail-derived operational events

Solmail writes immutable source email to Maildir and metadata to its SQLite
index. The Care watcher reads only qualifying indexed notification messages,
commits its own channel ledger to watch.db, and posts an idempotent event to
the protected D1 operations timeline. Each event records source, completeness,
capture time, and source event ID; Care email is partial by construction.
This is a second inbound event path, not a substitute for the authoritative
ticket database or full Care conversation synchronization.

Software inventory

Base environment

| Package or platform | Observed version | Role |
| --- | --- | --- |
| Ubuntu | 22.04.5 LTS | Workstation operating system |
| Linux systemd | 249 | User services, timers, restart policy, and boot persistence |
| XFCE | 4.16 | Desktop session and panel launcher environment |
| Bash | 5.1.16 | Administrative shell and service helpers |
| Python | 3.10.12 | Local bridge, mail, notifier, clock, token, and backup runtimes |
| SQLite library | 3.37.2 | Local delivery ledgers and mail index |
| Node.js | 24.11.1 | Worker tooling and Unified Clock frontend builds |
| npm | 11.6.2 | JavaScript dependency and script runner |
| OpenSSL | 3.0.2 | TLS support used by system networking and Python SMTP/IMAP |
| curl | 7.81.0 | Health and API probes |
| Git | 2.34.1 | Source tracking |

Cloudflare Worker project

The direct Node dependency surface is intentionally small.

| Package | Declared version | Installed version | Scope |
| --- | --- | --- | --- |
| wrangler | ^4.118.0 | 4.118.0 | Worker development, deployment, D1 migrations, and remote inspection |

package.json identifies the Worker as sol37-service-requests version
0.1.0, uses native ES modules, and exposes:

text
npm run check   -> node --check src/index.js
npm run dev     -> wrangler dev --persist-to ./.wrangler/state
npm run deploy  -> wrangler deploy

The top-level install also currently contains @emnapi/runtime,
@img/sharp-wasm32, and tslib as extraneous installation artifacts. They
are not declared application dependencies and must not be treated as part of
the Worker runtime contract.

Cloudflare managed services

These are platform services rather than local packages:

| Service | Resource or binding | Purpose |
| --- | --- | --- |
| Cloudflare Workers | sol37-service-requests | Public intake and authenticated desk API |
| Cloudflare D1 | sol37-service-requests | Authoritative requests, tokens, work items, staff, time entries, timeline, and strategic state |
| Cloudflare Queues | sol37-service-request-events | Minimal new/update event delivery |
| Cloudflare dead-letter Queue | sol37-service-request-events-dlq | Exhausted queue deliveries |
| Cloudflare Turnstile | TURNSTILE_SITE_KEY plus secret | Server-verified public intake abuse control |
| Cloudflare Send Email binding | EMAIL | Documentation-email path; destination verification remains operationally relevant |
| Worker Cron Triggers | daily and 15-minute schedules | Retention pruning and pending-email retry |
| Cloudflare observability | enabled, sampling 1.0 | Worker execution diagnostics |

The Worker uses compatibility date 2026-08-02, is route-bound to
sol.system42.one/api/service-requests, and is not exposed on workers.dev.
The deployment verified on August 19, 2026 is
3ebce34b-eff0-4176-b27a-f2ea4bbb84c5.

Static dashboard packages

| Package or technology | Version | Role |
| --- | --- | --- |
| HTML, CSS, browser JavaScript | Native browser APIs | Zero-build Services Desk client |
| qrcode-generator | 2.0.4 | Same-origin in-browser SVG QR generation |
| Google Chrome | 150.0.7871.186 | Live desktop verification browser |
| Browser sessionStorage | Native API | Per-tab bearer-token retention after fragment consumption |
| Browser localStorage | Native API | Role/version-scoped first-run tutorial completion flag; no credential or operational records |

The QR library is vendored at
www/services/desk/vendor/qrcode-generator-2.0.4.js under its MIT license.
The dashboard does not send QR payloads to an external service.

Local Python applications

| Application | Main file | Runtime dependencies | Purpose |
| --- | --- | --- | --- |
| Service request bridge | random/bin/service_request_bridge.py | Python standard library, local solmail module | Signed cloud-to-local event receiver and delivery ledger |
| Care/mail watcher | random/bin/service_request_mailwatch.py | Python standard library, Solmail CLI | IMAP sync orchestration and notification-event ingestion |
| Solmail | random/bin/solmail.py | Python standard library | IMAP sync, Maildir storage, SQLite index, authenticated SMTP |
| Property correspondence archiver | random/services-platform/scripts/build_property_correspondence_archive.py | Python standard library, local solmail module | Full-folder targeted IMAP retrieval, MIME preservation, attachment extraction, SHA-256 manifests, and private ZIP packaging |
| Cast display keeper | random/bin/sol37_record_player_display.py | PyChromecast | Holds a short-lived private DashCast session on each approved Cast target without logging bearer URLs |
| Display fleet watchdog | random/bin/sol37_desk_screen_fleet.py | Playwright, PyChromecast, Pillow, FFmpeg, SSH | Reconciles Cast senders, the Mac Mini kiosk, and live H.264/HLS screen presentations while hard-excluding Lee's TV |
| BlueBubbles reply/operations bridge | .local/bin/bluebubbles_notify_bridge.py | Python standard library, SQLite, BlueBubbles public API | Preserves explicit replies and independently delivers relevant incoming messages to D1, private push, and notifier history |
| SOL vision camera utility | random/bin/sol_vision_camera.py | Pillow, v4l2-ctl | Captures local frames, applies verified camera profiles, and performs read-only screen or reference-bar analysis |
| Custom notifier | random/bin/custom_notify.py | Python standard library, Tk, local TTS facade | Persistent two-way desktop notifier and history |
| Unified Clock backend | random/bin/sol_unified_clock.py | Flask, Werkzeug, cryptography, pywebpush, local modules | Private push gateway, master notification log, schedules, and notifier bridge |
| Access CLI | bin/sol37_services_admin_tokens.py | Python standard library | Owner-authenticated token/staff API client |
| Access panel | bin/sol37-services-admin-token-panel | Tk, Pillow, QRencode, Zenity, Xclip, systemd user controls | Password-gated desktop menu, display broadcast controls, and self-closing QR handoff |
| Sol-37 backup | random/bin/sol37_backup.py | Python runtime and local backup modules | Deduplicated runtime snapshots |

The service-request bridge, mail watcher, Solmail, and access CLI deliberately
use Python's urllib rather than adding a separate HTTP client dependency.

Python packages used by the notification and QR layers

| Package | Observed version | Production use |
| --- | --- | --- |
| Flask | 3.1.2 | Unified Clock local API |
| Werkzeug | 2.2.2 | Unified Clock WSGI server helpers in the current environment |
| cryptography | 46.0.5 | VAPID key serialization and elliptic-curve operations |
| pywebpush | 2.3.0 | Private and public browser push delivery |
| py-vapid | 1.9.4 | VAPID support under pywebpush |
| http-ece | 1.2.1 | Web Push payload encryption |
| Pillow | 12.1.1 | Loading QR PNGs into the desktop Tk handoff window |
| Tk | 8.6.12 | Desktop QR window and custom notifier GUI |
| PyChromecast | 13.1.0 | Direct private dashboard presentation on approved Chromecast/Nest targets through the DashCast web-page receiver |
| Playwright for Python | Installed local runtime | Renders the authenticated 1920x1080 fallback frame without putting credentials in browser command arguments |

aiohttp, requests, and their transitive packages exist elsewhere in the
workstation environment, but the Services Desk bridge and mail watcher do not
depend on them.

Unified Clock frontend packages

These packages belong to the private push and notification-center subsystem
used by this project.

| Package | Installed version | Role |
| --- | --- | --- |
| React | 18.3.1 | Unified Clock renderer and private notification UI |
| React DOM | 18.3.1 | Browser/Electron rendering |
| Electron | 30.5.1 | Desktop Unified Clock shell |
| electron-builder | 24.13.3 | Linux directory packaging |
| esbuild | 0.25.12 | Renderer and backend bundling |

The Unified Clock project is version 1.0.0 and can build a desktop renderer,
public PWA bundle, backend bundle, or Linux application directory.

Desktop and system utilities

| Package | Observed version | Role |
| --- | --- | --- |
| QRencode | 4.1.1 | Local PNG QR generation via stdin; tokens do not enter an external API |
| Zenity | 3.42.1 | Password prompt, action menu, status list, and revoke selection |
| Xclip | 0.13 | Explicit copy-token and copy-link actions |
| libnotify / notify-send | 0.7.9 | XFCE desktop notification channel |
| Pillow ImageTk | 12.1.1 | QR image presentation inside Tk |
| Ristretto | 0.12.2 | Enlarged one-off scan-code presentation and inspection |
| ImageMagick | 6.9.11-60 | Nearest-neighbor QR enlargement during verification |
| XDG Utils | 1.1.3 | Opening authenticated desk URLs in the configured browser |
| sudo | 1.9.9 | System-password gate for local access administration |
| Caddy | 2.6.4 | Static-site serving and narrow local reverse proxies |
| cloudflared | 2026.3.0 | Existing Sol-37 Cloudflare connectivity layer |

Verification-only tools

| Package | Observed version | Use |
| --- | --- | --- |
| Playwright CLI | 0.1.18 | Real browser desktop/mobile workflows, screenshots, and console checks |
| OpenCV (opencv-python) | 4.13.0.92 | Decode generated QR images and assert exact payload equality |
| ImageMagick | 6.9.11-60 | Produce scan-sized QR copies without interpolation |
| Node --check | Node 24.11.1 | Worker, inline dashboard script, and vendor syntax checks |
| Python py_compile | Python 3.10.12 | Local script syntax checks |
| Wrangler migration tooling | 4.118.0 | Confirm local/remote D1 migration state |

OpenCV and Playwright are validation tools. They are not required by the
running Worker, dashboard, bridge, watcher, or desktop access panel.

Repository and runtime layout

Authoritative project files

text
/home/david/random/services-platform/
  package.json
  package-lock.json
  wrangler.jsonc
  src/index.js
  migrations/0001_initial.sql
  migrations/0002_admin_fields.sql
  migrations/0003_admin_tokens.sql
  migrations/0004_operations_state.sql
  migrations/0005_operations_events.sql
  migrations/0006_staff_profile_details.sql
  migrations/0007_admin_token_scopes.sql
  migrations/0008_staff_lifecycle_and_provenance.sql
  migrations/0009_time_clock.sql
  migrations/0010_contacts_projects_inventory.sql
  README.md

/home/david/random/www/services/desk/
index.html
vendor/qrcode-generator-2.0.4.js

/home/david/random/bin/
service_request_bridge.py
service_request_mailwatch.py
solmail.py
custom_notify.py
sol_unified_clock.py
sol37_backup.py

/home/david/bin/
sol37-services-admin-token-panel
sol37_services_admin_tokens.py

Documentation and skills

text
/home/david/random/docs/service-request-platform.md
/home/david/random/docs/household-operations-platform.md

/home/david/.codex/skills/household-operations-desk/
/home/david/.codex/skills/staff-guest-access/
/home/david/.codex/skills/care-email-operations/

The three project-specific skills route future work to the correct deployment,
access-control, QR, mail, notification, and verification procedures. Existing
component skills remain authoritative for Solmail, Unified Clock, custom
notifications, web push, Playwright, screenshots, GUI launch, and backups.

Desktop launchers

The same access-panel executable is referenced by:

text
/home/david/.local/share/applications/sol37-services-admin-token.desktop
/home/david/.config/xfce4/panel/launcher-40/sol37-services-admin-token.desktop

Custom code summaries

Most project behavior is custom code built on small standard-library or
platform primitives. The summaries below identify the responsibility of each
module so a future maintainer does not mistake infrastructure glue for a
third-party package.

Cloudflare Worker: src/index.js

The Worker is the central application backend. Custom code implements public
request validation, Turnstile verification, idempotency, IP hashing and rate
limits, D1 persistence, queue publication/consumption, HMAC bridge delivery,
documentation-email attempts, retention cron work, owner/operator/staff-guest
authentication, route authorization, staff-scope filtering, work-item CRUD,
staff lifecycle CRUD, external-contact and interaction CRUD, project CRUD,
inventory CRUD, bidirectional relationship hydration, private cross-model
search, capability-scoped task actions, timeline writes, Care email event
validation, and normalized
JSON serialization.

It also owns the one-time credential protocol: random token generation,
SHA-256 hashing, expiry, revocation, first-use updates, role/scope validation,
and a token-bearing desk URL returned only on creation.

Dashboard: www/services/desk/index.html

The dashboard is a custom zero-build single-page client. Its code handles
fragment consumption, session-only credentials, authenticated fetches, IT
queue search/filter/detail/edit flows, household command views, work board,
schedule and routine editing, explicit staff assignment, staff profiles,
external people, project grouping, inventory/container records, private search,
follow-up/decision queues, interaction logging, sanitized staff handoffs,
protocols, handoffs, timeline, access-profile management, time clock and
ledger controls, status legends, browser QR rendering, and a role-aware Help
center with a first-run spotlight tour.

The shared sticky shell listens to both page scrolling and the desktop desk's
internal IT/household scroll regions. It compacts the identity and action area
after the user moves into content. At desktop width it preserves the complete
desk title and fits every action without overflow; phone and tablet widths use
deliberate short titles and 44-pixel controls in horizontally scrollable action
rails. The IT toolbar and household section navigation share the same height so
switching modes does not move the working area.

Role-specific rendering is a usability layer on top of server controls. Owners
can create, edit, archive, and restore staff profiles; operators can edit
operational profiles and assignments. Staff guest mode changes navigation and
labels, avoids loading IT/timeline APIs, and presents only sanitized data plus
allowed controls for explicitly assigned work and the linked profile's own
time entries. The guided tour is generated from the current role, so it never
teaches or spotlights a control that role cannot use.

The Staff Workspace opens with a per-credential, browser-local welcome splash
that uses the linked profile's first name and role. It previews the current
staff-safe command, visible work count, schedule count, and access boundary,
then links directly to the command view, work board, profile, or role-aware
tour. The top action rail retains a Welcome action so the splash can be
replayed without clearing the credential.

The staff Overview is a deliberate mirror rather than a copy of the private
Owner command page. Owners maintain a separate staffBrief beside the private
command brief. The Worker serializes only that explicit staff-safe phase,
headline, situation, next action, optional target time, and update time. The
Overview combines it with filtered work, schedule, protocol, and handoff data;
private owner records never become a fallback when staffBrief is absent.

The command brief also carries an Events to date recap. A local generator
reads the revisioned operations state, work board, schedule, and—only for the
Owner/Operator version—the private timeline and personnel counts. It builds a
bounded deterministic transcript that retains recorded statuses instead of
inferring completion. The Staff Guest version is generated separately from the
explicit staff brief plus staff/public work and schedule rows; private timeline
events never enter that artifact. Profile-assigned schedule entries are also
excluded from the shared recording; each person can read their permitted
schedule directly in the workspace.

Both transcripts are synthesized locally with the registered early voice
through 11speak.py and stored under
/mnt/sol-data/sol-stack/services-desk-events-summary/ with private modes. The
header action makes playback available from every authenticated desk view, and
the command brief contains the same playback/transcript controls. Owner and
Operator sessions can queue a manual regeneration. The
sol37-operations-summary.timer user timer refreshes both variants at 12:00 AM
America/Chicago. An open desk follows a background render for up to one hour
and updates the recap when it becomes ready; an existing ready recording stays
playable during that refresh. Playback, transcript display, and regeneration
do not update work, schedule, staffing, or timeline records.

Owner sessions additionally expose Broadcast to devices inside Events recap.
Find devices discovers Cast screens and audio-only speakers; select individual
destinations, all available Cast devices, or audio-only devices, then choose
the staff-visible or private owner recording. Private owner audio is audible
to anyone nearby. Playback replaces selected receivers' current media, preserves
volume settings, and starts independently on each device. Non-Cast endpoints
are not included. Protected TVs and groups with unverified membership remain
excluded; a muted microphone does not disable audio output.

Owner-only /events-summary/cast/targets, /status, and /broadcast routes
use the signed local bridge to sol37-recap-cast.service. Discovery and control
stay local; audio URLs are random, expire, and are limited to the selected
receiver's LAN address. Temporary per-receiver leases let managed Cast screens
yield to the narration and resume dashboard presentation afterward. The UI
reports receiver verification separately from requested or failed playback;
receiver telemetry does not establish physical speaker loudness.

The role-aware Help and guided tour explain Events recap, Early playback,
transcripts, generated timestamps, and midnight refresh. Only Owner/Operator
guidance includes manual regeneration. Replay the tour from Help after updates.
Photographic progress checkpoints retain original files and hashes in private
local evidence storage, with source-qualified observations in the private
timeline. Capture order, completion, hours and payment settlement are not
inferred from photographs or partial message screenshots.

Public services page: www/services/index.html

The public services landing page shares the desk's walnut, paper, orange,
typography, border, and shadow system while remaining a separate public intake
surface. Its sticky header includes a prominent Operations Desk route. Desktop
navigation stays fully visible; phone navigation becomes a 44-pixel swipeable
rail without producing document-level horizontal overflow. The hero, services,
process, project, resume, contact, and print views remain responsive and the
existing Turnstile-backed intake contract is unchanged.

D1 migrations: migrations/.sql

The migrations are custom schema history and initial operating data. They
progress the system from ticket intake through admin editing, named access
profiles, revisioned operations state, generic work/staff/timeline tables,
provenance-labeled profile details, linked staff credential scopes, staff
capabilities/activity, archive state, event provenance/completeness, numeric
hourly-rate snapshots, the audit-retained time ledger, external contacts and
interactions, project contexts, inventory records, and normalized links among
people, projects, work, and inventory.

Local bridge: random/bin/service_request_bridge.py

The bridge is a custom threaded HTTP service on loopback. It verifies the
Worker signature and replay window, persists before delivering, converts cloud
events into local channel jobs, fetches protected D1 detail only when required
for customer confirmation, generates deterministic confirmation Message-IDs,
and exposes a retry-pending mode for systemd.

Care/mail watcher: random/bin/service_request_mailwatch.py

The watcher is a custom daemon loop. It invokes bounded Solmail sync, queries
the local index, parses retained MIME messages, normalizes invisible text,
recognizes a restricted Care notification subject set, generates stable event
IDs, extracts bounded excerpts, labels them partial with source/capture
metadata, and tracks D1/popup/push/history delivery states independently. When
the Worker receives a Care application_received event, it idempotently
materializes a provisional candidate profile, source/claim provenance, and a
private screening work item. That intake never creates active staff, task
assignments, credentials, trials, schedules, or payroll semantics.

Solmail: random/bin/solmail.py

Solmail is a custom IMAP-native mail transport and indexer. It handles newest
bootstrap, incremental UID cursors, slow backfill, RFC822 fetch fallbacks,
atomic Maildir writes, SHA-256 deduplication, parsed date ordering, SQLite
metadata, and authenticated SMTP over STARTTLS. It replaces an older
fetchmail/local-SMTP design.

Backfill advances through actual retained UIDs rather than spending runs on
numeric gaps. The property correspondence archiver reuses Solmail's account,
fetch, Maildir, and SQLite boundaries while searching configured IMAP folders
from their earliest retained content. It preserves deduplicated original
messages and attachments in a mode-0600 ZIP with an indexed relevance record
and per-entry SHA-256 manifest. This broad archival path is independent of the
narrow Care notification watcher.

Household screen dashboard presentation

In approved multi-screen mode, the Kitchen Nest user service maintains a
continuously connected PyChromecast DashCast controller (receiver app
84912283) for the literal responsive Services Desk page. The Record Player
Chromecast and Living Room Chromecast HD use the central fleet watchdog's continuously refreshed
H.264/YUV420P rendering of that same authenticated page. This video-backed path
is intentional: receiver-app telemetry can remain healthy after a physical
screen has returned to Ambient mode, so those two targets are re-presented
before the current media expires. A separately revocable, expiring
Operator display credential is consumed from the URL fragment into browser
session storage; the owner bootstrap credential is never cast. ?display=1
suppresses only the automatic first-run tutorial overlay. It does not create a
second dashboard or alter Worker authorization.

Display mode uses the same dashboard interface in an ordinary browser, the Mac
Mini app, and video-backed Cast output. The dark theme is the default. Its
three presentation controls switch theme, hide/show both floating cards, and
enter/exit ?display=1; display mode keeps the navigation compact on desktop,
tablet, and phone. The compact fragment is removed from the visible address
immediately.

An Owner or Operator entering display mode requests a one-day generic
preview_guest through POST /admin/display-session. The endpoint rotates the
prior credential for that display ID and returns a one-time guest URL; D1 keeps
only the hash. The card says Staff Guest View, is not linked to a candidate,
and carries the least-privileged Staff Guest read boundary. It may read only a
sanitized operations view and generic guest profile; contacts, projects,
inventory, private search, IT cases, candidate records, private timeline, task
mutations, and timekeeping remain denied by the Worker.

The dashboard page itself owns both independent floating cards: a locally
generated guest QR and a live SOL VISION // LIVE frame. Ordinary LAN browsers
use the same-origin PNG gateway once per second through a fetch-to-blob loop.
The Mac bootstrap does not create, own, or override a separate card. Pointer
activity fades both cards and pauses refresh; eight seconds of idle time
restores them. The source frame is preserved at 240x324, aspect ratio 20/27,
with object-fit: contain. The Mac displays this same page instead of
maintaining a second wrapper-owned Vision card.

Record Player receives a live HLS presentation of the same interface. Its
renderer removes the page-owned cards from the base capture, then FFmpeg
composites the changing Vision and QR MJPEG inputs into matching cards so the
cast does not duplicate them. The Vision and QR paths remain independent and
moving rather than being baked into a static dashboard image. The source,
card, screenshots, playlist, segments, and status artifacts remain mode
0600; the private camera frame is never routed through the public Worker.
Fleet status records vision_frame as refreshed, stale, or unavailable
and requires vision_popup=true, vision_stream=live_mjpeg, a running
compositor, and successful Cast presentation. A last good frame can bridge a
brief local camera/API interruption.
The Mac card preserves the 240x324 source at 20/27 with
object-fit: contain; the Record Player compositor uses aspect-preserving
scale plus padding so neither path crops or stretches the camera frame.

sol37-desk-screen-fleet.service runs the cross-screen reconciliation pass. Its
effective mode comes from the unit plus all drop-ins, not the base unit alone.
mac-record-player selects Mac and Record Player; mac-only suppresses Cast;
the temporary home-health mode selects Mac, Record Player and Living Room.
Lee's TV remains denylisted. It writes a mode-0600 Chrome bootstrap on the Mac Mini over SSH and
verifies the dedicated fullscreen Chrome process through its private loopback
debugging endpoint. A short-lived headless Chromium capture refreshes
the dashboard and QR source files on each reconciliation pass, then closes; a
long-running FFmpeg compositor consumes those sources plus the live Vision feed
without resetting the media timeline. It emits two-second H.264/YUV420P/AAC HLS
segments from a stable process. The randomized LAN-only server supports Range,
HEAD, CORS, and receiver request telemetry. Avoid restarting the compositor for
QR countdown/text changes because playlist churn can freeze one card or eject
the Cast receiver. The
standalone Record Player DashCast unit remains disabled because the central
video watchdog is its persistence authority. Living Room's historical standalone
Housing sender is an available mode, not proof that its unit is currently enabled.
During the September 15 review all three standalone display units were disabled
and the effective fleet mode was home-health. API acceptance is not treated as visibility: the
watchdog records not_visible or unavailable unless the target reports a
playing/buffering/paused state. All Lee's TV entity variants are an explicit
denylist and are never addressed by this fleet.

The Living Room sender opens the same authenticated desk with
?display=1&presentation=housing-tv; it uses a compact high-contrast 3-by-2
card layout. Record Player, Kitchen, Mac, and Lee's TV keep their existing
routes and programs. No additional casting daemon or credential store is
introduced.

On controller startup, an existing Mac app can retain the last pre-restart
frame even when its bootstrap file is unchanged. The fleet therefore opens a
short-lived SSH forward to Chrome's loopback DevTools endpoint, targets only
the Services Desk app page, and issues one uncached Page.reload. App-mode
AppleScript reloads over SSH proved ineffective and are not accepted as
success. The first healthy pass reports mac_mini=reloaded; later passes avoid
disruptive reloads and may report running. Current pixels are verified with a
Mac screencapture rather than inferred from process or receiver telemetry.

The Living Room Home Assistant entity can be stale while its Chromecast remains
reachable. Its dedicated sender therefore uses the verified Cast UUID and LAN
endpoint directly; Home Assistant power/CEC is best effort, not a prerequisite
for the direct Cast connection. Do not call Home Assistant turn_on during a
healthy Record Player pass: it can asynchronously launch the Home Assistant
receiver over the desk. The fleet invokes it only after a failed direct launch
and then retries the media session. SOL Vision evidence confirms the actual Record
Player pixels, while Mac verification uses a remote screenshot. Cast telemetry
alone is not described as visual proof.

Historically, after the August 19 buyer visit completed, the active unit was first reduced to
Mac-only and then deliberately restored as
SOL37_DESK_SCREEN_MODE=mac-record-player. This scoped mode keeps the
authenticated Mac Mini Chrome app and the video-backed Record Player display
persistent while leaving Kitchen Nest, Living Room, and the static-screen
targets stopped. The standalone Cast target units remain disabled because the
central fleet owns Record Player persistence. Use mac-only to suppress every
Cast target; remove the mode setting only when deliberately restoring the full
approved multi-screen topology. A dormant discoverable receiver is not itself
permission to expand the active target set.

# Recording and stream evidence

The live HLS directory is a rolling playback cache. It is intentionally excluded
from runtime snapshots; this is not continuous long-term recording. Durable
recordings and screenshots are explicit private artifacts, preferably on the
mounted SOL data drive. When evidence must reflect the stream, capture from its
current playlist or a referenced transport segment, not a browser or desktop
substitute. A local .m3u8 may contain absolute HTTP segment paths: opening it as
a filesystem playlist can fail. Use the LAN stream URL or copy an already
completed referenced .ts segment before decoding it.

Preserve the source playlist, capture time, segment/recording hash and decode
result. A decoded stream proves generated content, not physical display pixels
or audible sound. Read receiver position twice and use a current in-frame camera
or Mac screenshot for those separate claims. Never publish private footage or
token-bearing display URLs as part of README updates.

BlueBubbles relevant-message monitoring

BlueBubbles continues to send only new-message webhooks from the Mac Mini to
the allowlisted Linux reply bridge. Every incoming message still enters the
existing explicit-response queue and a mode-0600 local incoming_messages
ledger keyed by a hashed message ID. A separate relevance classifier recognizes
known operational contacts and narrow staff, property-sale, county-tax,
homestead, appraisal, and foreclosure terms.

The local tables live in
~/.local/state/bluebubbles-integration/operations-events.db (mode 0600).
Only relevant events also enter its delivery queue, which independently retries the Owner/Operator-only
/admin/operations/external-events D1 route, Unified Clock private push, and
custom-notifier history. Stable event IDs are hashes of BlueBubbles message
GUIDs; raw GUIDs, chat GUIDs, phone numbers, email addresses, and server
credentials never enter the Worker event or push. Unrelated message bodies stay
local and are not copied into D1. Known display names may link
to existing staff/contact records. Keyword-only messages use the neutral actor
BlueBubbles contact. Staff Guest receives HTTP 403 for this ingestion route
and for the private timeline.

SOL vision calibration

programs/sol-vision-color-bars.html is a deterministic same-origin seven-bar
and grayscale test target. sol_vision_camera.py analyze-bars samples a caller-
specified normalized ROI and reports clipping, white balance, and channel
separation without changing camera controls. A passing chart validates only bar
ordering and separation, not absolute colorimetry or dashboard text legibility.
If the observed frame does not actually contain the display, the analyzer must
fail rather than tune against the room scene.

Access API CLI: bin/sol37_services_admin_tokens.py

This custom CLI is a narrow owner-authenticated API client. It lists access
profiles and staff, creates subject-linked Operator or linked Staff guest
credentials, maps the Worker's one-time response into script-friendly JSON,
and revokes an individual token. It does not write D1 directly, so dashboard
and CLI issuance share the same validation and audit path.

Desktop access panel: bin/sol37-services-admin-token-panel

The panel is a custom Zenity/Tk application launched from XFCE. Its scrollable,
Services-Desk-styled menu separates Open & Manage, Display Broadcast,
Authentication Cards, and Credential Tools. Display Broadcast can start the
Mac Mini plus Record Player fleet, end that fleet for the night, or show the
current controller/status evidence. The stop action uses
sol37_desk_screen_fleet.py --stop-displays: it stops only
sol37-desk-screen-fleet.service, closes the isolated Mac Mini kiosk, clears
Services Desk media from approved Cast targets when present, and preserves the
Lee's TV denylist plus unrelated media sessions. The panel dynamically lists
David and every non-archived staff profile as direct QR actions, while retaining
configurable credential forms, status tables, and manual revocation. A direct
action confirms a per-person rotation, creates the replacement, revokes and
verifies the prior linked card(s), then displays a locally generated QR from a
mode-0600 temporary image. The handoff closes itself after that exact
credential reports successful use.

Custom notifier: random/bin/custom_notify.py

The notifier is a custom Unix-socket daemon and Tk client with persistent JSON
history, one-shot or repeating requests, replies, dismissal, optional local
speech, audio caching, subtext, and externally appended System history. This
project normally uses non-spoken history entries plus separate XFCE popups.

Unified Clock: random/bin/sol_unified_clock.py

Unified Clock is a broader custom application reused as this project's private
notification nervous system. Its relevant code manages VAPID material,
subscriptions, encrypted Web Push, stable tags, delivery activity, full
notification history, the mobile Master Log, and custom-notifier mirroring.
Only its localhost push API is called by the bridge and mail watcher.

Static routing: random/bin/Caddyfile.pkd_share

The Caddy configuration contains custom route boundaries for the static site,
Cloudflare Worker API fallback, and signed internal bridge. The bridge path is
stripped and forwarded only to loopback. Sensitive local bridge surfaces are
not general static-file routes.

Systemd units and timers

The user units encode custom dependency, environment, retry, and restart
behavior. They keep the bridge, watcher, Unified Clock, and notifier alive;
they schedule channel retry and backups without embedding credentials in unit
files.

Skills and verification harnesses

The project skills are custom operational runbooks for future agents. Browser
tests, API permission probes, QR decode checks, mailbox queries, notification
tag audits, and backup-result checks are intentionally executable verification
steps rather than unverified documentation claims.

Cloud architecture

Public request API

The public form submits JSON to POST /api/service-requests with an
idempotency key and Turnstile response. The Worker validates source path,
origin, contact method, required fields, and rate limits before writing D1.

A successful request:

  1. Receives a stable SR- identifier.
  2. Is committed to D1 before acknowledgement.
  3. Enqueues a minimal event without private problem-description text.
  4. Schedules documentation email.
  5. Returns HTTP 202.

Private API groups

text
GET    /api/service-requests/admin/config
GET    /api/service-requests/admin/requests
GET    /api/service-requests/admin/requests/:id
PATCH  /api/service-requests/admin/requests/:id

GET /api/service-requests/admin/operations
PATCH /api/service-requests/admin/operations
GET /api/service-requests/admin/events-summary
GET /api/service-requests/admin/events-summary/audio
POST /api/service-requests/admin/events-summary/refresh
GET /api/service-requests/admin/work-items
POST /api/service-requests/admin/work-items
PATCH /api/service-requests/admin/work-items/:id
POST /api/service-requests/admin/work-items/:id/actions
GET /api/service-requests/admin/time-entries
POST /api/service-requests/admin/time-entries
PATCH /api/service-requests/admin/time-entries/:id
GET /api/service-requests/admin/staff
POST /api/service-requests/admin/staff
PATCH /api/service-requests/admin/staff/:id
POST /api/service-requests/admin/staff/:id/archive
POST /api/service-requests/admin/staff/:id/restore
POST /api/service-requests/admin/staff/:id/hire
GET /api/service-requests/admin/timeline
GET /api/service-requests/admin/operations/events
POST /api/service-requests/admin/operations/events

GET /api/service-requests/admin/contacts
POST /api/service-requests/admin/contacts
GET /api/service-requests/admin/contacts/:id
PATCH /api/service-requests/admin/contacts/:id
POST /api/service-requests/admin/contacts/:id/archive
POST /api/service-requests/admin/contacts/:id/restore
GET /api/service-requests/admin/contacts/:id/interactions
POST /api/service-requests/admin/contacts/:id/interactions
GET /api/service-requests/admin/projects
POST /api/service-requests/admin/projects
GET /api/service-requests/admin/projects/:id
PATCH /api/service-requests/admin/projects/:id
GET /api/service-requests/admin/inventory
POST /api/service-requests/admin/inventory
GET /api/service-requests/admin/inventory/:id
PATCH /api/service-requests/admin/inventory/:id
GET /api/service-requests/admin/search?q=...

GET /api/service-requests/admin/tokens
POST /api/service-requests/admin/tokens
PATCH /api/service-requests/admin/tokens/:id
POST /api/service-requests/admin/display-session

All private routes require a valid bearer credential. Role checks and response
filtering happen in the Worker, not only in the dashboard.

D1 migrations

| Migration | Adds |
| --- | --- |
| 0001_initial.sql | Public service requests, delivery status, consent, metadata, retention indexes |
| 0002_admin_fields.sql | Internal notes, scheduling, resolution, close time, admin status index |
| 0003_admin_tokens.sql | Named hashed credentials, role, expiry, revocation, first-use tracking |
| 0004_operations_state.sql | Revisioned strategic operations document |
| 0005_operations_events.sql | Generic work items, staff profiles, timeline, initial operating plan |
| 0006_staff_profile_details.sql | Provenance-labeled profile JSON snapshot field |
| 0007_admin_token_scopes.sql | JSON scope used to link staff guest credentials to one staff profile |
| 0008_staff_lifecycle_and_provenance.sql | Staff capabilities and archive metadata, work assignees/activity, source event identity/completeness/capture time |
| 0009_time_clock.sql | Numeric staff pay rates, actor/source-linked time entries, one-active-timer enforcement, duration/pay snapshots, and ledger indexes |
| 0010_contacts_projects_inventory.sql | External contacts/interactions, projects, physical inventory, normalized relationship tables, follow-up/decision/handoff work fields, and the initial Deer Valley people/project/work context |
| 0011_candidate_sale_reconciliation.sql | Candidate/staff classification, applicant lifecycle, source-specific claims, provisional credential state, sale lifecycle, external dependencies, tax/homestead evidence states, and the August 19 Deer Valley reconciliation |
| 0012_candidate_dossier_completeness.sql | Complete source-attributed dossiers for the four known candidates, including conflicting profile/application claims and partial-message provenance |
| 0013_inference_and_owner_planning.sql | Conditional staffing/schedule planning and separate administrative work without creating shifts, offers, or employment agreements |
| 0014_stale_candidate_task_reconciliation.sql | Reclassifies expired candidate reminders as waiting history so old due dates cannot outrank current work |
| 0015_command_brief_priority_reconciliation.sql | Preserves the superseded Lisa-priority correction as auditable history |
| 0016_norma_manual_state_restoration.sql | Restores the Owner's manual truth: Lisa conversation closed, Norma contacted/awaiting response, no trial or employment implied |

Never edit or reuse an applied migration number. Add the next numbered file,
apply it locally and remotely, then deploy code that depends on it.

D1 is Cloudflare's managed SQLite-compatible database. It is the authoritative
cloud state store for this project; the browser reaches it only through the
Worker's validation and authorization layer.

Dashboard and access model

Work domains

Generic work items support these domains:

  • IT
  • household
  • staff
  • property and sale transition
  • inventory
  • administrative
  • recurring non-medical household assistance

Shared fields include owner, priority, status, due time, location, notes,
attachments, dependencies, recurrence, visibility, assigned staff IDs,
staff-visible activity, audit timestamps, and creator identity.

The Work board and Schedule use the same work-item records. Saving an
assignment is explicit, and assigning staff automatically requires staff or
public visibility. Routine definitions are canonical objects with frequency,
interval, optional weekly weekdays, and optional end date; recurrence requires
a first due time and is validated by the Worker.

People, projects, inventory, and events

The private operations model deliberately keeps different concepts separate:

| Object | Meaning |
| --- | --- |
| Service request | An outside IT intake case |
| Work item | An action somebody needs to perform |
| Candidate profile | A person in the applicant pipeline; may have a provisional preview credential but has no employee, assignment, shift, time-clock, or payroll semantics |
| Staff profile | A person converted by an explicit Owner hire action, with duties, availability, assignments, timekeeping, and separately scoped access |
| Contact profile | A private external associate such as a realtor, buyer, vendor, contractor, lender, or title contact |
| Project | A grouping context connecting people, work, inventory, dates, and history |
| Inventory item | A persistent physical object or container with a printed human-readable ID |
| Timeline/contact event | A provenance-labeled fact or interaction that occurred |

Creating a contact never grants access. Brenda and Ashley are external contact
records rather than staff, and no phone or email is manufactured when the
source material does not establish one. All four current Care.com people are
candidate records. Nobody is hired until the Owner deliberately invokes
POST /staff/:id/hire; ordinary profile edits cannot cross that boundary.

Candidate facts are normalized in candidate_claims and attributed to
candidate_sources. Profile, application, notification, direct-message, and
Owner-outbound claims coexist. A profile value of 10 years and a narrative
claim of 15+ years produce a visible source-discrepancy marker instead of an
invented resolution. Compatible variants such as 3 years versus more than 3
years
, or $19 starting versus a $19-29 range, are treated as aligned and
displayed as one primary value with the alternate retained in the detailed
provenance panel. Background-check products retain their exact platform label
and date; missing reviews are neutral. The dashboard computes no opaque
candidate score and does not use photos or protected/irrelevant personal
attributes for ranking. User priority is explicitly entered and is not a model
inference.

Candidate lifecycle values distinguish applied, contacted, screening,
interview_requested, interview_scheduled, trial_proposed,
trial_scheduled, awaiting_response, unresponsive, on_hold, declined,
withdrawn, and not_selected. Requested is not scheduled; a time passing is
not attendance; proposed is not agreed; a candidate profile is not employment.

The command brief is reconciled by
scripts/household_operations_inference.py, run persistently by
sol37-household-brief-inference.service. It reads the authoritative project,
dependency, candidate, work, and timeline state, computes a stable source hash,
and writes only when the derived brief changes. PATCH /admin/operations
requires the caller's expectedRevision; a stale inference write receives
HTTP 409 instead of overwriting a newer Owner edit. The reconciler also skips a
brief whose inference metadata has manualOverride: true; an Owner must
explicitly clear that flag before automatic inference resumes. The current guardrails keep
completed/declined conversations closed, treat Norma's contacted state as
authoritative, and never turn a proposed trial or schedule into an agreement.
If the local model endpoint returns unusable output, deterministic rule-based
fallback still refreshes the sale phase, dependency summary, newest Care
application, candidate count, and next action without exposing model errors in
the dashboard. For a newest Care application, the fallback distinguishes an
email-only partial notification from a candidate whose owner-supplied profile
PDF or screenshot dossier is already attached; the Care notification excerpt
remains partial either way.
The browser independently refreshes operations every 60 seconds while idle and
visible, but pauses during editing or active display interaction.

Projects also retain a separate sale lifecycle and private state document.
project_dependencies records what is expected from an external party without
pretending it happened. project_evidence keeps homestead and tax evidence
states distinct: prepared, delivered, received, accepted, granted, and account
adjustment are not interchangeable.

work_item_contacts, project_contacts, and work_item_inventory make the
relationships navigable in both directions. A work item can also carry a
project, subtype, independent follow-up time, decision-required flag, and a
small sanitized staff handoff. Staff Guest serialization removes project and
external-contact data before response delivery; it preserves only the
task-visible objective, permitted/prohibited actions, decision authority,
staff-safe contact context, and linked inventory IDs.

Inventory IDs such as LAB-BOX-001 are immutable human-readable references.
The initial physical inventory is intentionally empty until a real item or
container is observed. Unknown ownership or disposition defaults to REVIEW;
DISPOSE_APPROVED is explicit and is never inferred from an unidentified item.
QR labels may supplement but never replace the printed ID.

Access profiles

| Profile | Capabilities |
| --- | --- |
| Owner | Full IT, household, staff, private contacts/projects/inventory/search/timeline, schedule/routine editing, all time entries and corrections, and credential management |
| Operator | IT, household, staff operational editing, contacts/projects/inventory/search, and work assignment; no credential, staff archive/restore, or time-ledger administration |
| Staff guest | One explicitly hired active staff profile, staff/public work, schedule, protocols, sanitized handoffs, capability-scoped task actions, and its own assigned-work timer/ledger |
| Candidate guest | One provisional candidate preview, staff-safe context only, no work mutation, assignments, time clock, or employee semantics |

Schedule rows cross the guest boundary only when explicitly marked staff or
public. Unmarked legacy rows remain Owner-only, and assigned schedule rows
are limited to the credential's linked staff profile.

The staff command mirror is stored as operations.staffBrief and edited in the
Owner command form. It is separate from operations.brief so an Owner can keep
private decisions, contacts, and evidence out of Staff Guest responses. If no
staff brief exists, the Worker returns a minimal personalized safe fallback; it
does not copy the private brief.

Staff guests cannot access IT requests, the private timeline, Care event
routes, contacts, projects, private search, private inventory collections,
other staff records, private work, token management, personnel CRUD, or generic
mutation routes. Their only task-mutation write path is
POST /work-items/:id/actions, which requires the linked active staff record,
explicit assignment, and the exact capability (task.acknowledge,
task.start, task.complete, or task.note_public). The Worker remains the
security boundary. The Overview makes these manual-update paths prominent for
active staff. A provisional candidate sees the same planning context with a
clear locked state until an explicit Owner hire action, active credential, and
real assignment exist.

The deliberate exception is active Staff Guest timekeeping: POST /time-entries may
start a timer only for explicitly assigned work, and PATCH /time-entries/:id
may stop only that guest actor's own active timer. A Staff Guest cannot submit
general/IT time, manual entries, rate overrides, corrections, voids, or another
actor's entry.

Guided help

Every authenticated role has a Help & tour action. The Help dialog lists
only that role's available features. The first-run tour switches through the
actual permitted panels, spotlights the live controls, supports Back/Next,
arrow keys and Escape, adapts to phone width, and can be replayed. Its seen
flag is browser-local UI state and contains no operational or credential data.

Credential lifecycle

- Raw credentials are returned exactly once.
- D1 stores SHA-256 hashes, not recoverable raw tokens.
- Tokens have independently named profiles, roles, scope, expiry, revocation,
lastUsedAt, and credential lifecycle state.
- The browser receives the token in a URL fragment, consumes it, removes the
fragment, and retains it in per-tab session storage.
- Personal devices normally receive named Operator credentials.
- Each staff member receives a separately scoped Staff guest credential.
- Candidate-linked credentials are provisional (or scheduled after an
explicitly recorded interview/trial schedule). They remain read-only and do
not prove hiring. Explicit hire moves linked live credentials to active.
- Direct menu cards are identities, not retained raw secrets. Selecting David
rotates only David-subject/personal cards; selecting a staff member rotates
only Staff Guest cards linked to that exact staffId.
- Revoking one profile does not affect other people or devices.
- Archiving a staff profile immediately invalidates it and revokes every active
Staff Guest credential linked to that profile, then removes the archived
staff ID from active work assignments while retaining activity history.
- Restoring a profile puts it in paused; previously revoked credentials stay
revoked and a new credential must be issued deliberately.

The desktop QR handoff polls the new credential's lastUsedAt field every two
seconds. After the phone performs its first authenticated request, the window
shows a connected state, emits a private local notification, and closes. A
manual close does not revoke the credential.

Clipboard copies, credential checks, and revocation requests run outside the
Tk UI thread so the QR window stays responsive during connection delays.
Xclip output is detached and copy requests have a five-second timeout per
selection; the clipboard owner cannot keep a captured pipe open and freeze
the window. Closing a window safely ignores pending worker results.

The XFCE access menu includes direct Owner entries for the desk, Household
Operations, Staff Lifecycle and assignments, and access-profile management.
Its Display Broadcast section starts, stops, and checks the Mac Mini plus
Record Player service-desk presentation. Its Authentication Cards section adds
direct rows for David and the current non-archived staff roster; the Credential
Tools section retains configurable personal/Operator and linked Staff Guest
creation, status, and revocation. Dashboard fragments may include panel=staff;
the client consumes and removes the fragment before showing the selected
protected panel.

Local event and notification architecture

Signed bridge

The Worker queue consumer signs the raw JSON request with HMAC-SHA256 over:

text
timestamp + "\n" + nonce + "\n" + raw_body

The local bridge validates the signature, timestamp window, nonce uniqueness,
body size, and event type before committing the event to local SQLite. Caddy
exposes only the intended bridge path and strips its prefix before forwarding
to 127.0.0.1:8914.

Independent delivery channels

The bridge records each destination independently:

  • XFCE popup via notify-send
  • custom-notifier history
  • Unified Clock private phone push
  • customer confirmation email when applicable

One failed secondary channel does not cause an already accepted phone push to
be resent. Stable event IDs become stable push tags. The retry timer revisits
only incomplete destinations every two minutes.

Unified Clock push path

The bridge and mail watcher submit only to the localhost endpoint:

text
POST http://127.0.0.1:18765/api/push/send

Unified Clock owns the VAPID keys, browser subscriptions, encrypted Web Push
delivery, private notification history, and canonical Master Log. Accepted
pushes are mirrored into custom-notifier history as non-spoken System entries.

Care notification email integration

There is no Care SDK, browser scraper, session-cookie automation, or private
API client in this project. The supported workaround uses notification email
already delivered to the account owner plus screenshots and records
deliberately provided by the owner.

The watcher recognizes a narrow subject set:

  • new message
  • application received
  • video interview confirmation

It verifies that the parsed sender address ends in @care.com, normalizes
invisible characters, derives a stable event ID from Message-ID, extracts a
bounded notification excerpt, and records explicit email provenance. The
normalized record includes sourceEventId, capturedAt, and
completeness: partial; the dashboard therefore labels it Care email
notification · partial excerpt
. It does not fetch a profile or conversation
from the Care website.

The watcher then independently delivers the event to D1 timeline, XFCE,
custom-notifier history, and Unified Clock private push. Its local ledger
prevents duplicate channel delivery.

Data stores

| Store | Path or resource | Contents |
| --- | --- | --- |
| Cloudflare D1 | sol37-service-requests | Requests, access profiles, strategic state, work items, staff, external contacts/interactions, projects, inventory, relationships, time entries, timeline |
| Bridge SQLite | ~/.local/state/service_request_bridge/bridge.db | Nonces and per-channel ticket-event delivery state |
| Mail watcher SQLite | ~/.local/state/service_request_mailwatch/watch.db | Care event payloads and per-channel delivery state |
| Solmail SQLite | ~/.local/mail/solmail.db | Message metadata, paths, UIDs, dates, and cursors |
| Solmail Maildir | ~/.local/mail/icloud | Immutable local .eml source messages |
| Property correspondence ZIP | ~/Downloads/deer-valley-correspondence-archive-2026-08-19.zip | Mode-0600 original messages, extracted attachments, contextual records, CSV index, and integrity manifest |
| Custom notifier history | ~/.local/state/custom_notify/history.json | Local notifier history and System mirrors |
| Unified Clock state | ~/.local/state/sol_unified_clock/ | Push keys/subscriptions, notification history, schedules, master log |
| Owner credential | ~/.config/sol37-services-admin-token.txt | Mode-0600 bootstrap owner bearer token |
| Bridge environment | ~/.config/service-request-bridge.env | Local HMAC and bridge configuration |
| Solmail environment | ~/.config/solmail.env | IMAP/SMTP account configuration |

Never print, commit, screenshot, or include the contents of the three secret
files above in a command argument or report.

Security and privacy boundaries

- Public intake requires server-side Turnstile verification.
- Public writes use idempotency keys and per-IP hashing/rate limits.
- Allowed origins and public source paths are explicit.
- Queue payloads omit full ticket details and contact destinations.
- Cloud-to-local delivery uses HMAC, timestamp checks, and nonce replay defense.
- Owner, Operator, and Staff guest permissions are checked in the Worker.
- Staff guest collection responses are filtered before serialization.
- External contact profiles, interactions, projects, private search, and the
private inventory collection return HTTP 403 to Staff Guest. Task responses
remove contact/project references before serialization.
- Staff actions require active-profile resolution, explicit work assignment,
and a named per-profile capability; hiding a button is not authorization.
- Staff time requires an active linked profile and exact work assignment;
server time and snapshotted cents-per-hour drive the estimate.
- Completed time is corrected or voided, never silently deleted; void entries
stay visible to Owner audit queries but do not count toward totals.
- Staff archive revokes all active linked guest credentials in the same D1
batch as the profile transition.
- Raw access credentials are one-time values; D1 stores only hashes.
- QR creation is local in the browser or workstation.
- Desktop QR PNGs are temporary mode-0600 files and are deleted on close.
- Mail excerpts are bounded and provenance-labeled.
- Detailed background-check content is outside the general operations record.
- Internal notes and resolution text are excluded from phone-push payloads.
- Local services bind to loopback unless an explicit authenticated gateway is
intended.
- Backups are deduplicated and verified for missing sources.

Development and deployment

Prerequisites

The current workstation already has the required environment. On a fresh
system, the minimum categories are:

  • Node.js and npm
  • Python 3 with Tk and Pillow
  • Wrangler authenticated to the correct Cloudflare account
  • QRencode, Zenity, Xclip, libnotify, and XDG Utils
  • Caddy for the local static and reverse-proxy surface
  • configured Solmail IMAP/SMTP credentials
  • running Unified Clock and custom-notifier services

Install Worker dependencies

bash
cd /home/david/random/services-platform
npm ci

Do not infer application dependencies from unrelated global packages or
extraneous node_modules entries. package.json and the lockfile are the
authority.

Apply D1 migrations

bash
cd /home/david/random/services-platform
npx wrangler d1 migrations list sol37-service-requests --local
npx wrangler d1 migrations list sol37-service-requests --remote
npx wrangler d1 migrations apply sol37-service-requests --local
npx wrangler d1 migrations apply sol37-service-requests --remote

Inspect the list before applying. A deployment is not complete while the
remote schema is behind the Worker.

Deploy the Worker

bash
cd /home/david/random/services-platform
npm run check
npm run deploy
npx wrangler deployments list

The dashboard is a static file served from /home/david/random/www; it does
not require a JavaScript build. Verify the live file with a cache-busting query
after editing.

Operations

User services

| Unit | Purpose | Policy |
| --- | --- | --- |
| service-request-bridge.service | Signed event receiver on loopback | Restart on failure |
| service-request-bridge-retry.timer | Retry incomplete delivery channels | Every two minutes |
| service-request-mailwatch.service | Solmail sync and Care event ingestion | 60-second loop, restart on failure |
| sol-unified-clock.service | Local push API and notification center | Restart on failure |
| custom-notify.service | Persistent desktop notifier | Always restart |
| bluebubbles-notify-bridge.service | Incoming ledger, explicit replies, independent notification fan-out | Existing message/reply path |
| bluebubbles-health.timer | Mac BlueBubbles and Linux bridge health | Read current timer state |
| sol37-bluebubbles-automation.timer | New-message scheduling functions and private push outbox | 20 seconds after the previous pass; model processing adds latency |
| sol37-household-brief-inference.service | Local brief and legacy clinical-message triage | Preserves manual brief override |
| sol37-desk-screen-fleet.service | Mac and approved Cast presentation | Effective mode depends on drop-ins; inspect before changing |
| sol-litellm.service / sol-chat-model.service | Local model gateway / inference backend | Gateway health alone does not establish completion latency |
| sol37-backup.timer | Deduplicated runtime snapshot | On boot and every six hours |

Status commands:

bash
systemctl --user status service-request-bridge.service --no-pager
systemctl --user status service-request-mailwatch.service --no-pager
systemctl --user status sol-unified-clock.service --no-pager
systemctl --user status custom-notify.service --no-pager
systemctl --user list-timers service-request-bridge-retry.timer sol37-backup.timer

Logs:

bash
journalctl --user -u service-request-bridge.service -n 100 --no-pager
journalctl --user -u service-request-mailwatch.service -n 100 --no-pager
journalctl --user -u sol-unified-clock.service -n 100 --no-pager

The mail watcher has no command-line parser. Do not invoke
service_request_mailwatch.py --help; that starts a second daemon loop. Manage
it through systemd.

Access administration

Use the XFCE panel launcher or run:

bash
/home/david/bin/sol37-services-admin-token-panel

Its current sections and actions are:

1. Open & Manage: Services Desk, Household Operations, staff lifecycle and
assignments, and access/status management.
2. Display Broadcast: start the Mac Mini/Record Player presentation, end it
for the night, or inspect latest display fleet status.
3. Authentication Cards: direct David and per-staff QR presentation rows.
These rotate that person's prior linked credential after confirmation.
4. Credential Tools: configurable personal/operator and Staff Guest
creation, access-profile status, and explicit revocation.

Display broadcast CLI equivalents:

bash
systemctl --user start sol37-desk-screen-fleet.service
/home/david/random/bin/sol37_desk_screen_fleet.py --stop-displays
/home/david/random/bin/sol37_desk_screen_fleet.py --status

CLI examples:

bash
python3 /home/david/bin/sol37_services_admin_tokens.py list
python3 /home/david/bin/sol37_services_admin_tokens.py staff
python3 /home/david/bin/sol37_services_admin_tokens.py create \
  --label "Named personal device" --role operator --days 30 --json
python3 /home/david/bin/sol37_services_admin_tokens.py create \
  --label "David personal access" --role operator --subject-id david \
  --subject-name "David Lones" --days 30 --json
python3 /home/david/bin/sol37_services_admin_tokens.py create \
  --label "Named staff access" --role staff_guest --staff-id STAFF_ID --days 1 --json
python3 /home/david/bin/sol37_services_admin_tokens.py revoke TOKEN_ID

The --json create response contains a one-time raw credential. Redirect it to
a mode-0600 file rather than leaving it in terminal scrollback.

Mail sync and response check

bash
python3 /home/david/random/bin/solmail.py sync --limit 20
python3 /home/david/random/bin/solmail.py newest --show 20
python3 /home/david/random/bin/solmail.py stats
python3 /home/david/random/bin/solmail.py backfill --limit 25
python3 /home/david/random/services-platform/scripts/build_property_correspondence_archive.py

When checking a specific staff response, query only the relevant sender/event
rows and avoid printing unrelated mail.

Verification

Static and Python syntax

bash
cd /home/david/random/services-platform
npm run check

python3 -m py_compile \
/home/david/random/bin/service_request_bridge.py \
/home/david/random/bin/service_request_mailwatch.py \
/home/david/random/bin/solmail.py \
/home/david/random/bin/sol37_record_player_display.py \
/home/david/random/bin/sol37_desk_screen_fleet.py \
/home/david/.local/bin/bluebubbles_notify_bridge.py \
/home/david/random/bin/sol_vision_camera.py \
/home/david/random/services-platform/scripts/build_property_correspondence_archive.py \
/home/david/bin/sol37_services_admin_tokens.py \
/home/david/bin/sol37-services-admin-token-panel

/home/david/.local/bin/bluebubbles_notify_bridge.py --self-test

Extract the dashboard inline script to a temporary .js file and run
node --check. Check the vendored QR script separately.

Live authorization matrix

Use separately issued test credentials and verify:

- Owner can access all private routes and manage tokens.
- Operator can read/write IT and household routes but receives HTTP 403 for
token management.
- Staff guest receives only sanitized operations, staff/public work items, and
its one linked active staff profile.
- Staff guest receives HTTP 403 for IT, timeline, Care event, token, detail,
personnel, contact, project, inventory, private-search, generic work-item
mutation, and unassigned task-action routes.
- An assigned task action succeeds only when the required capability is
present; acknowledge/start/complete/public-note activity is audited.
- Owner can start/stop self time, add manual entries, correct/void records, and
see all actors; Staff Guest can start/stop only its own assigned-work timer.
- Active duration/pay update in both the current-clock card and ledger, while
void entries contribute zero to summary totals.
- Archiving the linked profile revokes the guest token immediately; restore
does not resurrect that credential.

The August 19 live authorization test created a one-day Staff Guest credential,
confirmed four visible work items with four explicit sanitized handoffs, zero
contact links and zero project IDs, received HTTP 403 from contact, project,
inventory, and private-search collections, then revoked the credential and
confirmed its next request also returned HTTP 403.

Browser checks

Use Playwright at 390x844 phone, 768x900 tablet, and 1290x997 desktop sizes.
Confirm:

- no console errors
- no horizontal overflow
- correct navigation for each role
- only capability-granted actions appear on work assigned to the Staff guest
- no visible Care or private timeline text in Staff guest mode
- Associates is visible only to Owner
- People, Projects, Inventory, and private cross-model search appear only for
Owner/Operator; Staff Guest receives neither the tabs nor their data
- access/profile/status descriptions are present
- IT toolbar and Household Operations navigation have equal heights per viewport
- the header compacts after page or internal content scrolling
- desktop compact navigation shows the complete title and every action without overflow
- phone and tablet action rails do not create document overflow and expose 44px touch targets
- the public services header and hero stay within the viewport at all three widths
- Help content and guided-tour steps omit controls denied to the current role

QR checks

1. Generate the exact credential URL locally.
2. Decode the browser or desktop QR with OpenCV.
3. Assert exact string equality, not only successful detection.
4. Verify the received browser removes the fragment and resolves the intended
role/scope.
5. Confirm lastUsedAt changes after first authenticated use.
6. Confirm the desktop QR handoff closes automatically.

Notification checks

Use a unique stable tag. Confirm:

  • /api/push/send returns both ok and queued
  • one matching notification-history record exists
  • activity records reach queued and sent
  • exactly one custom-notifier mirror exists
  • retrying a secondary history channel does not resend the phone push

Adding people and devices

Personal device or trusted operator

Use the direct David card in the XFCE menu for normal phone handoff; it rotates
the prior David-linked personal card and leaves unrelated operators untouched.
Use configurable Operator creation for a separate device/person. Choose an
expiry appropriate to its trust level. Never share the owner credential for
routine mobile access.

Candidate and staff member

1. Create the candidate record with the Owner-only Add candidate profile workflow or
POST /admin/staff.
2. Preserve each source claim and lifecycle event without converting requested,
proposed, or elapsed appointments into completed stages.
3. A provisional candidate QR may demonstrate the guest workspace, but cannot
mutate work or use the time clock and does not establish employment.
4. When hiring is actually agreed, invoke the explicit Owner-only
POST /admin/staff/:id/hire transition. Do not simulate hire through PATCH.
5. Define final role, hours, availability, rate, permitted/prohibited duties,
onboarding checklist, and only the required task capabilities.
6. Mark only intended work items staff or public and explicitly assign the
staff profile before expecting action controls.
7. Choose that person in the XFCE Authentication Cards section to rotate and
present a Staff guest credential linked to that exact staffId, or use the
configurable Staff Guest workflow for a nonstandard expiry/label.
8. Present its short-lived QR and wait for first-use auto-close.
9. Verify the guest workspace before extending expiry.
10. Run a clock-in/out test against an explicitly assigned task and confirm the
linked profile rate and private per-person ledger scope.
11. Archive the staff profile during offboarding. This revokes every active
credential linked to that person without affecting other staff or devices.
12. If the person returns, restore the profile in paused state, review duties
and capabilities, and issue a new credential.

Do not put multiple staff members behind a shared guest token. Per-person
credentials provide scope, first-use time, expiry, and independent revocation.

Backups and recovery

The sol37-backup.timer invokes a deduplicated snapshot every six hours and
after major manual changes when explicitly started:

bash
systemctl --user start sol37-backup.service
systemctl --user status sol37-backup.service --no-pager
journalctl --user -u sol37-backup.service -n 20 --no-pager

The configured source set includes the static site and runbooks plus the
Services Desk Worker source, migrations, package manifests, Wrangler
configuration, canonical README, the desktop access-panel/API-client scripts,
and the backup implementation itself. node_modules and local Wrangler
scratch state are deliberately excluded.

Before destructive D1 work or after a material private-data migration, create
a mode-0600 remote SQL export under
~/.local/state/sol37/service-desk-d1-exports/. That state tree is itself a
snapshot source. Treat the export as private because it contains authoritative
operational records and hashed credentials; never publish it with the public
README or site.

Do not call a backup complete until its JSON result reports
"missing_sources": []. Snapshot IDs and item counts are runtime state, so
inspect the latest service result rather than relying on a fixed value in this
README.

The live Record Player HLS transport directory
~/.local/state/sol37/services-desk-screen-fleet-hls/ is intentionally
excluded. Its rotating .ts segments and playlist are a regenerable playback
cache that cannot be snapshotted consistently while casting. Fleet status,
rendered desk/QR/Vision artifacts, logs, source, D1 exports, and all other
persisted Sol-37 state remain included.

Cloudflare D1 remains the live authoritative store. The local SQL export is a
recovery artifact, not a second writable database.

Known constraints

- Care notification email is not full message synchronization. It can omit
content or present truncated excerpts.
- There is no supported Care consumer API in this implementation.
- Screenshots and manually supplied records require deliberate updates when
source profiles change.
- Cloudflare Send Email destination verification remains a separate delivery
requirement; local iCloud SMTP confirmations are the established path.
- LAN Caddy fallback currently proxies /api/service-requests
to pinned
Cloudflare edge addresses. Those addresses must be refreshed if Cloudflare
changes them.
- The bootstrap owner secret remains available as a recovery boundary. Normal
people and devices should use D1-backed named credentials.
- Closing a QR window does not revoke its credential. Revoke explicitly if the
QR was abandoned or exposed.
- A one-time token cannot be reconstructed from D1. If its reveal is lost,
revoke it and issue a replacement.
- Time-clock pay is an estimate from elapsed time and a rate snapshot. Payroll,
overtime, breaks, withholding, approval, and statutory recordkeeping remain
outside this application.
- Contact profiles are private operating records, not a messaging transport.
Recording an interaction does not send a message, accept a buyer offer, hire
a candidate, or grant a credential.
- Inventory photo/attachment values are references. Binary media storage and
printable QR-label production remain separate workflows.

Related runbooks

The checked-in code, current service units, live D1 schema, and live API win if
an older runbook or version snapshot disagrees.

Temporary Home Health visit display

The visit can include a supplied transcript and an Early-voice recording.
Transcript metadata is stored in the authenticated visit configuration; audio
stays on the SOL data drive. Owner/Operator GET /home-health/audio proxies the
existing local service-request bridge using its HMAC authentication. The local
bridge serves only the fixed narration file, never an arbitrary requested path.
Unauthenticated and Staff/preview access is denied. The player fetches authenticated audio on demand, supports playback controls,
never autoplays, and survives brief refreshes. Signing out stops playback and
releases media resources. Supplied
interpretations and citations are not independently verified medical findings.

The user-selected recording uses cloud ElevenLabs Early. The original cloud
file is retained privately on SOL; a mono 48-kbps playback copy reduces transfer
size. Compatible browsers progressively buffer authenticated MP3 audio through
MediaSource, while others show download progress before blob playback. A second
option reads the same transcript with browser speech synthesis and offers
pause/resume and stop. Neither option autoplays; starting one stops the other.
The shortened narration includes a brief assistive-technology and dashboard
overview. Take dashboard tour pauses narration and opens the existing tour.

After the expected arrival window passes, the dashboard shows OVERDUE and
awaits direct communication or an explicit disposition. Elapsed time never
marks the visit complete. A message discovered on an unmonitored patient phone
is distinguished from no relevant communication detected in monitored feeds.

The visit lifecycle now treats overdue as persistent until an explicit owner
disposition. It records outreach sent, failed or unconfirmed, response review,
arriving, arrived, rescheduled, cancelled, completed and manually resolved
states. Visit updates are idempotent and retained in the household timeline.
The confirmed window comes from authenticated visit configuration. Medical
schedule entries with an elapsed known end/due time rise as overdue; they are
not silently completed. Unresolved Home Health monitoring continues after the
calendar date changes.

scripts/home_health_outreach.py reconciles exact-recipient Mac Messages
records through the existing Mac connection. Explicit one-shot sending commits
a durable per-recipient claim before transmission; interrupted or failed sends
are never automatically retransmitted. The existing household inference monitor
publishes receipts and replies, including sender, transport, timestamps and
delivery/read uncertainty. Delivery does not imply clinical acknowledgment.
An owner-supplied group conversation can be monitored after verifying its exact
membership and sender; the prior failed individual attempts remain in history.
Group delivery indicators are not treated as proof of per-recipient delivery.
An owner records confirmed ETAs and dispositions through the visit control;
incoming message content cannot execute these controls. The persistent contact
action asks for the agency's structured scheduling/caregiver contact field.

Phone QR sign-in for computers

On an authenticated phone, Sign in another computer creates a two-minute, single-use QR handoff. On the destination sign-in page, Scan sign-in QR with webcam opens the camera on user request: rear camera preferred on phones/tablets, front webcam preferred on computers. QR frames are decoded locally by vendored jsQR 1.4.0; no images are uploaded. Camera tracks stop after scanning, closing the dialog, leaving the page, or hiding it. Normal token entry remains available.

Migration 0021 stores only hashes of handoff codes and short-lived browser credentials. The /api/service-requests/admin/qr-login/create, /redeem, and /revoke POST routes use the existing authentication principal. Atomic DELETE RETURNING consumes a valid grant once; a successful scan creates an eight-hour session whose permissions and continuing validity depend on its issuing credential. Revoking the issuer invalidates its QR sessions. Staff/preview credentials cannot issue handoffs, and QR sessions cannot mint additional handoffs. Signing out revokes the QR session. Codes and session secrets are sent in JSON responses/request bodies, never public QR image services, URL query strings, or logs. A generated QR is shown only in the authenticated phone dialog; ordinary TV QR/card policy is unchanged.

The header always keeps Sign in another computer in the primary action rail for authenticated Owner or Operator sessions. A QR-derived session shows the same control disabled with the reason, so the handoff policy is visible instead of appearing to have disappeared.

The jsQR Apache-2.0 license is included beside the pinned browser library. The existing QR generator creates phone codes. Mobile scanning requests an exact environment-facing camera, falls back to an enumerated Back/Rear device if needed, and offers a camera selector. The versioned scanner script prevents reuse of the earlier front-camera implementation. Live verification covers camera-feed decode and login, stopped camera tracks, concurrent single-use redemption, role preservation, owner-route denial for delegated Operators, revoked issuers, and invalid/reused codes. The actual destination webcam still requires the user's browser camera permission.

The local Services Desk Access launcher separately requests the invoking Linux account's sudo password. Its masked dialog identifies the account and validates via sudo -S -k -p '' -v, passing the password only through stdin. This password is never submitted to the website.

September 10 policy update: the user cancelled Lee's temporary exception during the visit. config.leeExcluded, the persistent local exclusion, disabled dated timers, and a paused follow-up prevent further automatic Lee actions. Stale queued retries cannot re-enable it. The dashboard labels it EXCLUDED · PERSONAL USE and shows the visit countdown without a Lee shutdown promise. Mac Mini, Living Room, and Record Player remain selected. Subsequent general fleet verification skips Lee's receiver.

The private /api/service-requests/admin/home-health endpoint stores the September 10 visit configuration, fleet telemetry, and idempotent display commands in D1 (migration 0019). Owner and Operator can read and enqueue commands; only Owner can configure and publish telemetry. Staff and preview guests are denied. Clinical copy lives in authenticated D1, not public HTML.

bin/sol37_home_health.py extends sol37_desk_screen_fleet.py; it does not run a second media server. Temporary home-health mode selects Mac Mini, Record Player live HLS, and Living Room live HLS only. Lee uses that same live pipeline during its temporary exception. Ordinary Lee exclusions are unchanged. Two persisted systemd calendar timers execute the fixed September 10, 11 AM and 1 PM Chicago transitions. Absolute-time guards reject early and expired activation; restoration affects Lee only. Original receiver state, retry state, selected-display stops, and event results are retained in private local state. Both events include read-only post-transition verification with local screenshots and receiver evidence.

Manual controls are under Household Operations → Command brief → Home Health Visit. Local commands use python3 /home/david/random/bin/sol37_home_health.py retry lee-tv, restore lee-tv, or end-all all. verify-now records current evidence without controlling devices. The visit hides QR and camera floaters by request; the existing local Show cards control remains available. Record Player continues through the fleet's live H.264 compositor. HLS explicitly declares its video codec in a master playlist. Fleet network-address changes rebuild its stream, and Mac failure no longer prevents independent Cast reconciliation.

The compositor timestamps live inputs from the wall clock to prevent progressive playback drift. Receiver reconciliation requires PLAYING, and verification checks advancing media time; HTTP success alone is insufficient. Lee recovery is bounded and never runs outside its fixed window.

Migration 0020 adds private monitor_json state. scripts/home_health_message_monitor.py runs inside the existing household brief inference service every 90 seconds, separately from its manual brief override. It reads recent local Solmail and BlueBubbles ledgers, prefilters relevant messages, and uses the existing local model in bounded batches. Source identities and supporting quotations are validated before publishing short summaries with source times to the authenticated dashboard. Raw messages stay local. No outgoing replies or physical-arrival confirmations are generated. The top visit panel distinguishes active monitoring, degraded analysis, and source-sync health; service_request_mailwatch.py writes a private sync heartbeat. Unchanged source batches reuse the last analysis. The monitor is limited to the configured visit date.

Household appointment overview

The private command brief places a compact sale-preparation card above two
appointment panels: current-month records and upcoming records/status. The
lee-appointments.js presenter uses the existing authorized operations schedule,
preserves source confirmation and tentative states, and surfaces correction
notes. Historical entries remain labeled as past; date passage never confirms
attendance or cancellation. Existing communication and household work cards
remain below the appointment overview. Panels stack on narrow screens.

The sale-preparation progress bar groups saved appointments into one node per
calendar day within its displayed date range. Teal marks days whose entries are
all recorded complete; gold marks other scheduled days. Date controls expand
the saved appointment titles, times, and statuses, including on phones. Cancelled
entries and planning placeholders are excluded. Nodes use the authorized
operations schedule and refresh with it; time passing never marks a visit done.

BlueBubbles local scheduling automation

scripts/appointment_eta_monitor.py consumes the existing BlueBubbles bridge's
private incoming_messages ledger. sol37-bluebubbles-automation.timer polls
every 20 seconds after the prior pass finishes. Only new messages trigger the
local fast model (Gemma 3 4B through loopback LiteLLM); no cloud fallback is used.
The existing explicit-reply and notification paths are unchanged.

The model emits a JSON function-call envelope. The dispatcher allows only
update_appointment_eta for a uniquely matched existing appointment, or
request_schedule_review for an ambiguous scheduling message. Sender matching
uses exact normalized contact phone/email plus schedule contactIds,
contactLinks, or an explicitly linked workItemId; private configuration can
also bind a verified sender to a schedule ID. No name-based guessing is used.
Multiple matching appointments and future-date changes require review.
Explicit source quotes, independently checked numeric timing, same-day bounds,
source ordering, optimistic revisions, idempotent retries, and readback protect
automatic updates. Travel duration while still with another client stays unknown.
The Worker now enforces the revision comparison inside the database write, so
two writers starting from the same revision cannot silently overwrite one another.
The model classifies intent; numeric duration arithmetic is derived from the
source text, not a generated UTC/local clock conversion. Acknowledgement-only
messages are a no-op. A model timeout or invalid output does not invent an ETA.
Arrival is sender-reported; completion, employment, payment, and outgoing
messages are outside this dispatcher. Set an appointment's autoEta.enabled
to false to disable its updates, or disable the timer globally.

Private config: ~/.config/sol37-bluebubbles-automation.json. Evidence, function
calls, deduplication, and health: ~/.local/state/sol37/bluebubbles-automation.json.
The dashboard reports health and cumulative review tasks; a stale heartbeat is
shown as needing a check. Tests: python3 -m unittest test_appointment_eta_monitor.py
from scripts/. Live model replay and isolated dispatcher tests must not insert
synthetic appointment changes into the real household schedule.

Applied updates and review requests enter a separate durable private-push
outbox. Stable source-derived tags allow receipt reconciliation; interrupted
acceptance is not blindly resent. Unified Clock owns the private Master Log
deep link and delivery history. queued means accepted for delivery; the Clock
sent activity reports push-provider delivery, not that a person read it.
No-update acknowledgements do not generate update pushes.

The runtime backup includes the automation config, scripts, user units, local
BlueBubbles ledger and automation evidence state. Broader mail archives and
the Mac's Messages database have separate source/backup boundaries; this worker
does not copy their entire contents into the public dashboard.

Requested email follow-up

scripts/nicole_document_watch.py watches the locally synchronized Solmail index
for the explicitly requested consultant follow-up after its source promise.
sol37-nicole-document-watch.timer checks once per minute; the existing mail
watcher owns IMAP synchronization. Identity matching requires the organization
name or the named sender plus consultation context. A candidate must also
mention documents, a proposal, agreement, estimate, invoice or scope of work,
or include a supported document filename; scheduling acknowledgements alone
do not satisfy this filter. Receipt creates private
review work and updates the existing consultation row, then sends an extractive
summary to the verified owner self-chat through BlueBubbles. It does not accept
terms or make bookings. Text/PDF/DOCX attachment excerpts are bounded; unreadable
attachments remain explicitly unreviewed. Source records and delivery state live
in ~/.local/state/sol37/nicole-document-watch.json, covered by runtime backup.
An interrupted text send is reconciled against Messages rather than resent.
Disable the timer or set state disabled: true to stop this requested watch.

The watch uses source excerpts rather than model-generated contract conclusions.
PDF extraction is limited to the first four pages; scanned images, unsupported
formats and unreadable attachments require manual review. The text summary is
therefore not a complete document review. Email receipt and recorded consultation
completion do not establish paid labor, acceptance of terms, or an actual visit
duration. Private household status belongs in the authenticated schedule and
work items, not this public README.

Verification: python3 -m unittest discover -s scripts -p
test_nicole_document_watch.py
covers identity/noise matching, interrupted-send
handling and delivery reconciliation. Inspect checkedAt, status, error,
and per-message dashUpdated / textStatus in the private state file. A healthy
empty pass means no matching email was found, not that the documents were sent.
The runtime backup's unit allowlist explicitly includes both this service/timer
and the BlueBubbles ETA monitor service/timer, in addition to scripts and state.

Sale-prep labor planning

Attention presentation now uses a brief full-screen flash paired with the chime,
with a ten-second cooldown and quiet-qualified rearming. The separate attention
popup is disabled. attention-timing.json keeps the QR quiet timer independently
at 600 seconds; continued presence does not repeatedly play audio. Record Player
encodes flash and sound on the same frame; physical audibility remains a separate
verification. See ../docs/sale-prep-transition.md for current settings.

The private sale-prep API includes src/sale-prep-staffing.json. Its candidate
scope, proposed sprint and comparison rates are returned only to Owner/Operator.
The browser's sale-prep-staffing.js and shared sale-prep-labor-model.js render
scenario person-hours separately from the calendar countdown. Candidate status
never grants employment, time-clock permissions or a booking.

Current dated tasks can record an owner-reported start time and daily completion
percentage. The banner labels that percentage as progress on today's focus and
continues to report overall verified sale readiness separately. A dated
staffingSummary and paymentSummary replace the dense multi-line scenario block
for the active day; full scenarios and evidence remain in Plan & updates.

Owner-only staffing-record writes use the existing revision-bound state and
event transaction. Each record requires worker, role, date, assigned hours,
rate, status, completed hours and dated evidence. Proposed/cancelled records do
not count as confirmed labor; future work cannot be completed. Partial work
remains confirmed until fully completed. Costs use individual recorded rates.
These planning/work estimates are not payroll or payment evidence.

The current planning placeholder is three proposed organizer working days, not
72 literal person-hours. Nicole's September 15 inline SOW proposes $825–$875
per working day including travel, so the three-day planning range is
$2,475–$2,625 before supplies, disposal and third-party services that require
separate approval. A working-day length and exact dates are not stated, so the
dashboard does not infer person-hours or an hourly rate. Confirmed/completed
labor remains driven only by recorded assignments. The SOW's September 17 start
date and client surname await correction; DocuSign and acceptance are pending.
Earlier 48-, 72- and 90-hour scenarios remain historical context. Unknown
walkthrough billing is excluded.
Phone layouts scroll vertically to retain readable text and form access.

scripts/reconcile_nest_appointments.py is an explicit one-shot reconciliation
of the private canonical schedule into a local mode-0600 Nest snapshot. It
preserves the old list and unmatched dates for review, along with source
statuses. orb_appointments.py reads that snapshot without adding API polling.
Snapshot dates are visible; stale/missing data must not imply cancellation.

The confirmation/date-audit Codex heartbeat is saved for 00:05 and 12:05
America/Chicago. sale_prep_reactions.py also enforces one automatic attempt per
half-day, reserving its window before network calls and retaining ambiguous-send
reconciliation. Failed attempts are not aggressively retried. This limit does
not alter display frame processing, motion behavior or existing operational feeds.

Validation: npm test, node scripts/verify_sale_prep_staffing.cjs,
node scripts/audit_relative_dates.cjs, node scripts/verify_presence_cue.cjs,
and Python tests for confirmation cadence, attention timing and presence.

Owner broadcast shortcuts: Broadcast audio is available in the toolbar and Command brief events card. It opens device selection directly. The desktop household pane scrolls the full brief, including the sale-preparation banner; the recap dialog also scrolls independently on shorter screens.

Exact Owner paths:

The direct Broadcast buttons open the dialog and discover receivers automatically. Choose Staff-visible recap or Private owner recap, select individual receivers or use Select all available Cast devices, Select audio-only, or Clear selection, then press Broadcast selected recap. Play recap / Play with Early plays in the current browser. Broadcast controls and their API are Owner-only; Operator and Staff Guest sessions do not gain them. The separately labeled Home Health Visit · broadcast controls manage that temporary display workflow, not recap audio.

The Command brief starts with the sale-preparation headline, timeline, and today's priority, followed by appointments, operating details, and recap controls in one scrolling area. Its layout has exactly two rows: section navigation and content. Selecting a section returns to its top; sale-preparation details stay within Command brief. On phones the document scrolls normally. scripts/verify_command_brief_scroll.cjs verifies wheel scrolling, content order, reachable recap controls, and section navigation at phone, tablet, and desktop sizes.

The enabled home-health layout also renders the saved Owner brief and next action
above its appointment panels. It must not hide the current sale decisions behind
the home-health presentation. The Owner brief, staff-safe brief, and dated clearing
priority remain separate; verification compares the visible Owner brief with its
saved record instead of requiring all three headlines to be identical.

The sale-preparation banner's Today's priority comes from the dated tasks in src/sale-prep-config.json merged with saved state.planOverrides, independently of the Owner brief and staff brief. Use the Owner-only plan-task API for supported fields. Updating one surface does not update the others. Reconcile the affected priority records and check the actual rendered wording; the regression script also verifies the corrected dated banner. For a TV discrepancy, verify a fresh frame from its stream in addition to the browser. See ../docs/sale-prep-transition.md for source mapping and stream capture details.

The priority regression check resolves the current Chicago date and requires the dated plan, derived banner priority, Owner brief, staff brief, staff-focus schedule row, and rendered text to agree. After restarting the fleet, verify receiver playback with advancing direct Cast time when an early fleet ledger still says buffering; a stale Home Assistant entity is not authoritative for current Cast playback.

Stop latest broadcast stops the latest active recap job shown by device discovery, or the broadcast just started here. It only stops receivers still playing that job's audio; unrelated replacement media is left alone. Managed dashboard screens resume after the reservation is released.

Recap refresh compares the spoken content, calendar day, scope, voice and encoding, so revision-only changes reuse ready audio. The exact generation time stays in metadata rather than forcing a new spoken timestamp. New recordings are validated before replacing ready files and encoded as 64 kbps MP3. Opening the recap preloads authenticated audio without playing it; simultaneous Play clicks share the same download. The browser download remains private and fully buffered for Safari compatibility; Cast receivers use the existing range-capable stream. Expand Read the recap transcript to read the full text. A failed refresh retains the previous ready recording.

Household TV event markers include explicitly authorized anonymous date-only private-event dots (owner/operator display presentation only). Guest/QR views do not receive these dates. Milestone and appointment dots share a date-positioned row; private titles, times, locations and clinical details remain excluded.

The transition header presents its sanitized subtitle and next-action line in a full-width Latest Update panel. It precedes countdowns on narrow screens and precedes the large title on short landscape screens. Responsive verification covers 16:9, 4:3, 5:4, ultrawide and portrait layouts; the complete update must fit above the fold without clipping. Display rendering remains distinct from physical readability confirmation.

SOL Ledger workspace (2026-10-21)

Owner, Operator and Household Coordinator sessions can open SOL Ledger in the
Operations navigation. Existing Services Desk tokens and QR sign-in establish
that dashboard session; no second account or broader guest permission is added.
Staff Guest and preview views do not show this management entry. The embedded
app at https://ledger.system42.one/ keeps encrypted browser vaults on its own
origin. No Services Desk bearer token is forwarded to it. Unlocking a vault is
separate from account authentication and from signing a contract. An existing
desktop session does not expose private files from the workstation or another
participant's vault. The independent verifier remains account-free.

On the primary workstation, Services Desk Access includes Open SOL Ledger
and Ledger evidence and recovery. Existing Authentication Cards and access
status/revocation remain the management controls. The launcher retains local
Linux-password verification. Its temporary browser handoff is mode 0600 under
/run/user and deletes itself after two minutes; raw tokens are not process
arguments. No new access credentials were issued during this integration.