169 lines
7.2 KiB
Markdown
169 lines
7.2 KiB
Markdown
|
|
# Sprint 112 Plan: Environment Snapshot and Diff
|
||
|
|
|
||
|
|
## Context
|
||
|
|
|
||
|
|
Before iterating on a failing environment, you must read it deterministically.
|
||
|
|
"Environment" means the intersection of: installed packages, active config files,
|
||
|
|
running processes, and file system state that your project depends on. Without a
|
||
|
|
structured snapshot, the iteration session has no baseline to compare against and
|
||
|
|
no way to tell whether a change actually helped.
|
||
|
|
|
||
|
|
Sprint 112 adds `EnvironmentSnapshot` — a structured, serializable record of the
|
||
|
|
environment at a point in time — and `EnvironmentDiff` — the delta between two
|
||
|
|
snapshots. These are the observability primitives that the build, test, and setup
|
||
|
|
feedback loops in later sprints will use.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Goals
|
||
|
|
|
||
|
|
1. Define `EnvironmentSnapshot` schema covering deps, configs, processes, and paths
|
||
|
|
2. Implement probes that read each domain from the actual system
|
||
|
|
3. Define `EnvironmentDiff` as a typed delta between two snapshots
|
||
|
|
4. Expose snapshot and diff as MCP tools
|
||
|
|
5. Produce a setup verification report (what is present vs what is needed)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Steps
|
||
|
|
|
||
|
|
### Step 1349: `EnvironmentSnapshot` core schema (12 tests)
|
||
|
|
|
||
|
|
Create `editor/src/EnvironmentSnapshot.h`.
|
||
|
|
|
||
|
|
Fields: `snapshot_id`, `captured_at`, `workspace_root`, `platform`
|
||
|
|
(os, arch, shell), `packages[]` (name, version, source: apt/pip/npm/cargo/etc.),
|
||
|
|
`config_files[]` (path, hash, key_values[] for known formats),
|
||
|
|
`processes[]` (name, pid, port, status), `env_vars[]` (key, value — filtered
|
||
|
|
to relevant set, never secrets).
|
||
|
|
|
||
|
|
Tests (12): constructable with defaults, snapshot_id non-empty, platform fields
|
||
|
|
populated, packages list starts empty, config list starts empty, process list
|
||
|
|
starts empty, env_vars list starts empty, JSON serialization, JSON deserialization,
|
||
|
|
unknown package source passthrough, schema version present, deterministic output.
|
||
|
|
|
||
|
|
### Step 1350: Package state probe (10 tests)
|
||
|
|
|
||
|
|
Create `editor/src/PackageProbe.h`.
|
||
|
|
|
||
|
|
Probes installed packages from available managers on the current platform.
|
||
|
|
Strategy: try each manager (dpkg-query, pip list, npm list -g, cargo --list)
|
||
|
|
via subprocess, parse stdout into PackageRecord rows, tag with source.
|
||
|
|
Failures are non-fatal — missing manager produces empty list for that source.
|
||
|
|
|
||
|
|
Tests (10): probe runs without crashing, result is parseable, known-installed
|
||
|
|
package appears in results, unknown package absent, source tag correct,
|
||
|
|
failed probe returns empty not error, deterministic sort order, version field
|
||
|
|
populated, JSON round-trip, timeout respected (default 5s).
|
||
|
|
|
||
|
|
### Step 1351: Config file probe (10 tests)
|
||
|
|
|
||
|
|
Create `editor/src/ConfigFileProbe.h`.
|
||
|
|
|
||
|
|
Scans workspace for known config files: `.env`, `*.yaml`, `*.toml`, `*.json`
|
||
|
|
(at workspace root depth only — no deep recursion). Reads content, computes
|
||
|
|
SHA-256 hash, extracts key_values for .env and simple YAML/TOML maps.
|
||
|
|
Sensitive keys (PASSWORD, SECRET, KEY, TOKEN) are redacted to `"[REDACTED]"`.
|
||
|
|
|
||
|
|
Tests (10): .env file parsed into key_values, sensitive keys redacted,
|
||
|
|
hash computed and stable, yaml keys extracted, toml keys extracted,
|
||
|
|
json keys extracted (top-level only), missing file produces absent entry,
|
||
|
|
hash changes on content change, deterministic key ordering, depth limit
|
||
|
|
respected.
|
||
|
|
|
||
|
|
### Step 1352: Process state probe (10 tests)
|
||
|
|
|
||
|
|
Create `editor/src/ProcessProbe.h`.
|
||
|
|
|
||
|
|
Lists running processes filtered to a known-relevant set (configurable list of
|
||
|
|
process name substrings). Uses `/proc` on Linux, `ps` subprocess on macOS.
|
||
|
|
Captures name, pid, and listening ports (from `ss -tlnp` or `netstat`).
|
||
|
|
|
||
|
|
Tests (10): probe runs without crashing, result parseable, known process
|
||
|
|
appears when running, absent process not in list, port captured when listening,
|
||
|
|
no port when not listening, deterministic sort, name filter respected,
|
||
|
|
JSON round-trip, timeout respected.
|
||
|
|
|
||
|
|
### Step 1353: `EnvironmentDiff` schema and computation (10 tests)
|
||
|
|
|
||
|
|
Create `editor/src/EnvironmentDiff.h`.
|
||
|
|
|
||
|
|
Compares two `EnvironmentSnapshot` instances. Fields: `added_packages[]`,
|
||
|
|
`removed_packages[]`, `changed_packages[]` (name + old_version + new_version),
|
||
|
|
`added_configs[]`, `removed_configs[]`, `changed_configs[]` (path + old_hash
|
||
|
|
+ new_hash), `added_processes[]`, `removed_processes[]`, `unchanged` (bool —
|
||
|
|
true when all diff lists are empty).
|
||
|
|
|
||
|
|
Tests (10): empty diff on identical snapshots, unchanged true on identical,
|
||
|
|
added package detected, removed package detected, changed package detected,
|
||
|
|
added config detected, changed config hash detected, process added detected,
|
||
|
|
JSON round-trip, deterministic diff ordering.
|
||
|
|
|
||
|
|
### Step 1354: Snapshot persistence (sidecar integration) (8 tests)
|
||
|
|
|
||
|
|
Extend `SessionStore.h` (or add `SnapshotStore.h`) to persist
|
||
|
|
`EnvironmentSnapshot` alongside iteration sessions.
|
||
|
|
|
||
|
|
Convention: `.whetstone/snapshots/<snapshot_id>.json`
|
||
|
|
|
||
|
|
Tests (8): save creates file, load round-trips, list returns ids, overwrite
|
||
|
|
preserves new data, missing snapshot returns nullopt, atomic write, path
|
||
|
|
derivation correct, sidecar dir created on first save.
|
||
|
|
|
||
|
|
### Step 1355: Setup verification report (8 tests)
|
||
|
|
|
||
|
|
Create `editor/src/SetupVerificationReport.h`.
|
||
|
|
|
||
|
|
Given a `RequiredEnvironment` spec (a list of expected packages, config files,
|
||
|
|
and processes) and a current `EnvironmentSnapshot`, produces a structured report:
|
||
|
|
`satisfied[]`, `missing[]`, `version_mismatch[]`, `ready` (bool — true when
|
||
|
|
missing and version_mismatch are both empty).
|
||
|
|
|
||
|
|
Tests (8): ready true when all present, missing package detected, version
|
||
|
|
mismatch detected, correct version passes, empty required produces ready=true,
|
||
|
|
JSON round-trip, deterministic output, report serializable.
|
||
|
|
|
||
|
|
### Step 1356: `whetstone_snapshot_environment` MCP tool (8 tests)
|
||
|
|
|
||
|
|
Input: `{ "workspace": string, "process_filter": string[]? }`
|
||
|
|
Output: full `EnvironmentSnapshot` JSON + `snapshot_id`.
|
||
|
|
|
||
|
|
Runs all three probes (packages, configs, processes) and persists result.
|
||
|
|
|
||
|
|
Tests (8): tool registered, snapshot_id returned, packages field present,
|
||
|
|
configs field present, processes field present, snapshot persisted to disk,
|
||
|
|
invalid workspace returns structured error, deterministic output shape.
|
||
|
|
|
||
|
|
### Step 1357: `whetstone_diff_environments` MCP tool (8 tests)
|
||
|
|
|
||
|
|
Input: `{ "before_id": string, "after_id": string }`
|
||
|
|
Output: `EnvironmentDiff` JSON + `unchanged` bool.
|
||
|
|
|
||
|
|
Loads both snapshots from store, computes diff, returns result.
|
||
|
|
|
||
|
|
Tests (8): tool registered, unchanged true on same snapshot, added package
|
||
|
|
appears in diff, removed package appears, changed package appears, missing
|
||
|
|
snapshot_id returns error, diff persisted to session if session_id provided,
|
||
|
|
deterministic output.
|
||
|
|
|
||
|
|
### Step 1358: Sprint 112 integration summary + regression (8 tests)
|
||
|
|
|
||
|
|
Create `editor/src/Sprint112IntegrationSummary.h`.
|
||
|
|
|
||
|
|
End-to-end test: capture snapshot → mutate environment (add a dummy config
|
||
|
|
file) → capture second snapshot → diff → verify change detected.
|
||
|
|
|
||
|
|
Tests (8): headers constructable, tools registered, end-to-end diff detects
|
||
|
|
change, verification report correct, sidecar files created, no header exceeds
|
||
|
|
600 lines, deterministic snapshots, full prior regression passes.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Architecture Gate
|
||
|
|
|
||
|
|
- Probes are non-fatal: subprocess failure returns empty list, not panic
|
||
|
|
- No new dependencies — subprocess via std::system or popen, parsing in C++
|
||
|
|
- Sensitive data never stored in snapshots (redaction in ConfigFileProbe)
|
||
|
|
- Max 600 lines per header
|
||
|
|
- All MCP outputs stable and machine-readable
|