Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project are documented here. Format loosely follows
## [Unreleased]

### Added
- **Leave-behind audit — `scripts/moraine.py`.** Reads the two records that outlive an uninstall — macOS installer receipts (`pkgutil`) and the admin/user trust domains (`security dump-trust-settings`) — and correlates them into a verdict neither produces alone: a trusted root certificate whose installing package has no payload left on disk. First live run on the development Mac found exactly that — `INNORIX.CA`, a self-signed `CA:TRUE` root valid to 2028-07-07, trusted unconditionally in the admin domain, installed 2026-05-01 by a Korean file-transfer plugin whose app, LaunchAgent, and processes are all gone. Receipts are judged `present`/`partial`/`vanished`/`no_payload` with any file sampling reported; roots with no owning receipt are `unattributed` (MDM profiles and enterprise Wi-Fi legitimately have none), never accused. Read-only: nothing is written and nothing deleted — removing a trust root is an admin act that stays a human decision. Exposed through the MCP surface as `moraine_report`.

### Changed
- **The scree pitch no longer claims the unpushed-work verdict is unique.** Orca's workspace cleanup runs the same `rev-list --not --remotes` check against the worktrees it manages, verified in its source. README now states the actual difference — scree sweeps every agent worktree on the machine whatever created it, plus primary checkouts stranded off main and registry entries whose directory vanished — and names the prior art instead of implying none exists.

- **Operator-friction scan — `scripts/friction.py`.** Deterministic port of canary's `lib/sessions/friction.ts` (that repository is now a frozen paper archive; this was its one capability Modore did not already have). Flags the user turns where the operator pushed back on agent behaviour, across nine categories — wrong-action, no-research-assertion, stalling-approval, rule-contamination, over-orchestration-token, stale-repetition, verbosity, tone-attitude, other-ai-friction — at severity 1-3. Keyword and tone matching only, no model in the judgment path. Claude Code and Codex sessions are discovered through scree's own collectors rather than a second traversal; Gemini CLI chats (`~/.gemini/tmp/*/chats`, joined to a workspace through the project registry) and Claude Desktop local-agent sessions are added because scree does not traverse either — its Gemini collector reads only the project registry, and it has no Claude Desktop collector. Content contract: user-authored turns only, quotes capped at 200 characters and masked by default (`--raw-quotes` opts out), nothing written. `--json` serves the structured report. First live run over a 30-day window: 200 sessions, 3,563 user turns, 430 findings (rage 247 / irritation 60 / correction 123).
- **Read-only MCP surface — `scripts/mcp_server.py`.** A zero-dependency stdio JSON-RPC server exposing three tools: `scree_report` (join, retention forecast, sole-copy and orphan verdicts, by section), `friction_scan` (the taxonomy above, filterable by store/category/minimum severity), and `system_scan_summary` (the storage and security scan result already on disk, with its age). Each tool runs `scree.py --json` or `friction.py --json` and forwards the output, so no judgment is reimplemented and the CLI, the Mac app, and the MCP surface cannot disagree. Cleanup, deletion, and scan execution are deliberately not exposed — Modore's destructive path is gated on an on-screen human approval, and an agent-reachable bypass would void it; pinned by a test that exercises every tool and asserts the only processes ever spawned are the two judgment scripts. The read-only contract is enforced where tools are registered, ported from AirMCP's own Swift MCP server (`IOSPreviewContract`): a tool is reachable only if it is on an explicit name allowlist and annotated read-only and non-destructive, so one added without a deliberate edit fails closed rather than being merely unlisted. All results are fenced as untrusted machine-derived data.
- **scree lineage: what the sessions remember versus what the disk still holds.** Every work path appearing in session records is classified alive+git / alive+plain / vanished, using existence and a `.git` presence check only; macOS case-variant ghosts (the same path recorded under different casings) are merged by casefold and reported once with their spellings. First live run: 422 paths — 118 alive git repos, 189 alive plain directories, 115 vanished (session records their only trace), 4 case ghosts. Consumers such as balance's lineup-gap diff read `--json .lineage`; scree itself reports universal facts only.
Expand Down
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Modore

> **A local audit of what your AI agents left behind.** On a Mac where Claude Code, Codex, Gemini, or an AI IDE has been working, Modore answers the questions no other tool asks: which sessions touched which projects, which transcripts silently expire in a few days, which agent worktrees hold the only copy of unpushed work, and which paths survive nowhere except in a session record — deterministically, metadata-only, with no LLM anywhere in the judgment path.
> **A local audit of what your AI agents left behind.** On a Mac where Claude Code, Codex, Gemini, or an AI IDE has been working, Modore answers the questions nothing else on that machine is asking: which sessions touched which projects, which transcripts silently expire in a few days, which agent worktrees hold the only copy of unpushed work — whichever tool you ran the agent from — and which paths survive nowhere except in a session record — deterministically, metadata-only, with no LLM anywhere in the judgment path.
> The same evidence-first approach also covers the PC that feels busy for no reason: process, network, autorun, security, and storage signals turned into plain-language evidence before you stop or delete anything.

[🌐 **Website**](https://heznpc.github.io/modore/) · [📦 Releases (when published)](https://github.com/heznpc/modore/releases) · [Architecture](./docs/ARCHITECTURE.md)
Expand All @@ -15,7 +15,7 @@

### 1. What did my AI tools leave behind? (Mac)

On a Mac where Claude Code, Codex, Gemini, or an AI IDE has been working, the machine fills with traces no other tool audits: session stores that silently expire on rolling windows, agent git worktrees holding the only copy of unpushed work, orphaned sessions pointing at deleted projects, gigabytes of rebuildable model and editor caches, and paths whose only surviving record is a session transcript. **scree**, Modore's session-and-residue audit, judges all of it — deterministically, metadata-only, with no LLM anywhere in the judgment path:
On a Mac where Claude Code, Codex, Gemini, or an AI IDE has been working, the machine fills with traces nothing audits end to end: session stores that silently expire on rolling windows, agent git worktrees holding the only copy of unpushed work, orphaned sessions pointing at deleted projects, gigabytes of rebuildable model and editor caches, and paths whose only surviving record is a session transcript. **scree**, Modore's session-and-residue audit, judges all of it — deterministically, metadata-only, with no LLM anywhere in the judgment path:

```bash
python3 scripts/scree.py # join · retention forecast · orphans · sole-copy verdicts · lineage
Expand All @@ -26,6 +26,7 @@ python3 scripts/scree.py preserve <session-file> # masked single-session export
- **Retention forecast** — per-store rolling windows estimated from file ages, with D-day flags for sessions about to expire inside still-active projects.
- **Sole-copy judgment** — agent worktrees *and* primary checkouts stranded off main: protected (dirty or unpushed commits) versus rebuildable, from read-only git evidence, every verdict preview-grade with an explicit revalidation duty.
- **Orphans & lineage** — sessions pointing at vanished workspaces (`orphan_basis: path_missing`), and every remembered work path classified alive+git / alive+plain / vanished, with macOS case-variant ghosts merged.
- **Not a uniqueness claim about the primitive** — an agent IDE that creates worktrees can and does compute the same unpushed-work verdict for the worktrees it manages (Orca's workspace cleanup runs the same `rev-list --not --remotes` check). scree's coverage is the difference: it sweeps every agent worktree on the machine regardless of which tool created it, plus primary checkouts stranded off main and registry entries whose directory is gone.
- **Contract** — leading JSONL lines are decoded in memory but message content is never retained or emitted; nested transcripts are attributed by `stat()` without being opened; pinned by tests. `preserve` is the single deliberate exception: one caller-named file, mask-by-default (`--raw` opts out), no bulk export.

**friction**, scree's sibling, reads the same four session stores for the opposite question — not what the agents left behind, but where the operator stopped them:
Expand All @@ -40,6 +41,21 @@ python3 scripts/friction.py scan --json --source codex # structured output
- **Same judgment contract** — keyword and tone matching only, no model anywhere in the path; a review aid that both under- and over-catches, so every verdict is tagged `evidence: preview`.
- **Content contract** — only turns authored by the user are examined; assistant text, tool calls, and nested subagent transcripts are never emitted. Quotes are capped at 200 characters and masked (email / JWT / API keys / private keys / home path) by default, with `--raw-quotes` as the explicit opt-out. Nothing is written.

### What stayed after you uninstalled it

Removing an app does not remove what it registered. `moraine` reads the two records that outlive the uninstall — macOS installer receipts and the system/user trust stores — and correlates them:

```bash
python3 scripts/moraine.py # receipts · trusted roots · orphaned-root verdict
python3 scripts/moraine.py --json
```

- **Receipts** — every package ever installed, with whether its payload still exists (`present` / `partial` / `vanished` / `no_payload`) and when it was installed. macOS never forgets a receipt, so it outlives the files.
- **Trusted roots** — every root in the admin and user trust domains, with self-signed / CA / key size / validity, and whether its trust is *unconditional*. An empty usage-constraint array means "trust as root for every policy", which reads like "nothing configured" and is exactly the opposite.
- **The correlated verdict** — a trusted root whose installing package has no payload left is a certificate still vouching for a vendor otherwise gone from the machine. Neither source produces that alone. `unattributed` means no receipt claims the root: MDM profiles, enterprise Wi-Fi, and hand-imported roots are legitimately unattributed, so it is a statement about attribution, not legitimacy.
- **Prior art** — AppCleaner is the reference for moraine hunting and its location list is genuinely vetted, but it is target-driven (you drop an app on it) and its binary contains no `SecTrust`/`SecCertificate` symbols at all; mole reads `pkgutil` receipts too, but only to locate app bundles in `/usr/local` and `/opt`, never to judge whether a payload survived. The trust store is the half neither reaches.
- **Deletes nothing.** Removing a trust root is an admin act and stays a human decision.

### Both questions, mid-session (MCP)

The judgments above were terminal-only, which meant the agent doing the work could not ask them while working. Modore ships a zero-dependency MCP server so it can — before deleting a worktree, before assuming a session will still be there tomorrow, before repeating something the operator already objected to:
Expand All @@ -55,6 +71,7 @@ python3 scripts/mcp_server.py --tools # inspect the surface without speaking
- `scree_report` — the join, retention forecast, orphan/sole-copy/lineage judgment, by section, with every truncation reported.
- `friction_scan` — the pushback taxonomy, filterable by store, category, and minimum severity.
- `system_scan_summary` — the storage and security scan result *already on disk*, with its age, because a stale result read as current is the failure mode here.
- `moraine_report` — installer receipts, trusted roots, and the orphaned-root verdict above.
- **A thin layer, not a second implementation** — each tool runs `scree.py --json` or `friction.py --json` and forwards what it prints, so the CLI, the Mac app, and the MCP surface cannot disagree about what is true.
- **Read-only by contract, enforced at registration** — a tool is reachable only if it is on an explicit allowlist and annotated read-only and non-destructive; one added without a deliberate edit fails closed. Cleanup, deletion, and scan execution are deliberately absent. Modore gates destruction on an approval a human grants on screen; an agent-reachable bypass would not be a feature, it would be the end of that guarantee. Every result is fenced as untrusted data.

Expand All @@ -68,6 +85,7 @@ A fan that will not stop, CPU/GPU load while idle, an unknown process, a strange

- **Two OS editions under one brand**: Modore for Windows and Modore for Mac share the same promise — explain local machine state in plain language without deleting anything automatically.
- **Mac Edition — AI-agent session audit**: `scree` (above) is the flagship Mac capability — cross-tool join, retention forecast, orphan/sole-copy/lineage judgment, metadata-only.
- **Mac Edition — leave-behind audit**: `moraine` judges what survived an uninstall from installer receipts and the macOS trust store, including root certificates left trusted by software that is no longer installed.
- **Mac Edition — operator-friction scan**: `friction` classifies the turns where the operator pushed back on agent behaviour across Claude Code, Codex, Gemini CLI, and Claude Desktop transcripts — nine categories, severity 1-3, deterministic keyword/tone matching, user-authored turns only, quotes masked by default.
- **Read-only MCP surface**: a zero-dependency stdio MCP server exposing scree, friction, and the existing storage/security scan summary to an agent mid-session. Judgment only — no cleanup, no deletion, no scan execution.
- **Mac Edition scanner**: Bash + JXA collectors for macOS security context, launchd/login items, Gatekeeper/SIP/XProtect, network/listening ports, installed-app size, and developer-runtime incidents. Every collector reports `ok`, `permission_denied`, `unavailable`, `timed_out`, or `failed`; a missing required collector can never become a safe verdict.
Expand Down Expand Up @@ -238,6 +256,7 @@ modore/
├── scripts/
│ ├── scree.py AI-agent session & residue audit (metadata-only)
│ ├── friction.py operator-pushback scan over the same session stores
│ ├── moraine.py receipts + trust-store audit of what survived an uninstall
│ ├── mcp_server.py read-only MCP surface (scree · friction · scan summary)
│ ├── menu.ps1 Windows interactive menu
│ ├── scanner.ps1 Windows scanner
Expand Down
Loading
Loading