From dc5c09b40bc26134fce05a86c5dfad2d7b4ac62d Mon Sep 17 00:00:00 2001 From: Mike Swanson Date: Mon, 15 Jun 2026 09:42:15 -0700 Subject: [PATCH] sync: auto-sync from GURU-5070 at 2026-06-15 09:41:53 Author: Mike Swanson Machine: GURU-5070 Timestamp: 2026-06-15 09:41:53 --- .claude/commands/discord-dm.md | 30 +++++ .claude/commands/mailbox.md | 2 + .claude/memory/MEMORY.md | 2 + .../feedback_dm_wrapped_command_lines.md | 12 ++ .../feedback_dmarc_rua_inky_onboarded_only.md | 12 ++ .claude/scripts/discord-dm.sh | 106 ++++++++++++++++++ .claude/skills/discord-dm/SKILL.md | 80 +++++++++++++ .../remediation-tool/references/gotchas.md | 13 ++- errorlog.md | 2 +- .../acg-website-showcase/multipage/about.html | 3 +- .../multipage/assets/logo/acg-mark.svg | 6 + .../multipage/calculator.html | 3 +- .../multipage/contact.html | 3 +- .../multipage/css/styles.css | 8 +- .../multipage/design/_markcheck.png | Bin 0 -> 17638 bytes .../multipage/design/lg-bold.png | Bin 0 -> 15906 bytes .../multipage/design/lg-paper.png | Bin 0 -> 17750 bytes .../multipage/design/lg-verdigris.png | Bin 0 -> 22684 bytes .../acg-website-showcase/multipage/index.html | 3 +- .../multipage/pricing.html | 3 +- .../multipage/radical.html | 1 + .../multipage/services.html | 3 +- ...06-14-mike-acg-website-multi-skin-build.md | 50 +++++++++ projects/graphifyy-eval/FINDINGS.md | 81 +++++++++++++ projects/graphifyy-eval/PROTOCOL.md | 106 ++++++++++++++++++ projects/graphifyy-eval/results.csv | 21 ++++ 26 files changed, 537 insertions(+), 13 deletions(-) create mode 100644 .claude/commands/discord-dm.md create mode 100644 .claude/memory/feedback_dm_wrapped_command_lines.md create mode 100644 .claude/memory/feedback_dmarc_rua_inky_onboarded_only.md create mode 100644 .claude/scripts/discord-dm.sh create mode 100644 .claude/skills/discord-dm/SKILL.md create mode 100644 projects/acg-website-showcase/multipage/assets/logo/acg-mark.svg create mode 100644 projects/acg-website-showcase/multipage/design/_markcheck.png create mode 100644 projects/acg-website-showcase/multipage/design/lg-bold.png create mode 100644 projects/acg-website-showcase/multipage/design/lg-paper.png create mode 100644 projects/acg-website-showcase/multipage/design/lg-verdigris.png create mode 100644 projects/graphifyy-eval/FINDINGS.md create mode 100644 projects/graphifyy-eval/PROTOCOL.md create mode 100644 projects/graphifyy-eval/results.csv diff --git a/.claude/commands/discord-dm.md b/.claude/commands/discord-dm.md new file mode 100644 index 0000000..3e664cf --- /dev/null +++ b/.claude/commands/discord-dm.md @@ -0,0 +1,30 @@ +--- +name: discord-dm +description: Send a Discord message to an org member's DMs or a team channel via the ClaudeTools bot. Prepopulated with user + channel IDs. Use for copy-paste-friendly delivery of wrapped command lines (consent links, long one-liners) or to ping someone directly. +--- + +# /discord-dm — direct Discord messaging + +Thin entry point to the `discord-dm` skill. Engine: `.claude/scripts/discord-dm.sh`. + +## Usage + +``` +/discord-dm Send a DM (mike|howard|rob|winter) or post to a channel (#bot-alerts|#dev-alerts) +/discord-dm list Show known users + channel IDs +``` + +Examples: + +```bash +bash .claude/scripts/discord-dm.sh mike "https://login.microsoftonline.com/.../adminconsent?client_id=..." +bash .claude/scripts/discord-dm.sh dev "build promoted to stable" +echo "$LONG_LINK" | bash .claude/scripts/discord-dm.sh mike +``` + +## Standing rule + +Any **wrapped / long single-line output** (M365 consent links, long CLI one-liners, +URLs with query strings) should be **DM'd to `mike`** so it's cleanly copy-pasteable +rather than mangled by terminal wrapping. See `.claude/skills/discord-dm/SKILL.md` +for the recipient forms, prepopulated directory, and gotchas. diff --git a/.claude/commands/mailbox.md b/.claude/commands/mailbox.md index beb07bb..48acc5c 100644 --- a/.claude/commands/mailbox.md +++ b/.claude/commands/mailbox.md @@ -2,6 +2,8 @@ Read and send mail for an Arizona Computer Guru mailbox via Microsoft Graph, using the shared **Claude-MSP-Access** app. Defaults to the mailbox of the user running it (from `identity.json`). +> **[BLOCKED 2026-06-14]** The `Claude-MSP-Access` app (`fabb3421`) was **DELETED** from the azcomputerguru.com tenant, so every token request returns **AADSTS700016** and this command cannot read or send until a replacement mail-capable app is provisioned. Decision (2026-06-15): the replacement is the **Exchange Operator** suite tier (`exchange-op`, `b43e7342-5b4b-492f-890f-bb5a4f7f40e9`) once `Mail.Send` (+ optionally `Mail.ReadWrite`/`Contacts`) is added to its manifest and consented — Mail.Send's real use is IR victim-notification during mailbox takeovers, so it lives in the suite. NOT yet provisioned. If a token fails with `AADSTS700016`, this is why — do not retry; surface this note. When provisioned, repoint `client_id` (API Configuration + the `py` helper) to `b43e7342...` and the vault path to `computerguru-exchange-operator.sops.yaml`. See `errorlog.md` and `remediation-tool/references/gotchas.md`. + ## Usage ``` diff --git a/.claude/memory/MEMORY.md b/.claude/memory/MEMORY.md index f76df8f..dd60fce 100644 --- a/.claude/memory/MEMORY.md +++ b/.claude/memory/MEMORY.md @@ -38,6 +38,8 @@ - [Mac RMM authentication fixed](feedback_mac_rmm_auth_fixed.md) — Use `.claude/scripts/rmm-auth.sh` helper instead of heredoc pattern. Heredoc with `--data-binary @-` fails on macOS. Helper uses `jq -n --arg` to build JSON safely. Usage: `eval "$(bash .claude/scripts/rmm-auth.sh)"` sets $TOKEN, $RMM, $REPO_ROOT. Updated in /rmm Phase 0. - [Verify committed state before push](feedback_verify_committed_state_before_push.md) — webhook builds from origin/main: verify the COMMITTED build (git stash + build), not the working tree; bad git-add pathspec silently aborts staging. Stage by directory. - [Scheduling = coord todo, not schedulers](feedback_scheduling_via_coord_todo.md) — Defer future work as a coord todo (POST /api/coord/todos; needs text + created_by_user + created_by_machine) for a later session to pick up. NOT /schedule remote CCR agents (no vault/creds there) or local scheduled tasks. +- [DMARC rua INKY only when onboarded](feedback_dmarc_rua_inky_onboarded_only.md) — Don't point a client's DMARC rua at reports-sg.inkydmarc.com unless that client is onboarded to INKY (most aren't). Use plain `p=none` with no rua otherwise. +- [DM wrapped command lines to Mike](feedback_dm_wrapped_command_lines.md) — Long single-line output (consent links, URLs, one-liners) gets DM'd to Mike via the `discord-dm` skill so it's copy-pasteable, not terminal-wrapped. `discord-dm.sh mike ""`. - [Attribution is read, never inferred](feedback_attribution_from_identity.md) — Who-did-what (user+machine) comes ONLY from identity.json + users.json + git authorship. Never infer from hostname patterns, the userEmail hint, or memory. The "5070" box is Mike's. sync.sh reconciles git config to identity.json; /save renders the User block via whoami-block.sh. - [D2TESTNAS SSH Access](feedback_d2testnas_ssh.md) — Use root@192.168.0.9 with Paper123!@#, not sysadmin. - [Bypass Permissions Setting](feedback_bypass_permissions_setting.md) — Set permissions.defaultMode to bypassPermissions in settings.json on all machines. diff --git a/.claude/memory/feedback_dm_wrapped_command_lines.md b/.claude/memory/feedback_dm_wrapped_command_lines.md new file mode 100644 index 0000000..5e777b6 --- /dev/null +++ b/.claude/memory/feedback_dm_wrapped_command_lines.md @@ -0,0 +1,12 @@ +--- +name: feedback-dm-wrapped-command-lines +description: DM any wrapped/long single-line output (consent links, long one-liners, URLs) to Mike in Discord so it's copy-pasteable +metadata: + type: feedback +--- + +Any wrapped command line or long single-line output meant for the user to copy — M365 admin-consent links, long CLI one-liners, URLs with query strings, enrollment/installer URLs — should be **sent to Mike as a Discord DM**, not left only in the terminal. + +**Why:** Terminal wrapping breaks long single-line items across lines, so copy-paste picks up line breaks/spaces and corrupts the link or command. A Discord DM preserves it as one clean line. (Mike, 2026-06-15.) + +**How to apply:** Use the `discord-dm` skill — `bash .claude/scripts/discord-dm.sh mike ""` (or `echo "$X" | ... mike`). Still show it inline in the response too, but the DM is the canonical copy-paste source. The skill is prepopulated with all org user IDs (mike/howard/rob/winter) and channel IDs (#bot-alerts/#dev-alerts); keep its directory in sync with `.claude/users.json`. Build payloads with `jq -nc --arg` + `printf | curl --data-binary @-` (direct `-d` mangles multiline → Discord 50109). diff --git a/.claude/memory/feedback_dmarc_rua_inky_onboarded_only.md b/.claude/memory/feedback_dmarc_rua_inky_onboarded_only.md new file mode 100644 index 0000000..601d3ef --- /dev/null +++ b/.claude/memory/feedback_dmarc_rua_inky_onboarded_only.md @@ -0,0 +1,12 @@ +--- +name: feedback-dmarc-rua-inky-onboarded-only +description: Only point a client's DMARC rua at INKY (reports-sg.inkydmarc.com) if that client is onboarded to INKY +metadata: + type: feedback +--- + +When adding a DMARC record for a client, do NOT copy ACG's own convention of `rua=mailto:reports@reports-sg.inkydmarc.com` unless that specific client is onboarded to INKY DMARC. azcomputerguru.com uses INKY, but most clients are not on it. + +**Why:** INKY only processes aggregate reports for domains provisioned in the INKY account. Pointing an un-onboarded client's `rua` there sends reports to an aggregator that ignores them — no monitoring value, just misdirected traffic. (Mike, 2026-06-15, CryoWeave.) + +**How to apply:** For a client not on INKY, use `v=DMARC1; p=none;` with no `rua` (valid policy, improves deliverability posture, no report destination), or a same-domain mailbox if they want reports. Reserve the INKY rua for INKY-onboarded domains. See [[reference_ix_server_access]] for the DNS host (ns1/ns2.acghosting.com = cPanel on IX). diff --git a/.claude/scripts/discord-dm.sh b/.claude/scripts/discord-dm.sh new file mode 100644 index 0000000..aa38181 --- /dev/null +++ b/.claude/scripts/discord-dm.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# discord-dm.sh — send a Discord message to an org member (DM) or a team channel, +# using the ClaudeTools bot. Built for copy-paste-friendly delivery of wrapped +# command lines (consent links, long one-liners) straight to a person's DMs. +# +# Usage: +# discord-dm.sh "message text" +# echo "message text" | discord-dm.sh +# discord-dm.sh list # show known users + channels +# +# is one of: +# - a user name: mike | howard | rob | winter -> opens a DM +# - a channel: #bot-alerts | #dev-alerts | bot | dev -> posts to the channel +# - a raw snowflake: 17-19 digit ID (treated as a USER DM; prefix chan: for a channel) +# +# Token resolution (mirrors post-bot-alert.sh, first hit wins): +# 1. SOPS vault: projects/discord-bot/bot-token.sops.yaml field credentials.bot_token +# 2. projects/discord-bot/.env key DISCORD_TOKEN +# +# Exit codes: 0 success; 1 usage/arg error; 2 token missing; 3 Discord API error. +# Unlike post-bot-alert.sh this does NOT soft-fail — a DM is usually load-bearing +# (you asked for it on purpose), so a failure is surfaced with a non-zero exit. + +set -u + +ROOT="${CLAUDETOOLS_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" +API="https://discord.com/api/v10" +UA="ClaudeToolsBot (claudetools, 1.0)" + +# --- prepopulated org directory (from .claude/users.json + post-bot-alert.sh) --- +declare -A USERS=( + [mike]="264814939619721216" + [howard]="624667664501178379" + [rob]="261978810713505792" + [winter]="624666486362996755" +) +declare -A CHANNELS=( + [bot-alerts]="624710699771232265" # whole team (Syncro + general) + [dev-alerts]="1509998508198068484" # private RMM/Dev (Howard + Mike) +) + +print_dir() { + echo "Users (DM):" + for u in "${!USERS[@]}"; do printf " %-8s %s\n" "$u" "${USERS[$u]}"; done + echo "Channels:" + for c in "${!CHANNELS[@]}"; do printf " #%-12s %s\n" "$c" "${CHANNELS[$c]}"; done +} + +RECIPIENT="${1:-}" +if [ -z "$RECIPIENT" ] || [ "$RECIPIENT" = "-h" ] || [ "$RECIPIENT" = "--help" ]; then + sed -n '2,30p' "$0"; exit 1 +fi +if [ "$RECIPIENT" = "list" ]; then print_dir; exit 0; fi +shift + +# --- message (remaining args, else stdin) --- +if [ "$#" -gt 0 ]; then MSG="$*"; elif [ ! -t 0 ]; then MSG="$(cat)"; else MSG=""; fi +if [ -z "$MSG" ]; then echo "[ERROR] empty message — nothing sent" >&2; exit 1; fi + +# --- resolve recipient -> mode (dm|channel) + target id --- +MODE=""; TARGET=""; LABEL="" +key="$(printf '%s' "$RECIPIENT" | tr '[:upper:]' '[:lower:]')" +case "$key" in + bot|bot-alerts|"#bot-alerts") MODE=channel; TARGET="${CHANNELS[bot-alerts]}"; LABEL="#bot-alerts" ;; + dev|dev-alerts|"#dev-alerts") MODE=channel; TARGET="${CHANNELS[dev-alerts]}"; LABEL="#dev-alerts" ;; + chan:*) MODE=channel; TARGET="${key#chan:}"; LABEL="channel ${TARGET}" ;; + \#*) c="${key#\#}"; if [ -n "${CHANNELS[$c]:-}" ]; then MODE=channel; TARGET="${CHANNELS[$c]}"; LABEL="#$c"; else echo "[ERROR] unknown channel: $RECIPIENT (try: discord-dm.sh list)" >&2; exit 1; fi ;; + *) + if [ -n "${USERS[$key]:-}" ]; then MODE=dm; TARGET="${USERS[$key]}"; LABEL="$key (DM)" + elif printf '%s' "$key" | grep -qE '^[0-9]{17,19}$'; then MODE=dm; TARGET="$key"; LABEL="user ${key} (DM)" + else echo "[ERROR] unknown recipient: $RECIPIENT (try: discord-dm.sh list)" >&2; exit 1; fi ;; +esac + +# --- bot token: vault first, then .env --- +TOKEN="$(bash "$ROOT/.claude/scripts/vault.sh" get-field \ + projects/discord-bot/bot-token.sops.yaml credentials.bot_token 2>/dev/null)" +if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then + ENV_FILE="$ROOT/projects/discord-bot/.env" + [ -f "$ENV_FILE" ] && TOKEN="$(grep -iE '^[[:space:]]*DISCORD_TOKEN[[:space:]]*=' "$ENV_FILE" | head -1 | sed -E 's/^[^=]*=[[:space:]]*//; s/^["'"'"']//; s/["'"'"'][[:space:]]*$//')" +fi +if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then echo "[ERROR] no bot token (vault + .env both empty)" >&2; exit 2; fi + +auth=(-H "Authorization: Bot ${TOKEN}" -H "Content-Type: application/json" -H "User-Agent: ${UA}") + +# --- for a DM, open (or fetch) the user's DM channel --- +if [ "$MODE" = "dm" ]; then + DM="$(printf '%s' "$(jq -nc --arg r "$TARGET" '{recipient_id:$r}')" | \ + curl -s -m 15 "${auth[@]}" -X POST "$API/users/@me/channels" --data-binary @-)" + CHID="$(printf '%s' "$DM" | jq -r '.id // empty' 2>/dev/null)" + if [ -z "$CHID" ]; then echo "[ERROR] could not open DM channel for $LABEL: $DM" >&2; exit 3; fi + TARGET="$CHID" +fi + +# --- post the message (printf | --data-binary @- — direct -d mangles multiline JSON) --- +RESP="$(printf '%s' "$(jq -nc --arg c "$MSG" '{content:$c}')" | \ + curl -s -m 15 -w $'\n%{http_code}' "${auth[@]}" \ + -X POST "$API/channels/${TARGET}/messages" --data-binary @-)" +HTTP="$(printf '%s' "$RESP" | tail -n1)" +BODY="$(printf '%s' "$RESP" | sed '$d')" + +if [ "$HTTP" = "200" ]; then + echo "[OK] discord-dm: sent to ${LABEL} (message_id=$(printf '%s' "$BODY" | jq -r '.id // empty'))" + exit 0 +fi +echo "[ERROR] discord-dm: Discord returned ${HTTP:-no-response} — ${BODY}" >&2 +exit 3 diff --git a/.claude/skills/discord-dm/SKILL.md b/.claude/skills/discord-dm/SKILL.md new file mode 100644 index 0000000..16c45d6 --- /dev/null +++ b/.claude/skills/discord-dm/SKILL.md @@ -0,0 +1,80 @@ +--- +name: discord-dm +description: > + Send a Discord message to an org member's DMs or to a team channel via the + ClaudeTools bot. Use this whenever you need to hand a person something + copy-paste-friendly that the terminal would wrap or mangle — consent links, + long single-line commands, URLs, tokens-to-rotate notices — or to ping someone + directly. Prepopulated with every org member's user ID and the team channel IDs, + so you address people by name (mike/howard/rob/winter) not raw snowflakes. + Invoke on: "DM me/ in discord", "send a discord message", + "message on discord", "discord DM", "send that link to my discord", + "ping ". For one-line [SYNCRO]/[RMM] status alerts to the alert channels, + prefer post-bot-alert.sh; use this for direct/person-targeted delivery. +--- + +# discord-dm — direct Discord messaging to the org + +Thin wrapper over the Discord bot API. The engine is +`.claude/scripts/discord-dm.sh`; this doc is the usage contract. + +## When to use + +- **Wrapped command lines** — Mike's standing rule: any wrapped/long single-line + output (M365 consent links, long CLI one-liners, URLs with query strings) gets + **DM'd to him in Discord** so it's cleanly copy-pasteable, not mangled by terminal + wrapping. Default target for that is `mike`. +- Pinging a specific teammate with something actionable. +- Posting to a team channel when you want to choose the channel explicitly + (`#bot-alerts` / `#dev-alerts`). + +For routine one-line `[SYNCRO]` / `[RMM]` status alerts, keep using +`post-bot-alert.sh` (it auto-routes by prefix). This skill is for **person-targeted +DMs** and deliberate channel posts. + +## Usage + +```bash +bash .claude/scripts/discord-dm.sh "message text" +echo "message text" | bash .claude/scripts/discord-dm.sh +bash .claude/scripts/discord-dm.sh list # print known users + channels +``` + +``: + +| Form | Effect | +|---|---| +| `mike` `howard` `rob` `winter` | opens/uses that user's DM channel | +| `#bot-alerts` / `bot` | posts to #bot-alerts (whole team) | +| `#dev-alerts` / `dev` | posts to #dev-alerts (Howard + Mike, private) | +| `chan:` | posts to an arbitrary channel by ID | +| `<17-19 digit id>` | treated as a **user** DM | + +## Prepopulated directory (from `.claude/users.json` + `post-bot-alert.sh`) + +| Name | Discord user ID | | Channel | ID | +|---|---|---|---|---| +| mike | 264814939619721216 | | #bot-alerts | 624710699771232265 | +| howard | 624667664501178379 | | #dev-alerts | 1509998508198068484 | +| rob | 261978810713505792 | | | | +| winter | 624666486362996755 | | | | + +Keep this table and the `USERS`/`CHANNELS` maps in the script in sync with +`.claude/users.json` when people are added/removed. + +## Implementation notes / gotchas + +- **Bot token** resolves from the SOPS vault + (`projects/discord-bot/bot-token.sops.yaml` field `credentials.bot_token`), + falling back to `projects/discord-bot/.env` `DISCORD_TOKEN`. Works from any + machine, not just BEAST. +- **DMs** require opening the user's DM channel first + (`POST /users/@me/channels {recipient_id}`) then posting to that channel id. +- **Multiline payloads:** always build JSON with `jq -nc --arg` and feed curl via + `printf '%s' "$payload" | curl ... --data-binary @-`. Passing the JSON directly + with `-d "$(...)"` mangled multiline content and returned Discord error + `50109 invalid JSON body` (hit 2026-06-15 sending the CryoWeave consent link). +- **Not soft-fail:** unlike `post-bot-alert.sh`, a send failure exits non-zero — + a requested DM is load-bearing, so surface the error. +- The bot must share a server with the user and the user must allow DMs from + server members, or the DM-channel open / send can 403. diff --git a/.claude/skills/remediation-tool/references/gotchas.md b/.claude/skills/remediation-tool/references/gotchas.md index 604777d..8b0a1f9 100644 --- a/.claude/skills/remediation-tool/references/gotchas.md +++ b/.claude/skills/remediation-tool/references/gotchas.md @@ -24,7 +24,16 @@ Five multi-tenant apps replace the old single over-permissioned app. Use minimum | `tenant-admin` | ComputerGuru Tenant Admin | `709e6eed-0711-4875-9c44-2d3518c47063` | `computerguru-tenant-admin.sops.yaml` | | `defender` | ComputerGuru Defender Add-on | `dbf8ad1a-54f4-4bb8-8a9e-ea5b9634635b` | `computerguru-defender-addon.sops.yaml` | -**Deprecated (do not use):** ~~ComputerGuru - AI Remediation~~ (`fabb3421`) — old single-app with 159 permissions including Defender ATP. Broke consent on tenants without MDE license. Retire/delete from portal when confirmed no active tenants depend on it. +**DELETED from the azcomputerguru.com tenant 2026-06-14** (was *ComputerGuru - AI Remediation* / *Claude-MSP-Access* / *Cloud MSP Access*, `fabb3421-8b34-484b-bc17-e46de9703418`) — old single-app with 159 permissions including Defender ATP. Any token request now returns **AADSTS700016** (app/SP gone). Two consequences: +1. It held the ONLY **Mail.Send / Mail.ReadWrite / Contacts** scopes the fleet had, so **`/mailbox` (ACG own-mail send/read) and the M365 contacts task are BLOCKED** until a replacement app is provisioned. The 5-app suite below has none of those scopes (`investigator` = `Mail.Read` only). +2. The legacy "old app only" tenants below (Valleywide, Dataforth, Cascades) have NO working remediation app anymore — migration to the new suite is now REQUIRED, not optional. + +**Decision 2026-06-15 (Mike):** Mail.Send belongs in the SUITE, not a separate app. The real use case is incident response, auto-notifying victims during a mailbox takeover, which is a remediation action. Plan: add **`Mail.Send`** (application) to the **Exchange Operator** tier (`exchange-op`, `b43e7342-5b4b-492f-890f-bb5a4f7f40e9`), the existing Exchange remediation/write app. `Mail.ReadWrite` + `Contacts` are optional and only needed to fully restore the general `/mailbox` read/send + contacts task (secondary). + +Implementation (NOT yet executed — production multi-tenant app change, needs explicit go + admin-consent clicks): +1. Add the Graph app permission(s) to the Exchange Operator app manifest in the home tenant; grant admin consent in the home tenant. +2. Re-consent Exchange Operator in each tenant where IR victim-notification is needed (adding a permission invalidates prior consent and re-prompts). +3. Repoint `commands/mailbox.md` `client_id` + vault path to `computerguru-exchange-operator.sops.yaml`, and consent Exchange Operator in the ACG home tenant so `/mailbox` (own-mail) works again. When searching customer admin portals for a service principal (role assignments, app role assignments, CA exclusions), search by the display name for that tier (e.g., "ComputerGuru Security Investigator"). @@ -123,6 +132,6 @@ If token request or API call returns AADSTS650052 referencing `WindowsDefenderAT | mvaninc.com | 5affaf1e-de89-416b-a655-1b2cf615d5b1 | YES (2026-04-21) | — | YES (2026-04-21) | YES (2026-04-21) | — | — | — | — | Fully onboarded. Incident 2026-04-21: sysadmin GA account unauthorized sign-in from OKC via device PRT (MITCH-LAPTOP/JUNE). Remediated: pw reset, sessions revoked. CA policy (MFA all users) still pending — Mike to create. | | Quantum Wealth Management | 2fd0092b-e9b7-474c-ad73-301f34dd6b64 | YES (2026-05-27) | YES (2026-05-27) | YES (2026-05-27) | YES (2026-05-27) | YES (2026-05-27) | ASSIGNED (2026-05-27) | ASSIGNED (2026-05-27) | ASSIGNED (2026-05-27) | Fully onboarded via onboard-tenant.sh. NEW tenant (not the dormant GoDaddy one ddf3d2c9); quantumwms.com verified+primary; john@/sheila@ licensed. Intermedia->M365 migration in progress (Syncro #32323). | -**Migration note:** Valleywide, Dataforth, and Cascades still use the old deprecated app. Next visit: consent Security Investigator + assign Exchange Administrator role to new SP, then retire old app consent. +**Migration note (now URGENT):** Valleywide, Dataforth, and Cascades were on the old app (`fabb3421`), which was DELETED 2026-06-14 — they currently have NO working remediation access. Migrate each: consent Security Investigator (+ Exchange Operator if write is needed) and assign the Exchange Administrator role to the new SP in that tenant. Keep this table updated when rolling out to new tenants or migrating existing ones. Run `onboard-tenant.sh` after each consent and update the role columns from the script's final status output. diff --git a/errorlog.md b/errorlog.md index 965983b..b1f2e92 100644 --- a/errorlog.md +++ b/errorlog.md @@ -9,7 +9,7 @@ Format: `YYYY-MM-DD | MACHINE | command/skill | error (brief)` -2026-06-14 | GURU-5070 | mailbox skill (Graph token) | FABB app `fabb3421` (Claude-MSP-Access / "Cloud MSP Access") token request returned AADSTS700016 — app/SP no longer present in azcomputerguru.com tenant (deleted; gotchas.md already marked it deprecated). Blocks /mailbox + the M365 contacts task. Verified the remediation suite (live, ACG tenant) carries NO Mail.Send/Mail.ReadWrite/Contacts scopes (investigator has Mail.Read only) — so a straight repoint can't restore mailbox-send/contacts. Pending Mike decision: stand up a single-tenant ACG-internal mailbox app vs. add scopes to a suite tier. +2026-06-14 | GURU-5070 | mailbox skill (Graph token) | FABB app `fabb3421` (Claude-MSP-Access / "Cloud MSP Access") token request returned AADSTS700016 — app/SP no longer present in azcomputerguru.com tenant (deleted; gotchas.md already marked it deprecated). Blocks /mailbox + the M365 contacts task. Verified the remediation suite (live, ACG tenant) carries NO Mail.Send/Mail.ReadWrite/Contacts scopes (investigator has Mail.Read only) — so a straight repoint can't restore mailbox-send/contacts. Pending Mike decision: stand up a single-tenant ACG-internal mailbox app vs. add scopes to a suite tier. [2026-06-15] Docs hardened — gotchas.md now marks fabb3421 DELETED with the Mail/Contacts-scope blast radius + flags the 3 legacy "old app only" tenants (Valleywide/Dataforth/Cascades) as now having NO working remediation app (migration URGENT); mailbox.md carries a BLOCKED/AADSTS700016 banner. DECISION 2026-06-15 (Mike): Mail.Send goes into the suite (Exchange Operator tier) since its real use is IR victim-notification during mailbox takeovers; add Mail.Send to the exchange-op manifest + consent, repoint mailbox.md to exchange-op. Implementation not yet executed (production app change, needs go). 2026-06-14 | GURU-KALI | coord skill (coord.py) | Documented invocation `py .claude/skills/coord/scripts/coord.py ...` failed exit 127 — `py` (the Windows py-launcher) does not exist on Linux. Worked around with `python3`. [RESOLVED 2026-06-14] Added `.claude/scripts/py.sh` (resolves the working interpreter: identity.json `python.command` -> py -> python3 -> python, skipping the MS Store shim) and repointed all skill/command DOC invocations from bare `py` to `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh"`. The `.sh` skill scripts already resolved internally — left untouched. Broadcast to fleet. diff --git a/projects/acg-website-showcase/multipage/about.html b/projects/acg-website-showcase/multipage/about.html index c79922d..ccc5eac 100644 --- a/projects/acg-website-showcase/multipage/about.html +++ b/projects/acg-website-showcase/multipage/about.html @@ -10,6 +10,7 @@ + @@ -17,7 +18,7 @@