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:
@@ -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**:
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
20
.claude/memory/feedback_attribution_from_identity.md
Normal file
20
.claude/memory/feedback_attribution_from_identity.md
Normal 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.
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
66
.claude/scripts/whoami-block.sh
Executable 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
|
||||||
76
session-logs/2026-05-26-guru-kali-attribution-hardening.md
Normal file
76
session-logs/2026-05-26-guru-kali-attribution-hardening.md
Normal 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, afternoon–evening 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`).
|
||||||
Reference in New Issue
Block a user