Skip to content
Merged
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
118 changes: 112 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,24 @@ jobs:
- name: Trace/conflict CLI tests
run: python scripts/run_acceptance.py --only verify_traceability_cli

- name: Trace adapter tests (REST + 8 MCP tools + 43-tool gate)
- name: Trace adapter tests (REST + 8 MCP tools + 51-tool gate)
run: python scripts/run_acceptance.py --only verify_traceability_adapters

# -- git change intelligence + overlays (v2 Phase 7) ----------------
# Read-only by construction: no step below mutates Git or contacts a
# remote, and every overlay build must change zero canonical rows.
- name: Git command security boundary tests
run: python scripts/run_acceptance.py --only verify_git_security

- name: Overlay model + diff taxonomy + isolation tests
run: python scripts/run_acceptance.py --only verify_overlay_model

- name: Change Impact Packet + verifier tests (Bundle stays draft.2)
run: python scripts/run_acceptance.py --only verify_impact_packet

- name: Overlay adapter tests (CLI + REST + 51-tool MCP gate + versions)
run: python scripts/run_acceptance.py --only verify_overlay_adapters

- name: No provider credential reaches CI
run: |
python - <<'PY'
Expand Down Expand Up @@ -358,7 +373,8 @@ jobs:
# read-only Asset tools, Phase 3 read-only document tools, Phase 4
# read-only semantic/lens tools, Phase 5 read-only graph tools and
# Phase 6 read-only trace/conflict tools ALONGSIDE them, never in
# place of one. 9+4+6+7+9+8 = 43, exactly.
# place of one, and Phase 7 read-only git-overlay tools too.
# 9+4+6+7+9+8+8 = 51, exactly.
core = {"search", "route", "dispatch", "get_glossary",
"find_similar_cases", "save_case", "get_doc",
"propose_fix", "apply_fix"}
Expand All @@ -378,6 +394,10 @@ jobs:
"get_trace_path", "get_traceability_coverage",
"list_traceability_gaps", "list_engineering_conflicts",
"get_engineering_conflict"}
overlay = {"list_git_overlays", "get_git_overlay",
"get_git_diff_summary", "search_git_overlay",
"get_git_overlay_evidence", "get_change_impact_report",
"list_impacted_requirements", "list_impacted_tests"}
server = mcp_server.create_mcp_server(get_runtime())
names = {t.name for t in asyncio.run(server.list_tools())}
assert core <= names, f"core MCP tool missing: {core - names}"
Expand All @@ -386,9 +406,11 @@ jobs:
assert semantic <= names, f"semantic MCP tool missing: {semantic - names}"
assert knowledge <= names, f"graph MCP tool missing: {knowledge - names}"
assert trace <= names, f"trace MCP tool missing: {trace - names}"
expected = core | asset | document | semantic | knowledge | trace
assert overlay <= names, f"overlay MCP tool missing: {overlay - names}"
expected = (core | asset | document | semantic | knowledge | trace
| overlay)
assert names == expected, f"unexpected MCP tools: {names ^ expected}"
assert len(names) == 43, f"expected 43 tools, got {len(names)}"
assert len(names) == 51, f"expected 51 tools, got {len(names)}"
# No write/import tool: Phase 3 adds no document writer, Phase 4
# nothing that configures providers or triggers paid analysis,
# Phase 5 nothing that promotes, creates, merges, changes
Expand Down Expand Up @@ -499,7 +521,7 @@ jobs:
- name: Artifact export contract
run: python tests/verify_artifacts.py

- name: Migration to v0007 + content-store byte round-trip
- name: Migration to v0008 + content-store byte round-trip
shell: bash
run: |
python - <<'PY'
Expand All @@ -508,7 +530,7 @@ jobs:
os.environ["OPENMIND_MACHINE_DIR"] = tempfile.mkdtemp()
from openmind import db, content_store as cs
db.init_db()
assert db.migration_status()["version"] == 7, db.migration_status()
assert db.migration_status()["version"] == 8, db.migration_status()
conn = db._c()
tables = {r[0] for r in conn.execute(
"SELECT name FROM sqlite_master WHERE type='table'")}
Expand All @@ -527,6 +549,13 @@ jobs:
"engineering_conflicts", "engineering_conflict_objects",
"engineering_conflict_evidence",
"engineering_conflict_decisions"} <= tables, tables
assert {"git_repositories", "workspace_git_baselines",
"git_overlays", "git_overlay_repositories",
"git_overlay_files", "git_overlay_segments",
"git_overlay_evidence", "git_overlay_entity_deltas",
"git_overlay_relation_deltas", "git_overlay_trace_impacts",
"git_overlay_conflict_impacts", "git_overlay_reports",
"git_overlay_search_index"} <= tables, tables
assert "content_blob_hash" in {
r[1] for r in conn.execute("PRAGMA table_info(segments)")}
assert "payload_json" in {
Expand Down Expand Up @@ -884,6 +913,83 @@ jobs:
"conflict -> promotion -> coverage -> bundle")
PY

# -- git overlay smoke (v2 Phase 7): temp repo -> baseline -> branch
# overlay -> modified code -> impact report -> packet export + verify.
# Read-only: the overlay build must change zero canonical rows.
- name: Git overlay smoke (baseline, branch overlay, impact, packet)
shell: bash
env:
OPENMIND_EMBED_OFFLINE: "1"
OPENMIND_EMBED_DEVICE: cpu
run: |
python - <<'PY'
import os, subprocess, sys, tempfile
os.environ["OPENMIND_DATA_DIR"] = tempfile.mkdtemp()
os.environ["OPENMIND_MACHINE_DIR"] = tempfile.mkdtemp()
repo = tempfile.mkdtemp(prefix="om_ovl_smoke_")
env = dict(os.environ, GIT_AUTHOR_NAME="ci", GIT_AUTHOR_EMAIL="ci@x.t",
GIT_COMMITTER_NAME="ci", GIT_COMMITTER_EMAIL="ci@x.t")
def git(*a): subprocess.run(["git", *a], cwd=repo, env=env, check=True,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def wr(rel, s):
p = os.path.join(repo, rel); os.makedirs(os.path.dirname(p) or repo, exist_ok=True)
open(p, "w", newline="\n").write(s)
git("init", "-q"); git("config", "core.autocrlf", "false")
wr("src/Svc.java", "class Svc {\n int run() { return 3000; }\n int keep(){return 1;} }\n")
wr("conf.properties", "timeout=3000\n")
git("add", "-A"); git("commit", "-qm", "base")
main = subprocess.run(["git","rev-parse","--abbrev-ref","HEAD"], cwd=repo,
env=env, capture_output=True, text=True).stdout.strip()
git("checkout", "-q", "-b", "feature")
wr("src/Svc.java", "class Svc {\n int run() { return 5000; }\n int keep(){return 1;}\n int add(){return 2;} }\n")
wr("conf.properties", "timeout=5000\n")
git("commit", "-aqm", "feature change"); git("checkout", "-q", main)

from openmind.runtime import get_runtime
from openmind import db
rt = get_runtime()
pid = rt.workspaces.create("ovl-smoke", path=repo.replace("\\", "/"))["id"]
rt.ingest.start(pid, wait=True, timeout=240)
rt.knowledge.sync(pid, actor="ci")
rt.git.discover_repositories(pid)
cap = rt.git.capture_baseline(pid, actor="ci")
assert cap["ok"], cap

conn, lock = db.shared_connection()
def counts():
with lock:
return {t: conn.execute(f"SELECT COUNT(*) c FROM {t}").fetchone()["c"]
for t in ("assets","segments","engineering_entities",
"engineering_relations","trace_paths",
"engineering_conflicts","knowledge_revisions")}
before = counts()
ovl = rt.overlays.create_overlay(pid, kind="branch",
repositories=[{"repository":"git:.","base":main,
"head":"feature","target_branch":main}],
name="ci")
oid = ovl["overlay"]["id"]
assert ovl["overlay"]["state"] == "ready", ovl["overlay"]
files = rt.overlays.list_overlay_files(pid, oid)
assert files["count"] >= 2, files
rep = rt.overlays.get_impact_report(pid, oid)["report"]
assert rep["schemaVersion"] == "1.0.0-draft.1", rep["schemaVersion"]
assert rep["riskSummary"]["overallRisk"] in ("info","low","medium","high",
"critical","unknown")
after = counts()
assert before == after, f"ISOLATION VIOLATED: {before} != {after}"

out = tempfile.mkdtemp(prefix="ovl_pkt_")
rt.overlays.export_impact_packet(pid, oid, out)
v = subprocess.run([sys.executable, "-m", "openmind.impact_verify", out],
capture_output=True, text=True)
assert v.returncode == 0, v.stdout + v.stderr
# refresh unchanged -> no-op
ref = rt.overlays.refresh_overlay(pid, oid)
assert ref.get("refreshed") is False, ref
print("git overlay smoke OK: baseline -> branch overlay -> impact "
"report -> packet verify -> no-op refresh; canonical drift NONE")
PY

- name: CLI asset help + fixture ingest + asset list + evidence read
shell: bash
run: |
Expand Down
68 changes: 66 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,14 +417,78 @@ python -m openmind.cli conflict promote --workspace p_... --candidate sx_... \
`conflict promote`, and only when confirmed + active + verified with all
referenced objects resolving canonically.

Deliberately **not** in this phase: Git diff synchronization and branch/PR
overlays (Phase 7), webhooks, CI merge blocking, automatic conflict
Deliberately **not** in this phase: automatic conflict
resolution, automatic promotion, connectors, plugin packaging (Phase 8),
Neo4j/Cypher/GraphQL, and the Bundle 2.0 schema freeze. Full design:
[docs/v2/phase-6-traceability-conflicts.md](docs/v2/phase-6-traceability-conflicts.md).

---

## Git Change Intelligence & Branch/PR Overlays (v2 Phase 7)

Phase 7 adds an **isolated Git Overlay plane**: a read-only projection of a
branch, pull request, commit range or working tree onto a coherent snapshot of
the canonical Base Workspace. It answers *"if this change landed, what
engineering knowledge would it touch, break, or fix?"* — with the same evidence
discipline as the rest of OpenMind. It **never mutates Git, never contacts a
remote, and never writes a single canonical row.**

```bash
# capture a coherent baseline (clean worktree + known Knowledge Revision)
python -m openmind.cli git baseline capture --workspace p_... --json

# analyse a local PR using only locally available refs (no GitHub fetch)
python -m openmind.cli pr analyze --workspace p_... --repository git:. \
--base main --head feature/namecheck --pr-number 123 \
--title "Add NameCheck timeout handling" --wait --json

# the evidence-cited Change Impact Report, then a portable Impact Packet
python -m openmind.cli overlay impact --workspace p_... --overlay ov_... --json
python -m openmind.cli impact export --workspace p_... --overlay ov_... \
--output ./.openmind-impact --json
python -m openmind.impact_verify ./.openmind-impact
```

- **One read-only Git boundary.** Every Git call goes through a single
`subprocess.run(shell=False, ...)` gate that permits only a read-only
allow-list (`rev-parse`, `diff`, `cat-file`, `merge-base`, …) and rejects
`checkout`/`reset`/`merge`/`fetch`/`push`/… *before spawning a process*. Refs
are validated and resolved through `--verify --end-of-options` so a hostile
value can never become a Git option, and no command contacts a remote.
- **Overlay ≠ Workspace.** An overlay is `Base snapshot + Git file delta +
overlay content snapshots + overlay Segments/Evidence + graph delta + derived
impact`. It references Base objects by id but lives in its own tables; every
result carries the Base coordinates (Knowledge Revision, policy checksum,
commits) that make it reproducible. **Building an overlay changes zero
canonical rows** (asset/graph/trace/conflict tables and the revision ledger
are provably untouched).
- **Honest diff intelligence.** Added / modified / deleted / renamed / copied /
type-changed / binary / symlink / submodule / Git-LFS changes are all handled;
a pure rename is a *path change*, not a fake implementation change; binary and
LFS content is flagged, never parsed or embedded; before/after bytes are
snapshotted immutably so Evidence survives Git GC.
- **Evidence-cited impact, never invented.** A changed implementation is walked
back to its Requirements via reverse formal traceability, revalidated against
a **virtual Graph View** (Base graph + overlay delta); tests are *recommended*,
never executed; projected Gaps and Conflicts are computed from the Phase 6
detectors over only the changed subjects and are **never** written into the
canonical gap/conflict tables. Risk is deterministic and rule-based —
`unknown` is never downgraded to `low`.
- **Additive and compatible.** All 43 existing MCP tools are unchanged; Phase 7
adds 8 read-only overlay tools (**51 total**). `.openmind` stays `1.1.0`, the
Knowledge Bundle stays `2.0.0-draft.2`, and overlays get a *separate* Change
Impact Packet (`1.0.0-draft.1`). Reconciliation after an external merge is
explicit — OpenMind never merges, and never auto-promotes a projected relation.

Deliberately **not** in this phase: any Git mutation or remote contact, GitHub
API auth / PR fetching / comments / webhooks, GitLab/Bitbucket, CI merge
blocking, automatic semantic analysis of changed files, plugin packaging and
Verified Agent Skills (Phase 8), and the Feature Evidence Packet (Phase 9).
Full design:
[docs/v2/phase-7-git-overlays.md](docs/v2/phase-7-git-overlays.md).

---

## Built For AI Agent Workflows

Open Mind is designed as infrastructure for practical AI agents and tool
Expand Down
65 changes: 65 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,71 @@ paths/gaps and open/under-review/accepted-risk conflicts.
consumer can run — it also checks trace referential integrity, step
ordering and coverage arithmetic.

### `git` / `overlay` / `pr` / `impact` — Git change intelligence (v2 Phase 7)

Read-only Git overlays project a branch/PR/commit-range/working-tree change
onto a coherent Base snapshot. **Nothing here mutates Git, contacts a remote,
or writes a canonical row.** Refs must already exist locally.

```bash
# 1. repositories + a coherent baseline (needs a clean worktree at HEAD and a
# known Knowledge Revision — capture is explicit, never silent on a dirty tree)
python -m openmind.cli git repositories --workspace p_... --discover --json
python -m openmind.cli git status --workspace p_... --repository git:. --json
python -m openmind.cli git baseline plan --workspace p_... --json
python -m openmind.cli git baseline capture --workspace p_... --actor me --json
python -m openmind.cli git baseline list --workspace p_... --json

# 2. build an overlay (branch / pr / commit-range / working-tree / change-set)
python -m openmind.cli overlay create --workspace p_... --kind branch \
--repository git:. --base main --head feature/namecheck --wait --json
# multi-repository change-set: repeatable --repo-range "git:key=base..head"
python -m openmind.cli overlay create --workspace p_... --kind change-set \
--repo-range "git:service-a=main..feature/a" \
--repo-range "git:service-b=main..feature/b" --wait --json
# local PR convenience (metadata is caller-supplied; no GitHub call)
python -m openmind.cli pr analyze --workspace p_... --repository git:. \
--base main --head feature/namecheck --pr-number 123 \
--title "Add NameCheck timeout handling" --wait --json

# 3. read the evidence-cited impact (all read-only, provisional)
python -m openmind.cli overlay files --workspace p_... --overlay ov_... --json
python -m openmind.cli overlay impact --workspace p_... --overlay ov_... --json
python -m openmind.cli overlay requirements --workspace p_... --overlay ov_... --json
python -m openmind.cli overlay tests --workspace p_... --overlay ov_... --json
python -m openmind.cli overlay conflicts --workspace p_... --overlay ov_... --json
python -m openmind.cli overlay search --workspace p_... --overlay ov_... --query timeout --json

# 4. incrementality + lifecycle
python -m openmind.cli overlay refresh --workspace p_... --overlay ov_... --json # no-op if unchanged
python -m openmind.cli overlay close --workspace p_... --overlay ov_... --json
python -m openmind.cli overlay delete --workspace p_... --overlay ov_... --json # removes only overlay data

# 5. after an EXTERNAL merge (OpenMind never merges), reconcile explicitly
python -m openmind.cli overlay reconcile --workspace p_... --overlay ov_... \
--actor reviewer --note "PR 123 merged into main." --wait --json

# 6. portable Change Impact Packet + standalone verifier
python -m openmind.cli impact export --workspace p_... --overlay ov_... \
--output ./.openmind-impact --json
python -m openmind.impact_verify ./.openmind-impact
```

- **Overlay revision + staleness.** A changed head ref produces the next overlay
revision; an unchanged refresh is a no-op; when the Base commit or Knowledge
Revision advances the overlay goes `stale` and must be refreshed explicitly —
the old report is never silently reinterpreted.
- **Projected, not canonical.** Impacted requirements/tests, projected gaps and
projected conflicts are overlay records; tests are *recommended*, never run;
a projected gap/conflict is never inserted into a canonical table.
- **Impact Packet ≠ Knowledge Bundle.** The packet (`1.0.0-draft.1`) is a
separate, deterministic, hash-manifested export; the canonical Bundle stays
`2.0.0-draft.2`. `python -m openmind.impact_verify` re-checks hashes and
referential integrity with no database.

Full design and guarantees:
[docs/v2/phase-7-git-overlays.md](v2/phase-7-git-overlays.md).

### `export`

```bash
Expand Down
Loading
Loading