sync: auto-sync from GURU-KALI at 2026-05-26 18:47:58

Author: Mike Swanson
Machine: GURU-KALI
Timestamp: 2026-05-26 18:47:58
This commit is contained in:
2026-05-26 18:48:01 -07:00
parent f94849fc00
commit 4e97e20a2f
8 changed files with 235 additions and 11 deletions

View File

@@ -40,7 +40,11 @@ Claude writes all sections directly. Be concise, factual, technical. No filler p
### Required sections (in order) ### Required sections (in order)
1. **User block**name, machine, role, session span. Pull from `.claude/identity.json` + git config. 1. **User block**generate it deterministically; do NOT hand-write or infer it. Run:
```bash
bash .claude/scripts/whoami-block.sh
```
Paste its output verbatim as this section. The script reads `.claude/identity.json` (+ `users.json` for role) — the only authoritative attribution sources. Never derive the user from the hostname, the `# userEmail` context hint, or memory. If the script emits a `[WARNING]` about a stale machine/hostname mismatch, stop and fix `identity.json` before saving.
2. **Session Summary** (Ollama) 2. **Session Summary** (Ollama)
3. **Key Decisions** (Ollama) 3. **Key Decisions** (Ollama)
4. **Problems Encountered** (Ollama) 4. **Problems Encountered** (Ollama)
@@ -61,7 +65,7 @@ When in doubt, include MORE detail — future sessions search these logs to reco
bash .claude/scripts/sync.sh bash .claude/scripts/sync.sh
``` ```
`sync.sh` handles: stage tracked changes by name (never `git add -A`), auto-commit, fetch + rebase, push, then the same flow for the vault repo, then surface cross-user `## Note for <user>` blocks. `sync.sh` handles: reconcile this machine's `git config user.name/email` to `.claude/identity.json` (so commit authorship can't drift), stage all changes with `git add -A` (after purging garbled Windows path-as-filename cruft), auto-commit, fetch + rebase, push, then the same flow for the vault repo, then surface cross-user `## Note for <user>` blocks.
After sync, emit a **Post-commit Summary**: After sync, emit a **Post-commit Summary**:

View File

@@ -10,7 +10,7 @@
- [IX Server SSH Access](reference_ix_server_ssh.md) - SSH access notes, no key auth from CachyOS workstation yet - [IX Server SSH Access](reference_ix_server_ssh.md) - SSH access notes, no key auth from CachyOS workstation yet
- [IX Access via Tailscale](reference_ix_access_tailscale.md) - IX server accessible with Tailscale on, no VPN needed - [IX Access via Tailscale](reference_ix_access_tailscale.md) - IX server accessible with Tailscale on, no VPN needed
- [Neptune Access via D2TESTNAS](reference_neptune_access_d2testnas.md) - Neptune must be routed through D2TESTNAS - [Neptune Access via D2TESTNAS](reference_neptune_access_d2testnas.md) - Neptune must be routed through D2TESTNAS
- [ACG-5070 Workstation](reference_workstation_setup.md) - Windows 11, replaced CachyOS. SOPS vault, Ollama, all dev tools. - [GURU-5070 Workstation (Mike's primary)](reference_workstation_setup.md) - Mike's box, Windows 11. Same machine as OC-5070/ACG-5070/acg-guru-5070 (renamed). SOPS vault, Ollama, all dev tools.
- [Matomo Analytics](reference_matomo_analytics.md) - Self-hosted analytics at analytics.azcomputerguru.com, site IDs, tracking for all 3 sites - [Matomo Analytics](reference_matomo_analytics.md) - Self-hosted analytics at analytics.azcomputerguru.com, site IDs, tracking for all 3 sites
- [Dataforth Contact - AJ](reference_dataforth_contact.md) - AJ at Dataforth, dataforthgit@ email forwarding to him - [Dataforth Contact - AJ](reference_dataforth_contact.md) - AJ at Dataforth, dataforthgit@ email forwarding to him
- [TickTick Integration](reference_ticktick_integration.md) - OAuth API integration, MCP server, SOPS vault creds, project/task CRUD - [TickTick Integration](reference_ticktick_integration.md) - OAuth API integration, MCP server, SOPS vault creds, project/task CRUD
@@ -21,9 +21,10 @@
- [Pluto Build Server](reference_pluto_build_server.md) - General-purpose Windows build VM, 172.16.3.36, SSH as Administrator, MSVC toolchain — use for any EXE (utilities, Howard's tools, GuruRMM agent) - [Pluto Build Server](reference_pluto_build_server.md) - General-purpose Windows build VM, 172.16.3.36, SSH as Administrator, MSVC toolchain — use for any EXE (utilities, Howard's tools, GuruRMM agent)
## Users ## Users
- [Howard Enos](user_howard.md) — Mike's brother, technician, full trust/access. Known machine: ACG-TECH03L. - [Howard Enos](user_howard.md) — Mike's brother, technician, full trust/access. Machines: ACG-TECH03L (laptop), Howard-Home (desktop) — authoritative list in users.json.
## Feedback ## Feedback
- [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.
- [GuruRMM agent parity rule](feedback_gururmm_agent_parity.md) — "Add feature X to the agent" = Windows + Linux + macOS in the same change, no exceptions. Stub + TODO if real impl not feasible. - [GuruRMM agent parity rule](feedback_gururmm_agent_parity.md) — "Add feature X to the agent" = Windows + Linux + macOS in the same change, no exceptions. Stub + TODO if real impl not feasible.
- [D2TESTNAS SSH Access](feedback_d2testnas_ssh.md) - Use root@192.168.0.9 with Paper123!@#, not sysadmin - [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 - [Bypass Permissions Setting](feedback_bypass_permissions_setting.md) - Set permissions.defaultMode to bypassPermissions in settings.json on all machines
@@ -51,7 +52,7 @@
- [Cascades folder redirect — fdeploy failure/recovery](feedback_cascades_folder_redirect.md) — Must pre-create subfolders before first logon. fdeploy caches failures silently. Recovery: fix-shell-redirect.ps1. Both GUID and legacy name keys required. - [Cascades folder redirect — fdeploy failure/recovery](feedback_cascades_folder_redirect.md) — Must pre-create subfolders before first logon. fdeploy caches failures silently. Recovery: fix-shell-redirect.ps1. Both GUID and legacy name keys required.
## Machine ## Machine
- [ACG-5070 Workstation Setup](reference_workstation_setup.md) - Windows 11 Pro clean install 2026-03-30, replaced CachyOS. All tools installed. - [GURU-5070 Workstation Setup](reference_workstation_setup.md) - Mike's primary (owner confirmed 2026-05-26). Windows 11 Pro. Renamed from OC-5070 → ACG-5070/acg-guru-5070 → GURU-5070; all the same box, all Mike's.
## Pending Setup ## Pending Setup
- [Mac gururmm setup pending](project_mac_gururmm_setup_pending.md) — ACTION REQUIRED: run `bash scripts/install-hooks.sh` in gururmm repo on Mikes-MacBook-Air before any RMM work - [Mac gururmm setup pending](project_mac_gururmm_setup_pending.md) — ACTION REQUIRED: run `bash scripts/install-hooks.sh` in gururmm repo on Mikes-MacBook-Air before any RMM work

View File

@@ -0,0 +1,20 @@
---
name: Attribution is read, never inferred
description: Who-did-what (user + machine) comes only from identity.json + users.json + git authorship. Never infer it from hostname patterns, the userEmail hint, or memory notes.
metadata:
type: feedback
---
When attributing work to a person or machine — in session logs, wiki articles, coord messages, commit authorship, or conversation — the attribution MUST come from authoritative sources, never from inference:
- **Who sits at this keyboard:** `.claude/identity.json` (per-machine, gitignored). Greet and stamp logs from its `full_name` / `user` / `machine` / `role`.
- **Which machines belong to whom:** `.claude/users.json``users.<user>.known_machines`. This is the registry.
- **Commit authorship:** `git log` author (`%an <%ae>`). `sync.sh` reconciles `git config` to identity.json on every run, so the author is always correct going forward.
- **Session-log User block:** generated by `bash .claude/scripts/whoami-block.sh` — paste verbatim, do not hand-write.
**Why:** On 2026-05-26 Mike reported recurring misattributions (Howard tied to Mike's machines and vice versa). Investigation proved git history and session logs were actually CLEAN (0 author-vs-machine-owner mismatches across 892 commits / 180 logs). The misattributions were a *reasoning-time inference* problem fed by stale, owner-less memory items — e.g. an "ACG-5070 workstation" note with no owner, and Howard's note saying "desktop hostname TBD". A session would then guess ownership and guess wrong. The fix was deterministic attribution + binding every machine in memory to its owner. Compare [[feedback_identity_precedence]] (identity.json beats the userEmail hint).
**How to apply:**
- Never write "this was probably done by X" or assign a machine to a person from a hostname pattern (e.g. the "5070" box is Mike's per users.json — it had names OC-5070 / ACG-5070 / acg-guru-5070 / GURU-5070; see [[reference_workstation_setup]]).
- If identity/ownership is unknown, read users.json/identity.json or ASK — do not infer.
- When you add a machine to a memory note, state its owner and point at users.json as the source of truth; don't restate the machine list as if it were authoritative.

View File

@@ -1,13 +1,15 @@
--- ---
name: ACG-5070 Workstation Setup name: GURU-5070 Workstation Setup (Mike's primary)
description: Primary workstation ACG-5070 (Windows 11 Pro), clean install 2026-03-30. Replaced CachyOS. description: Mike Swanson's primary workstation, current hostname GURU-5070 (Windows 11 Pro). Renamed over its life — same physical box as OC-5070 / ACG-5070 / acg-guru-5070.
type: reference type: reference
--- ---
## Workstation: ACG-5070 ## Workstation: GURU-5070 — owner: **Mike Swanson** (confirmed 2026-05-26)
**Same physical machine across renames:** `OC-5070``ACG-5070` / `acg-guru-5070`**`GURU-5070`** (current). All of these hostnames (and the bare `OC-5070` git author from March 2026) are this one box, and it is Mike's. Authoritative owner record: `.claude/users.json` (`users.mike.known_machines` includes `GURU-5070`). Do not attribute "5070" work to anyone but Mike, and never to Howard. See [[feedback_attribution_from_identity]].
- **OS:** Windows 11 Pro (clean install 2026-03-30) - **OS:** Windows 11 Pro (clean install 2026-03-30)
- **Previous OS:** CachyOS Linux (gone, replaced by Windows) - **Previous OS:** CachyOS Linux (gone, replaced by Windows — historical only)
- **Hardware:** ASUS laptop, Intel Arrow Lake-S + NVIDIA RTX 5070 Ti Mobile, dual NVMe - **Hardware:** ASUS laptop, Intel Arrow Lake-S + NVIDIA RTX 5070 Ti Mobile, dual NVMe
### Installed Tools ### Installed Tools

View File

@@ -6,8 +6,8 @@ type: user
Howard Enos is a technician at Arizona Computer Guru LLC and Mike Swanson's brother. He has full access to all systems, credentials, and client data — same level as Mike. No permission gating. Howard Enos is a technician at Arizona Computer Guru LLC and Mike Swanson's brother. He has full access to all systems, credentials, and client data — same level as Mike. No permission gating.
Known machine: ACG-TECH03L (laptop). Desktop hostname TBD (will be registered on first sync). Howard's machines: **ACG-TECH03L** (laptop) and **Howard-Home** (desktop). This list is NOT authoritative here — the source of truth is `.claude/users.json` (`users.howard.known_machines`). Do not infer machine ownership from this note or from hostname patterns; read users.json + the local `.claude/identity.json`. See [[feedback_attribution_from_identity]].
When working with Howard, treat him exactly as you would Mike — same context loading, same credential access, same capabilities. He uses claudetools for MSP work tracking, client management, and daily IT operations. When working with Howard, treat him exactly as you would Mike — same context loading, same credential access, same capabilities. He uses claudetools for MSP work tracking, client management, and daily IT operations.
His git commits should show `Howard Enos <howard@azcomputerguru.com>`. His git commits show `Howard Enos <howard@azcomputerguru.com>``sync.sh` reconciles git config to identity.json on every sync.

View File

@@ -43,6 +43,31 @@ purge_garbled_paths() {
return 0 return 0
} }
# --- Attribution: force commit authorship to match identity.json -------------
# Commit author is the per-person source of truth and must NEVER ride on stale
# local `git config`. If config disagrees with identity.json, correct the LOCAL
# repo config (not --global) so this machine's commits are always attributed to
# the human who actually owns this identity. Call once per repo (claudetools,
# then vault) before any commit happens.
reconcile_git_identity() {
local want_name="$1" want_email="$2" cur
if [ -n "$want_name" ]; then
cur=$(git config user.name 2>/dev/null || true)
if [ "$cur" != "$want_name" ]; then
echo -e "${YELLOW}[WARNING]${NC} git user.name '${cur}' -> '${want_name}' (corrected to match identity.json)"
git config user.name "$want_name"
fi
fi
if [ -n "$want_email" ]; then
cur=$(git config user.email 2>/dev/null || true)
if [ "$cur" != "$want_email" ]; then
echo -e "${YELLOW}[WARNING]${NC} git user.email '${cur}' -> '${want_email}' (corrected to match identity.json)"
git config user.email "$want_email"
fi
fi
return 0
}
# Machine + timestamp # Machine + timestamp
if [ -n "$COMPUTERNAME" ]; then if [ -n "$COMPUTERNAME" ]; then
MACHINE="$COMPUTERNAME" MACHINE="$COMPUTERNAME"
@@ -109,12 +134,36 @@ fi
# Load user identity # Load user identity
USER_DISPLAY="unknown" USER_DISPLAY="unknown"
USER_GITEA="" USER_GITEA=""
USER_EMAIL=""
if [ -f ".claude/identity.json" ]; then if [ -f ".claude/identity.json" ]; then
USER_DISPLAY=$($PYTHON -c "import json,sys; d=json.load(open('.claude/identity.json')); print(d.get('full_name', d.get('user','unknown')))" 2>/dev/null || echo "unknown") USER_DISPLAY=$($PYTHON -c "import json,sys; d=json.load(open('.claude/identity.json')); print(d.get('full_name', d.get('user','unknown')))" 2>/dev/null || echo "unknown")
USER_GITEA=$($PYTHON -c "import json,sys; d=json.load(open('.claude/identity.json')); print(d.get('user',''))" 2>/dev/null || echo "") USER_GITEA=$($PYTHON -c "import json,sys; d=json.load(open('.claude/identity.json')); print(d.get('user',''))" 2>/dev/null || echo "")
USER_EMAIL=$($PYTHON -c "import json,sys; d=json.load(open('.claude/identity.json')); print(d.get('email',''))" 2>/dev/null || echo "")
fi fi
echo -e "${GREEN}[OK]${NC} Syncing as: $USER_DISPLAY (machine: $MACHINE)" echo -e "${GREEN}[OK]${NC} Syncing as: $USER_DISPLAY (machine: $MACHINE)"
# Force this repo's commit authorship to match identity.json before any commit.
# Skip if identity.json was unreadable (USER_DISPLAY left at the "unknown"
# sentinel) — overwriting correct config with "unknown" would be worse than
# leaving it alone, and is the exact mis-attribution this guard prevents.
if [ "$USER_DISPLAY" != "unknown" ]; then
reconcile_git_identity "$USER_DISPLAY" "$USER_EMAIL"
else
echo -e "${YELLOW}[WARNING]${NC} identity.json present but unreadable — leaving existing git config untouched (not reconciling)."
fi
# Warn (don't block) if identity.json's machine field disagrees with the real
# hostname — the classic "stale identity.json copied to a new box" failure that
# stamps logs/commits with the wrong machine. Compare case-insensitively and
# ignore a trailing ".local" (macOS).
if [ -f ".claude/identity.json" ]; then
ID_MACHINE=$($PYTHON -c "import json; print(json.load(open('.claude/identity.json')).get('machine',''))" 2>/dev/null || echo "")
norm() { printf '%s' "$1" | tr 'A-Z' 'a-z' | sed 's/\.local$//'; }
if [ -n "$ID_MACHINE" ] && [ "$(norm "$ID_MACHINE")" != "$(norm "$MACHINE")" ]; then
echo -e "${YELLOW}[WARNING]${NC} identity.json machine '${ID_MACHINE}' != hostname '${MACHINE}' — identity.json may be stale on this box. Fix it before attributing work."
fi
fi
# Phase 1a: Update submodules to latest remote # Phase 1a: Update submodules to latest remote
# `git submodule foreach` only visits *initialized* submodules, so a fresh clone # `git submodule foreach` only visits *initialized* submodules, so a fresh clone
# would silently skip population and the old "[OK] updated" message lied. We now # would silently skip population and the old "[OK] updated" message lied. We now
@@ -385,6 +434,12 @@ else
cd "$VAULT_PATH" cd "$VAULT_PATH"
echo -e "${GREEN}[OK]${NC} Vault: $VAULT_PATH" echo -e "${GREEN}[OK]${NC} Vault: $VAULT_PATH"
# Same attribution guard for the vault repo (it has its own git config).
# Same "unknown" skip as the main repo — the main-repo warning already fired.
if [ "$USER_DISPLAY" != "unknown" ]; then
reconcile_git_identity "$USER_DISPLAY" "$USER_EMAIL"
fi
# Commit any local vault changes (porcelain catches untracked-only too) # Commit any local vault changes (porcelain catches untracked-only too)
if [ -n "$(git status --porcelain)" ]; then if [ -n "$(git status --porcelain)" ]; then
echo -e "${YELLOW}[INFO]${NC} Local vault changes detected — committing..." echo -e "${YELLOW}[INFO]${NC} Local vault changes detected — committing..."

66
.claude/scripts/whoami-block.sh Executable file
View File

@@ -0,0 +1,66 @@
#!/bin/bash
# Emit the canonical session-log "## User" attribution block.
#
# ATTRIBUTION IS NEVER INFERRED. The only sources of truth are:
# .claude/identity.json (per-machine, gitignored — who sits at this keyboard)
# .claude/users.json (role lookup by user key)
# Do NOT derive the user from the hostname, the `# userEmail` context hint, or
# from memory. /save calls this script and pastes its output verbatim as the
# session log's first section, so every log is stamped identically and correctly.
set -e
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
ID="$REPO_ROOT/.claude/identity.json"
USERS="$REPO_ROOT/.claude/users.json"
if [ ! -f "$ID" ]; then
echo "## User"
echo "- **User:** UNKNOWN — no .claude/identity.json on this machine."
echo "- **Action:** run the first-machine onboarding flow in CLAUDE.md before saving."
exit 0
fi
PYTHON=""
for c in py python3 python; do
if command -v "$c" >/dev/null 2>&1 && "$c" -c "import sys" >/dev/null 2>&1; then PYTHON="$c"; break; fi
done
if [ -z "$PYTHON" ]; then
echo "## User"
echo "- **User:** (could not render — no Python interpreter found)"
exit 0
fi
"$PYTHON" - "$ID" "$USERS" <<'PYEOF'
import json, sys, socket, re
idp, usersp = sys.argv[1], sys.argv[2]
try:
d = json.load(open(idp))
except Exception as e:
print("## User")
print(f"- **User:** UNREADABLE — .claude/identity.json failed to parse ({e}).")
print("- **Action:** repair identity.json before saving; do not infer the user.")
sys.exit(0)
full = d.get("full_name") or d.get("user", "unknown")
user = d.get("user", "unknown")
machine = d.get("machine", "unknown")
role = d.get("role", "")
if not role:
try:
role = json.load(open(usersp))["users"].get(user, {}).get("role", "")
except Exception:
pass
def norm(x): return re.sub(r'\.local$', '', (x or '').lower())
host = socket.gethostname()
stale = machine and host and norm(machine) != norm(host)
print("## User")
print(f"- **User:** {full} ({user})")
print(f"- **Machine:** {machine}")
if role:
print(f"- **Role:** {role}")
if stale:
print(f"- **[WARNING]** identity.json machine '{machine}' != hostname '{host}'. "
f"identity.json may be stale on this box — verify before trusting this attribution.")
PYEOF

View File

@@ -0,0 +1,76 @@
# Session Log — Work Attribution Hardening
## User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-KALI
- **Role:** admin
- **Session span:** 2026-05-26, afternoonevening MST (ending ~18:47 MST)
## Session Summary
Routine `/sync` rounds (clean fast-forwards; one larger 21-commit pull bringing GuruScan, the coord-todos system, `/wiki-compile`, and Lone Star/QuantumWMS wiki seeds) led into an `identity.json` update mandated by a coord message from the Mac: added `"claudetools_root": "/home/guru/claudetools"` (detected via `git rev-parse --show-toplevel`) and replied to the originating session to confirm.
Mike then raised the core problem: recurring misattribution of work to the wrong user/hostname, often via stale machine names. Initial instinct was to treat this as a git-authorship/"attribution rules" gap and build enforcement (mailmap/history rewrite). Mike course-corrected twice — it is not a git/gitea problem, it is (1) stale/owner-less `MEMORY.md` items corrupting reasoning-time inference, and (2) a shortcoming in the `/save`+`/sync` commands. Forensics confirmed his read precisely: across 892 commits, **zero** had an author disagreeing with the named machine's owner; across 180 session logs, **zero** User-block mismatches. Git history and logs were already clean. The only misattribution vector was inference fed by stale memory (an "ACG-5070 workstation" note with no owner; Howard's note saying "desktop hostname TBD") plus attribution being a soft model instruction rather than a deterministic step.
Implemented a three-part fix and ran it through the Code Review Agent (which found one HIGH and one MEDIUM bug — both fixed and re-verified). Result: attribution is now read deterministically from `identity.json`/`users.json`/git authorship, never inferred; every machine in memory is bound to an owner; and the 5070 box's rename lineage is recorded as Mike's.
## Key Decisions
- **No git history rewrite.** Confirmed git authorship is clean (0 mismatches/892 commits), and the repo is shared + rebase-synced across ~6 machines, so a filter-repo rewrite would be destructive and unjustified. Mailmap was also dropped — there is no display-attribution problem to paper over.
- **Attribution is read, never inferred** — codified as the governing rule. Sources of truth: `identity.json` (who's at the keyboard), `users.json` (machine registry), git authorship. Never hostname patterns, the `userEmail` hint, or memory.
- **Deterministic User block** via a dedicated script (`whoami-block.sh`) that `/save` runs and pastes verbatim — removes the model's freedom to fill the block from inference.
- **git config reconciled to identity.json on every sync** (local config, both repos), so commit authorship cannot drift going forward.
- **Confirmed with Mike:** `OC-5070` / `acg-guru-5070` / `ACG-5070` / `GURU-5070` are one physical machine, renamed over time, all Mike's. Recorded as such.
- **Namespaced this log by machine** (`-guru-kali-attribution-hardening`) instead of appending to the shared `2026-05-26-session.md`, to keep attribution unambiguous — consistent with the work itself.
## Problems Encountered
- **Self-inflicted inference, live:** initial analysis asserted "OC-5070 = old GURU-5070" and "azcomputerguru = ambiguous" as fact — the exact failure mode under repair. Corrected to verifying against `users.json`/asking Mike rather than inferring.
- **Code review found a HIGH bug:** on a present-but-malformed `identity.json`, `USER_DISPLAY` fell back to the `"unknown"` sentinel and would have been written as the git author (clobbering correct config in both repos). Fixed by guarding both reconcile call sites to skip when `USER_DISPLAY == "unknown"` and warn instead. Verified: existing config preserved.
- **MEDIUM bug:** `whoami-block.sh` dumped a Python traceback and emitted an empty block on malformed `identity.json`. Fixed with a try/except fallback block + `exit 0`. Verified clean.
- **Could not re-review via SendMessage** (tool unavailable in this environment); fixes were the review agent's own prescribed changes and were each verified empirically instead of re-spawning a full review agent.
## Configuration Changes
**Modified:**
- `.claude/scripts/sync.sh` — added `reconcile_git_identity()`; called (guarded against the `unknown` sentinel) in the claudetools repo and the vault repo; added a stale identity.json-machine vs hostname warning.
- `.claude/commands/save.md` — User block now generated by `whoami-block.sh` (not hand-written/inferred); corrected the stale description of sync.sh staging (`git add -A` after garbled-path purge, not "by name").
- `.claude/memory/user_howard.md` — machines now ACG-TECH03L + Howard-Home, deferring to `users.json`; removed the "desktop hostname TBD" hole.
- `.claude/memory/reference_workstation_setup.md` — retitled to GURU-5070 (Mike's primary); recorded the OC-5070 → ACG-5070/acg-guru-5070 → GURU-5070 rename chain and explicit owner.
- `.claude/memory/MEMORY.md` — updated the two stale 5070 index lines and Howard's line; surfaced the new attribution rule at the top of Feedback.
- `.claude/identity.json` — added `claudetools_root: /home/guru/claudetools` (gitignored, per-machine; not synced).
**Created:**
- `.claude/scripts/whoami-block.sh` — deterministic `## User` block generator from identity.json (+users.json role); handles missing/malformed identity and missing Python.
- `.claude/memory/feedback_attribution_from_identity.md` — the keystone "attribution is read, never inferred" rule with the why and how-to-apply.
## Credentials & Secrets
None created or discovered. No secret values touched. The git config reconcile uses only the name/email already in `identity.json`.
## Infrastructure & Servers
- Coord API `http://172.16.3.30:8001/api/coord` — sent confirmation message `dfeb6f2a-2d90-4bfe-bf95-a14eec449b3d` (GURU-KALI → Mikes-MacBook-Air) re: claudetools_root.
- This machine: GURU-KALI (Linux/Kali), git config `Mike Swanson <mike@azcomputerguru.com>` (matches identity.json — reconcile is a no-op here).
- Machine→owner registry remains authoritative in `.claude/users.json`: mike = GURU-5070, Mikes-MacBook-Air, GURU-BEAST-ROG, GURU-KALI (DESKTOP-0O8A1RL retired); howard = ACG-TECH03L, Howard-Home.
## Commands & Outputs
- Forensic cross-check (read-only): `git log --all --format=... | python3` mapping machine-in-subject → owner vs author → **0** author≠owner across 302 machine-named commits; 7 distinct author identities, all reconcilable to Mike or Howard.
- Session-log scan: 180 logs, 128 with User/Machine blocks, **0** user≠machine-owner mismatches; 52 pre-protocol (unattributed, not misattributed).
- `whoami-block.sh` verified: happy path (Mike/GURU-KALI/admin), malformed identity (UNREADABLE fallback, exit 0), missing identity (UNKNOWN fallback, exit 0).
- `reconcile_git_identity` verified in scratch repo: corrects on drift, silent no-op when matching, no clobber on empty args, `unknown`-sentinel guard preserves existing config.
## Pending / Incomplete Tasks
- **Broader memory-staleness sweep** — offered, not yet done. The attribution-scoped items are fixed; a general pass over the other ~50 memory files (e.g. `feedback_bypass_permissions_setting.md`, machine-setup notes) is a separate task awaiting go-ahead.
- **Code Review re-approval** — the review verdict was "Requires Revision"; all checklist items were fixed + verified but not formally re-approved by the agent (SendMessage unavailable). Re-run a review if a formal sign-off is wanted.
- **GuruRMM BUG-005 (mac build)** — still open, awaiting Mike's product decision (ship mac agents vs defer + make the audit pipeline treat a stubbed platform as N/A).
## Reference Information
- Code Review Agent id (this session): `a2b8c4d44c3c72b5d`.
- Coord confirmation message id: `dfeb6f2a-2d90-4bfe-bf95-a14eec449b3d`.
- New rule memory: `.claude/memory/feedback_attribution_from_identity.md`.
- Related memory: `feedback_identity_precedence.md` (identity.json beats the userEmail hint).
- Scripts: `.claude/scripts/whoami-block.sh`, `.claude/scripts/sync.sh` (`reconcile_git_identity`).