Commit Graph

506 Commits

Author SHA1 Message Date
72116ed443 Session log: Cascades MHS kiosk fix + SDM bootstrap (mid-flight) + Sombra onboarding side-quest 2026-04-30 19:08:03 -07:00
87789ed9bb Clarified: Billing works, but time tracking bypassed on 31 tickets
Updated Howard's note with correct analysis after Mike's clarification:

BUSINESS RULE (from Mike):
- ALL tickets need time entries (except cancelled)
- Even warranty/free work logs time
- Time tracking separate from billing decisions

FINDINGS:
- Billing:  Working (29 invoices exist, 2 correctly non-billed)
- Time tracking:  Bypassed (all 31 show 00:00:00)

ROOT CAUSE:
- Manual invoice line items used instead of time tracking
- Hours typed in descriptions ("Applied X.0 Prepay Hours")
- Prevents productivity/utilization reporting

Pattern: 20 prepay deductions + 16 direct charges, all via manual
line items. Workflow skips Syncro time tracking system entirely.

Examples included with hours that should have been logged.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-30 18:47:24 -07:00
006eff35d5 docs: Syncro invoice verification pattern (lesson from false alarm)
Created memory entry documenting correct way to verify ticket-invoice linkage
in Syncro API after 2026-04-30 incident where faulty verification script
falsely claimed 31 tickets had no invoices (actually 29 had invoices properly,
2 were correctly Non-Billable).

Key lessons:
- List endpoint does NOT return ticket_id or line_items
- Must query individual invoices for full data
- Invoice numbers are strings, not integers
- Use ticket ID (internal), not ticket number (user-visible)

Added to memory index for future GrepAI semantic search.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-30 18:44:12 -07:00
e000b8c3e8 CORRECTION: Billing analysis was wrong - all 31 tickets properly handled
Previous commits falsely claimed 31 tickets had no invoices. This was based on
a fundamentally flawed verification script that:
- Used list endpoint instead of individual invoice details
- Failed to check invoice-level ticket_id field
- Had type comparison errors (string vs int)

CORRECTED FACTS:
- 29 out of 31 tickets DO have proper invoices (93.5% success)
- 2 tickets correctly have no invoices (marked Non-Billable)
- #32083 (DAnaise.com): Non-Billable status
- #32022 (Michael Johnson): Cancelled, Non-Billable

NO ACTION REQUIRED - Howard's billing workflow is working correctly.

Sincere apologies for the false alarm. Mike caught the error immediately.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-30 18:41:37 -07:00
45ca852a1f Root cause analysis: Syncro workflow issue, NOT Claude integration
Pattern analysis reveals:
- 31 tickets span March 3 - April 28 (not one-time event)
- Multiple update date clusters (batch processing pattern)
- All missing normal invoice workflow steps
- Tickets changed to 'Invoiced' status without:
  * Time entries
  * Invoice generation
  * Workflow comments

NOT a Claude/API integration issue - Claude doesn't change ticket statuses.

Likely causes:
1. Manual bulk status updates to clear queue
2. Misconfigured Syncro automation/workflow
3. Periodic batch status changes

Urgent: Need to review Syncro automation rules and prevent future revenue loss

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-30 18:33:56 -07:00
175592f966 VERIFIED: 31 tickets have no time AND no invoices
Deep verification performed:
- Checked customer invoice records for all 31 tickets
- ZERO invoices found matching these tickets
- Cascades confirmed to have NO contract (11 tickets affected)
- Example: Kittle #32223 marked 'Invoiced' but no invoice exists
- This is genuine lost revenue, not contract-covered work

Estimated impact: 31 billable tickets with no revenue captured

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-30 18:29:55 -07:00
106c655dea CRITICAL: Update Howard note - 31 tickets closed without time entries
Major billing gap identified:
- 39 tickets closed/invoiced today
- 31 have ZERO time logged (00:00:00)
- Many marked 'Invoiced' but sent with no time
- Detailed list provided for review and correction

Sombra RMM enrollment: no billing needed per Mike

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-30 18:25:23 -07:00
7785731c16 Add note for Howard: Sombra Residential billing reminder
- Ticket #32225 exists but has no time logged
- Today's GuruRMM enrollment work is unbilled
- Needs either ticket update or new ticket creation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-30 18:23:14 -07:00
02e690c6bb Add Sombra Residential LLC client + Server2013 docs
- New clients/sombra-residential/CONTEXT.md (server stub, GuruRMM agent, EOL flag)
- credentials.md: pointer to vault for Administrator password
2026-04-30 14:27:30 -07:00
1f23f66404 sync: auto-sync from DESKTOP-0O8A1RL at 2026-04-30 13:53:29
Author: Mike Swanson
Machine: DESKTOP-0O8A1RL
Timestamp: 2026-04-30 13:53:29
2026-04-30 13:53:30 -07:00
c5b64259a5 session log: 2026-04-30 — Tedards/Bardach/Dataforth MSP work + DKIM setup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 11:19:22 -07:00
18e5a467d2 Session log: Cascades CA bypass phased rollout + pilot user + phone re-enroll
Cascades caregiver shared-phone bypass pilot — 2026-04-29 evening into
2026-04-30 early morning continuation.

Major work:
- Adopted phased per-group CA rollout (corrects original tenant-wide §5
  design that would have blocked off-site office users)
- Step A: backfilled admin@ into excludeUsers on all 8 existing Cascades
  CA policies (mirrors sysadmin@ exclusion posture; Option 1 break-glass)
- Outlook + Helpany + LinkRx assigned to Cascades - Shared Phones group
  and added to MHS kiosk app list (final dashboard: 5 caregiver apps)
- Created cloud-only pilot user pilot.test@cascadestucson.com,
  SG-Caregivers-Pilot group, Business Premium license, vault entry
  pushed to Gitea vault repo
- Built 4 CA changes: PATCH legacy all-users-MFA to exclude pilot group,
  CREATE 3 new Report-only policies (block off-network, block
  non-compliant, 8h sign-in frequency) with both admins excluded
- Pilot phone wipe + re-enroll after first attempt stuck; PIN set,
  awaiting MHS to take over launcher and SDM sign-in prompt

6 new project/feedback memories. Resume point at top of new session log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 10:57:28 -07:00
7128b9e57d Session log: cPanel CVE-2026-41940 IOC scan + remediation on IX/WebSvr
Both servers were already patched (11.110.0.97 and 11.134.0.20) via
daily auto-update. IOC scan found 16 flagged sessions across both
plus 4 uncommented SSH keys on IX.

Critical remediation:
- Forensic evidence preserved before any deletion
- 4 uncommented SSH keys removed from IX (server-side backup retained)
- 16 flagged sessions purged across both servers
- Root passwords rotated via chpasswd
- New WHM API tokens created; 3 stale transfer-* tokens revoked
- Vault entries + 1Password Infrastructure items updated

Forensic deep-dive verdict: patch held. All 7 actual CVE exploit
attempts (botnet IPs hitting /json-api/version) returned HTTP 403.
The "multi-line pass" IOC hits on user sessions were false positives.
Unidentified 76.18.103.222 root session traced to routine SSL
maintenance (zero sensitive endpoints touched).

Skill hardening:
- Added MANDATORY service-token directive to .claude/commands/1password.md
  enforcing OP_SERVICE_ACCOUNT_TOKEN from SOPS for all op CLI calls
- Per Mike: memory files alone don't reliably bind agent behavior;
  baking governance into skill content loaded at moment of use.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 07:22:52 -07:00
f20a9628c3 radio: browseable Q&A — /api/qa, /api/audio range streaming, /episode HTML view
Make the radio archive Q&A pairs actually browseable end to end:

- /api/qa list endpoint (year, min_score, exclude_banter, topic_class,
  pagination, sort by air_date or score). Returns the same column shape as
  /api/search Q&A hits.
- /api/audio/{episode_id} streams the MP3 with HTTP Range support so the
  browser <audio> can seek. 206 + Content-Range when ranged, 200 when
  full-file. Returns 404 cleanly when episodes/ tree is absent (Jupiter).
- /episode/{id} HTML transcript view: chronological segments with clickable
  timestamps, Q&A blocks spliced inline (anchor #qa-<id>), intros marked
  inline, right-rail summary. Hash-anchor on load auto-seeks the audio.
- New question_excerpt / answer_excerpt fields on /api/search Q&A hits and
  on /api/qa items: trim leading run-on chatter, take ~300 chars, end on a
  sentence boundary or word boundary with ellipsis.
- Index UI: each Q&A hit now links to /episode/{id}#qa-{qa_id}; new
  "Browse all Q&A" toggle (year selector, sort, append-load 50 per page,
  defaults to min_score=3); FTS snippet replaced with the plain excerpt
  when available.

No new dependencies, no schema changes, no LLM calls. Uses
EPISODES_DIR env (default /data/episodes) — Jupiter compose still only
mounts /data so audio degrades gracefully to 404 there until episodes
are uploaded.
2026-04-30 07:17:48 -07:00
e6d7c293db sync: auto-sync from Mikes-MacBook-Air.local at 2026-04-30 06:24:45
Author: Mike Swanson
Machine: Mikes-MacBook-Air.local
Timestamp: 2026-04-30 06:24:45
2026-04-30 06:24:46 -07:00
6239f9fc3a radio: session log update — index UI exposes classifier filters
Backend min_score/exclude_banter wired through to HTML index. Adds
score badges (1-5 red->green), topic_class pills, dim styling on
banter rows. Live on http://172.16.3.20:8765/. Synced to portable
repo. pscp ENOSPC quirk worked around by plink-stdin streaming.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 06:07:00 -07:00
b9af34fbd8 radio: index UI exposes min_score / exclude_banter + score badges
Adds quality-filter controls to the search UI: a "min score" select
(any/2+/3+/4+/5) and a "hide banter" checkbox. Q/A hits gain a small
color-coded usefulness badge (1-5, red->green) and a topic_class tag
(computer-help, banter, off-topic, promo). Low-score and banter rows
render dimmed by default so they're visible but de-emphasized.

Defaults to "any" + banter visible to preserve existing search habits.
Mike toggles up when he wants quality. URL-encoded params built via
URLSearchParams so empty values don't leak into requests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 05:54:45 -07:00
48c8b311bf radio: session log — portable laptop bundle + /api/db.sqlite deploy
New private Gitea repo `azcomputerguru/radio-archive-portable` for
laptop offline use. Upstream gained /api/db.sqlite for HTTP-only DB
sync (no SSH keys needed). Jupiter container rebuilt + restarted with
the classifier-populated DB; verified end-to-end (200 OK, 60.5 MB,
1,405 classifier rows intact, min_score filter working).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 05:37:01 -07:00
5e3b1a2297 radio: add /api/db.sqlite for offline laptop sync
Streams the read-only archive.db over the same Tailscale-routed port
as the search service. Companion to azcomputerguru/radio-archive-portable
which curl-fetches from this endpoint and runs locally on the laptop.

Disclosure equivalent to /api/search (which already exposes every
transcript), so no auth added. Deployed to Jupiter; verified GET
returns 60 MB SQLite blob with all 1,405 classifier rows intact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:50:50 -07:00
8d4bb16255 radio: session log — Q/A usefulness classifier (Track 1) complete
3.5h run on qwen3:14b processed 1,405/1,407 Q/A pairs (2 failed,
will retry on next invocation). 37% scored 4-5 (useful), 41%
scored 1-2 (banter/promo/off-topic). API filter ready; Jupiter
redeploy pending Mike's manual review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:34:15 -07:00
42688901f9 radio: Q/A usefulness classifier + min_score search filter (Track 1)
Adds an Ollama-based content quality classifier and exposes the
results via the search API. 1,407 existing Q/A pairs were scored
in 3.5h via qwen3:14b (1,405 succeeded, 2 failed).

Distribution: 37% scored 4-5 (useful), 41% scored 1-2 (banter/promo/
off-topic). 43% flagged as banter overall. Default-on filtering at
search time will hide ~half of the noise without losing any real
listener questions.

Files:
- new classify_qa_quality.py: walks qa_pairs, calls Ollama qwen3:14b
  per row, writes usefulness_score/topic_class/is_banter back to DB.
  Idempotent (--rebuild to reprocess), --smoke for sample check, --limit
  for partial runs. Detached run handles 1407 rows in ~3.5h on a 4090.
- server/main.py: /api/search accepts min_score (0-5) and exclude_banter
  query params. NULL scores treat as "include" so unprocessed rows still
  appear. Episode detail endpoint includes the new fields in qa results.

Schema migration in import_to_sqlite.py was made by the same agent run
(visible on the live archive.db: usefulness_score / topic_class /
is_banter columns now exist on qa_pairs).

Local archive.db updated; Jupiter container has NOT been redeployed
yet — that is a separate manual step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:32:41 -07:00
447b90e092 Session log: Cascades audit retention design + Pro-Tech Services email investigation
Cascades:
- Approved Howard's corrected 4-policy CA bypass design
- Caught + fixed policy 3 GDAP bug (Service provider users exclusion)
- Decided hybrid LAW + Storage Account audit retention (ACG-billed,
  reuse existing Trusted Signing Azure subscription, westus2)
- Wrote full audit retention runbook for Howard
- Reshaped break-glass to two accounts (split-storage YubiKeys)
- Documented Cascades M365 admin model (admin@/sysadmin@ Connect-excluded
  by design; local AD Administrator separate identity layer)
- Decided Howard gets Owner on ACG sub with guardrails (resource lock +
  cost alert) instead of per-RG Contributor

Pro-Tech Services:
- DNS recon of pro-techhelps.com + pro-techservices.co
- Diagnosed calendar invite delivery issue (DKIM domain mismatch +
  no DMARC = strict receivers silently drop invites)
- Drafted non-technical IT-provider migration email to Michelle Sora

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:05:41 -07:00
6b63c154d2 sync: auto-sync from GURU-BEAST-ROG at 2026-04-29 13:29:17
Author: Mike Swanson
Machine: GURU-BEAST-ROG
Timestamp: 2026-04-29 13:29:17
2026-04-29 13:29:19 -07:00
808d45dda1 Session log: 2026-04-29 Cascades close-out update
Append /save close-out timestamp + commit reference to today's bypass-pilot
Phase B buildout log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 11:05:41 -07:00
e00d4ebeb2 sync: auto-sync from DESKTOP-0O8A1RL at 2026-04-29 09:18:32
Author: Mike Swanson
Machine: DESKTOP-0O8A1RL
Timestamp: 2026-04-29 09:18:32
2026-04-29 09:18:33 -07:00
fd933b68c3 remediation-tool: flag PIM role_assigned gap for Howard
role_assigned() only checks direct/permanent roleAssignments.
PIM-managed assignments are in roleAssignmentSchedules and won't
be found, producing noisy (non-blocking) output on re-runs against
tenants with PIM-assigned roles (e.g. Cascades).

TODO comment added at the helper — Howard to implement the fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 09:11:40 -07:00
d62a14ca4e scc: pavon owncloud diagnostic scratch scripts from 2026-04-29 session
Six small bash scripts uploaded to /tmp on 172.16.3.22 during the
OwnCloud cron stacking incident — investigation, group enumeration,
failed group-restrict attempt, occ subcommand discovery. Captured for
audit; full context in clients/pavon/session-logs/2026-04-29-session.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:25:11 -07:00
f22d33f2ae pavon: session log — OwnCloud VM cron stacking diagnosed and stabilized
Found 75-126 stale `occ system:cron` processes on 172.16.3.22 piling up
since 2026-04-27 due to bad oc_filecache LIKE query against pavon's 257K
camera files. Killed stale procs (load 80 -> 5), wrapped apache crontab
with `flock -n /tmp/oc-cron.lock` to prevent restacking. Per-user
versioning disable rejected by OwnCloud Community (`files_versions`
can't be enabled for groups); workaround `occ versions:cleanup pavon`
identified and deferred. Migration/retention cron deferred per user.
NVR architecture clarified: GeoVision NVRs sync via OC Desktop client
with virtual file placeholders; no direct SMB access to Jupiter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:51:21 -07:00
a2f38c1038 cascades: CA unblock + Phase B buildout + onboard-tenant.sh CA Admin backfill
Day-long session unblocking the Cascades CA reconciliation that was paused on
the Tenant Admin SP directory-role gap. Discovered Microsoft also tightened
the OAuth scope for /identity/conditionalAccess/* reads (Policy.Read.All now
required, Policy.ReadWrite.ConditionalAccess no longer accepted for reads).
Patched Tenant Admin manifest accordingly and re-consented in Cascades.

Phase B Intune state turned out to be far more built than the 4/20 log
suggested -- compliance policy, Wi-Fi, device restrictions, both SDM app
configs (Authenticator + Teams), and 7 of 8 apps were already deployed and
assigned. PATCHed device restrictions to block camera/Bluetooth/roaming
and enabled Managed Home Screen multi-app kiosk (ALIS + Teams visible,
10-min auto-signout). PATCHed Cascades named location to add primary WAN
(184.191.143.62/32). Howard added Outlook from Managed Play; SMB encryption
enabled on \CS-SERVER\homes.

CA bypass design corrected -- original §5 plan in user-account-rollout-plan.md
called for "block off-site + MFA on-site" which doesn't match the actual goal
of bypass when network + device assurance present. Reshaped to three policies
that produce on-site-compliant = password only, anything else = MFA or block.

onboard-tenant.sh patched to:
  1. Backfill Policy.Read.All on Tenant Admin SP if missing (idempotent --
     for tenants consented before the 2026-04-29 manifest update).
  2. Assign Conditional Access Administrator directory role to Tenant Admin
     SP at onboard time. Mirrors the Exchange Operator fix Mike landed in
     16f95e8.

Validated with --dry-run against Cascades. Customer-facing tenants already
onboarded should be re-run with this script to backfill both items.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:32:23 -07:00
a1209b28bb GuruRMM submodule: update with UI_GAPS reference in roadmap
Added cross-reference from FEATURE_ROADMAP.md to UI_GAPS.md tracking document.

Clarifies that features may be backend-complete but UI-incomplete.

Submodule commit: f76051a

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-29 07:27:14 -07:00
394062811c GuruRMM submodule: update to include UI gaps tracking document
Added comprehensive UI_GAPS.md for sprint planning and progress tracking.

Documents 6 major UI gaps (P1-P2):
- Policies dashboard (critical - config mechanism)
- Temperature collection (BUG-001 fix)
- Enrollment management
- Tunnel sessions
- Install reporting
- Organizations management

Each gap includes status, missing components, effort estimates, dependencies.

Submodule commit: a018e7e

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-29 07:26:32 -07:00
8360ea1253 sync: auto-sync from HOWARD-HOME at 2026-04-29 07:19:46
Author: Howard Enos
Machine: HOWARD-HOME
Timestamp: 2026-04-29 07:19:46
2026-04-29 07:19:49 -07:00
7d5c332525 memory: GuruRMM holistic development principles
Documented two fundamental GuruRMM development principles:

1. Holistic Feature Development (MANDATORY):
   - Every feature requires complete stack: backend, API, UI/UX, docs
   - Features without management interfaces are incomplete
   - Design for scalability and future expansion
   - Example workflows included

2. AI-Optional Operation:
   - Product must work without AI agents (Claude, autonomous tools)
   - AI features are enhancements, not requirements
   - Core operations remain deterministic and reliable

Principles documented in guru-rmm/docs/DESIGN.md and now in memory for
cross-session reference.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-29 07:17:11 -07:00
01e949db08 GuruRMM submodule: update to include holistic development principles
Updated DESIGN.md with two fundamental principles:
1. Holistic Feature Development - every feature needs full stack (backend, API, UI, docs)
2. AI-Optional Operation - product works without AI agents; AI features are enhancements

Submodule commit: e490307

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-29 07:16:29 -07:00
ba756b49dd GuruRMM submodule: update to include network discovery + collection nodes
Updated GuruRMM roadmap with two major features:
- Network Discovery Node (P2): site-level device discovery and mapping
- Local Collection Node (P2): reduce WAN traffic by local aggregation

Submodule commit: db7d074

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-29 07:07:26 -07:00
dbf4325c46 session log: add note for Howard - Cascades CA fix approved, new approval workflow
Howard is cleared to proceed with Path A (Graph API role assignment) for
Cascades CA Administrator fix.

Also communicated new approval workflow:
- General tools: Howard can modify OR Claude can execute with Howard/Mike approval
- Projects: require Mike approval, features→roadmap, bugs→bug list

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-29 06:54:42 -07:00
f74463d014 memory: approval workflow for tools vs projects
Tools (remediation-tool, onboard scripts, MSP utilities):
- Howard can modify directly
- Claude can execute with Howard OR Mike approval
- No roadmap process, immediate operational changes

Projects (GuruRMM, ClaudeTools API, etc.):
- Require Mike approval
- Features go to roadmap
- Bugs go to bug list

Established during Cascades CA role gap fix discussion.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-29 06:51:39 -07:00
b83c024ad2 imc: Manda laptop provision (DESKTOP-KRHQ5TS) + ServerIMC phantom-DC confirmed
- New laptop provisioned onsite at IMC Speedway: joined to imc.local, AD
  account created for Manda (incoming GM), Outlook bound to her M365
  mailbox, Office activated via retail key, AIMsi USER#=4 per Leslie.
- Syncro ticket #32218 invoiced — 1.5 hrs Onsite Business labor debited
  from IMC's prepay block (14.0 -> 12.5 hrs).
- ServerIMC (192.168.0.63) confirmed as a real authentication-degrading
  phantom DC: SRV/A records claim it's a DC; LDAP/Kerberos refuse
  connections. Promoted from "unclear, worth verifying" (2026-04-13) to
  confirmed AD hygiene issue. Was the root cause of the 2026-04-22 remote
  domain-join failure. Needs follow-up ticket: repair or ntdsutil cleanup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:10:29 -07:00
00fa539e4f cascades save: AD-side pilot prep done; CA reconciliation blocked on SP role gap (2026-04-28)
Thread 1 (AD-side prep on CS-SERVER) completed:
- howard.enos password reset to memorable value (PHS will sync to M365 once staging exits)
- proxyAddresses=SMTP:howard.enos@cascadestucson.com added (G1 convention)

Thread 2 (CA reconciliation) blocked: ComputerGuru - Tenant Admin SP
(appId 709e6eed-...) has zero directory role assignments in Cascades.
Graph CA endpoints 403 despite Policy.ReadWrite.ConditionalAccess on token.

Decision pending: Path A (Graph-side role assignment via existing
RoleManagement.ReadWrite.Directory) vs Path B (portal click as admin@).
Target role: Conditional Access Administrator
(b1be1c3e-b65d-4f19-8427-f6fa0d97feb9) on SP objectId
a5fa89a9-b735-4e10-b664-f042e265d137.

Follow-up: extend onboard-tenant.sh to assign this role at onboard time
(parallels 16f95e8 Exchange Admin fix for Exchange Operator SP).

Pilot target slipped 2026-04-27 to 2026-04-28. ALIS App Store still
inaccessible — install-side of ALIS SSO still deferred regardless.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 07:19:11 -07:00
519278a664 radio: session log update — Jupiter container live at 172.16.3.20:8765
Append to 2026-04-28-session.md covering the FastAPI/SQLite container
deploy: build + ship + verify, plus credentials, paths, and re-deploy
procedures for both DB updates and source updates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 06:05:34 -07:00
71ada136a8 radio: FastAPI/SQLite query server, deployed to Jupiter
Read-only HTTP layer over archive.db. Endpoints: /api/stats,
/api/episodes, /api/episodes/{id}, /api/episodes/{id}/transcript,
/api/search (FTS5 over segments + qa_pairs, bm25-ranked, snippets),
/api/callers. Single-file HTML index with debounced search UI.

Deployed: Jupiter (Unraid Docker), bound to 172.16.3.20:8765, LAN only.
Container path: /mnt/user/appdata/radio-archive/{app,data}.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 06:00:22 -07:00
b3da922901 Session log: Mac sync and review session (2026-04-28)
Synced with Gitea, reviewed 14 commits from GURU-BEAST-ROG:
- Radio show audio processing (Tara voice profile, Q&A extraction, 4090 benchmark)
- Cascades client work (Howard - HIPAA remediation, Entra Connect staging)
- Valleywide client init (app modernization project)

Note detected: Co-host name 'Tom' needs correction in radio show profiles.

Session type: Sync and context review only, no active development.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-28 05:38:49 -07:00
90b1ffff8b radio: session log — full archive imported (572 ep / 482.7h / 57.7 MB DB)
Execution-only follow-on to 2026-04-27. Both batch passes done (519+53,
0 errors), import_to_sqlite.py run incrementally to bring archive.db
to final state. Next step: Jupiter Docker container deploy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 05:30:08 -07:00
72b7996be4 session log: 2026-04-27 general — SharePoint version history Q&A
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 19:42:14 -07:00
82940d96d7 radio: utf-8 transcript writes + sqlite archive importer + session log
- src/transcriber.py: open transcript.{json,txt,srt} with encoding="utf-8".
  Windows cp1252 default crashed on Whisper output containing U+2044.
- import_to_sqlite.py: new. Walks archive-data/transcripts, builds
  archive.db (5 tables + 2 FTS5 virtual tables, sha256-keyed idempotency).
  20.5 MB / 208 episodes at smoke-test time, 1.9s rebuild.
- batch_process.py: tracked from prior session — full-archive batch with
  resumable transcribe/diarize/intros/qa pipeline.
- .gitignore: archive-data/ and logs/.

Session log: 2026-04-27-archive-batch-and-sqlite-import.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:38:02 -07:00
488bf5849e radio: attach caller names to Q&A pairs from transcript intros
QAPair gets caller_name and caller_role fields populated by a new
attach_caller_names(pairs, transcript_segments) helper. For each pair,
finds the active opening intro at the question_start time (8s forward
tolerance, no backward limit — a caller's call can run for 10+ minutes
and the intro happens once at the start) and attaches the speaker name.

Validation on 9-episode test set:
  19/19 Q&A pairs (100%) now have caller names attached.

Examples of corrections from oracle attribution:
  2018-s10e18 @ 73:36  Christopher (was misattributed to "Tara")
  2015-s7e19 @ 35:45   William     (was misattributed to "Tara")
  2010-05-08-hr1       Jackie x3, Bruce
  2012-03-10-hr1       Adam x2
  2016-s8e43           John, Doug
  2017-s9e30           Tom, Denise x3, Charlie

speaker_oracle.py: adds speaker_at(time, intros) helper used both by the
existing resolve_speakers() and the new caller-name attachment. Also
adds the "let's fit/bring/put X in/on" intro pattern variant (caught
Charlie at 70:21 in 2017-s9e30 that "talk to X" missed).

download_full_archive.py: SSH keepalive every 30s + per-file retry-on-
failure (up to 3 attempts with reconnect). Earlier run hung on a dead
connection at file 109 of 589 with no recovery; restarted run is now
running at ~10 MB/s vs ~2-3 MB/s before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:55:31 -07:00
1b574caba4 radio: transcript-driven speaker name resolution (oracle)
New module src/speaker_oracle.py extracts speaker introductions from
transcripts ("let's talk to William", "we have Clay from the Nerd Junkies",
"in Tara's place, we have Clay", "thanks for the call <name>") and binds
them to non-HOST diarization turns. Pure post-pass on diarization JSONs,
no audio processing — corrects audio-only cosine errors using Mike's
deterministic on-air announcements.

Algorithm:
- Extract intros: regex patterns for caller pickups, guest intros,
  fill-in announcements, caller closes. Case-strict (rejects mid-sentence
  lowercase matches), with a blacklist of common false-positive words.
  Deduplicates same-name intros within 5s.
- Resolve speakers: for each non-HOST turn, find the LATEST opening intro
  at or before turn.start (with 8s forward tolerance for boundary slop).
  Later intros implicitly close earlier callers, so the most recent
  intro wins. No artificial lookback limit (callers can talk for 10+ min).
- Falls back to caller_close patterns within 30s after a turn ends.

Validation on 9-episode test set:
  2018-s10e18: Christopher 190s correctly named (was mislabeled "Tara")
  2012-06-09 : Kay 160s correctly named (was mislabeled "Tara")
  2015-s7e19 : Clay 45s as fillin for Tara, William 40s as caller
  2016-s8e43 : Charles 630s, Bruce 210s, John 205s — most callers named
  2017-s9e30 : Denise 295s, Tom 115s, Elaine 85s, Jeff 10s
  Many other callers across all episodes correctly named.

Remaining unnamed CO-HOST/CALLER (~5-10% of non-HOST time) are real
co-host banter or callers without explicit Mike-introductions.

benchmark.py: adds Phase 2.5 "Name Resolution" between diarization and
Q&A extraction. Prints named-speaker breakdown per episode. Doesn't
modify diarization JSONs (resolution is computed on demand).

Next step: feed named turns into qa_extractor so Q&A pairs get caller
name attached for searchability. Also: bootstrap recurring-speaker
profiles (Tara, Tony, Rob, Randall, producers) by accumulating
intro-tagged windows across the full archive once download completes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:48:16 -07:00
4c89402df8 radio: skip Clay profile build (failed) — accept 2015-s7e19 Q&A as noisy
First attempt at Clay's voice profile from 2015-s7e19 produced
Clay-vs-Mike cosine similarity of 0.994 — essentially a Mike clone.
Root cause: 10s WavLM x-vector chunks averaged Mike's frequent
interjections together with Clay's dialogue, and Mike's well-trained
profile dominated the resulting embedding signal.

Mike's call: skip Clay, accept the 2015-s7e19 Q&A as noisy. Clay rarely
appears in other episodes, so the cost of not having his profile is
bounded to this one episode plus any rare future appearances.

Cleanup:
- voice-profiles/clay/ removed
- voice-profiles/profiles.json: Clay entry removed
- Memory updated to record the decision and the failure mode

Kept build_clay_profile.py in-repo as documentation of the attempt and
the Mike-similarity-filter pattern. Useful starting point if a future
attempt provides cleaner pure-Clay timestamps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:36:46 -07:00
c760e430c0 radio: bumper detection in diarizer + full archive download script
Adds a transcript-driven bumper filter to the diarization pipeline. When
a transcript segment matches qa_extractor's promo/bumper signatures, the
overlapping audio windows are labeled BUMPER and the WavLM cosine match
is skipped. Prevents music/promo from being matched against speaker
profiles (the failure mode Mike caught in 2018-s10e18 @ 09:20-10:05).

Code changes:
- src/voice_profiler.py: identify_speakers() takes optional skip_ranges
  parameter; windows whose midpoint falls in a skip range get labeled
  "[bumper]" and skip cosine match
- src/diarizer.py: diarize() takes optional transcript_path; pre-computes
  bumper time ranges via qa_extractor._is_promo_or_bumper, passes to
  identify_speakers; adds BUMPER speaker label
- benchmark.py: passes transcript_path to diarize()

Aggregate impact across 9-episode test set:
  Tara attribution: 4880s -> 3680s  (-1200s / -25%)
  Q&A pairs: 17 -> 19 (+2)
    (bumper-flagged segments had been disrupting conversation detection
     in 2017-s9e30 and 2018-s10e18)
  CALLER total: 1320s -> 1190s  (bumpers previously labeled CALLER moved)
  Per-episode bumpers caught: 1-8, total ~165 bumper segments across set

Remaining Tara false positives are real callers acoustically similar to
Tara (Christopher in 2018, Kay in 2012, William and Charles in 2015) and
guest Clay in 2015-s7e19 — those need profile rebuild + Clay profile,
not bumper filtering.

Adds download_full_archive.py — resumable mirror-style downloader that
walks IX server's /home/gurushow/public_html/archive/{year}/ and copies
all MP3s to archive-data/episodes/. Run is in progress (~589 files,
~10-15GB). Used to source clean profile windows for the remaining
co-hosts (Tara rebuild, Clay, Tony, Rob, Randall, producers).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:17:50 -07:00
a4f527f31e radio: per-year test set (one episode per year, 2010-2018)
Added 2010, 2015, 2018 test episodes to round out the test set to one
per available year:
- 2010-05-08-hr1 (May 2010, earliest available; pre-Tara era)
- 2015-s7e19 (Jan 2015, avoids training's s7e30)
- 2018-s10e18 (only 3 non-training 2018 episodes exist)

Archive has no 2019 directory — Rob's "2018/2019 appearances" are
constrained to the 5 available 2018 episodes only.

Per-year diarization summary (Tara presence, post-rename):
  2010-05-08    30s   1.2%   likely false positive (pre-Tara)
  2011-03-12   140s   5.6%   likely false positive (call-in only)
  2012-03-10    30s   1.1%   likely false positive (call-in only)
  2012-06-09   340s  12.8%   suspicious — Mike to confirm
  2014-s6e19   680s  23.3%   confirmed
  2015-s7e19   280s   9.9%   plausible — Mike to confirm
  2016-s8e43  1890s  35.5%   confirmed
  2017-s9e30   610s  11.4%   plausible
  2018-s10e18  880s  17.1%   COULD BE ROB — Mike flagged Rob for
                              2018/2019 appearances; cosine threshold may
                              be hitting on Rob being acoustically similar
                              to Tara

Total Tara across 9 episodes: 1h 21m / 8h 52m audio (15.3%).

Q&A counts (still suspect — every voice that isn't Mike-or-Tara is
labeled CALLER, so Randall/Rob/producers inflate the bucket):
  2010=4, 2011=1, 2012a=2, 2012b=0, 2014=0, 2015=1, 2016=2, 2017=4, 2018=3
  Total: 17 pairs across 9 episodes

4090 perf on the expanded set:
- Diarization: 31928s in 121.5s = 262.7x realtime (vs 209.7x on 5070 Ti, +25.3%)
- Transcription (3 new episodes only): 10554s in 112.4s = 93.9x

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 15:20:09 -07:00