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
43 changes: 38 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,11 +337,14 @@ Refs:

`cfg ui` starts a localhost-only web UI over the same action layer as the CLI and
MCP server. It reads like a git client: a collection-and-record tree on the left,
a commit-graph history rail, and a line-aligned side-by-side diff that collapses
unchanged context (expandable in place) and keeps the field name pinned while you
scroll. It can run status, diff, impact, commit, branch draft commits, PR open
and merge, log, show, adopt, restore, tag, init, import, and fsck, and ships
dark and light themes.
a recent-activity rail before you select anything, per-record commit graphs, and
a line-aligned side-by-side diff that collapses unchanged context (expandable in
place) and keeps the field name pinned while you scroll. The recent activity view
surfaces live drift and the latest cfgit commits across all configured records,
so you can see what changed recently without opening records one by one. It can
run status, diff, impact, commit, branch draft commits, PR open and merge, log,
show, adopt, restore, tag, init, import, and fsck, and ships dark and light
themes.

By default it binds to `127.0.0.1:8765` and tries the next free ports if needed:

Expand All @@ -354,6 +357,35 @@ If you omit `--port`, cfgit will try the next free local ports. If you pass
`--port` explicitly, cfgit treats that port as intentional and fails if it is
already in use.

## Synthetic UI demo

The repo includes a fake support-control-plane fixture for screenshots, demos,
and UI testing. It creates only synthetic records in the database you pass; use a
throwaway database name when you want to keep the run contained.

```bash
python examples/seed_support_demo.py \
--uri 'mongodb://localhost:27017/?replicaSet=rs0' \
--db cfgit_ui_demo \
--reset

cfg --config-file examples/cfgit-support-demo.toml init
cfg --config-file examples/cfgit-support-demo.toml import --all -m "initial synthetic demo import"

python examples/seed_support_demo.py \
--uri 'mongodb://localhost:27017/?replicaSet=rs0' \
--db cfgit_ui_demo \
--drift

cfg --config-file examples/cfgit-support-demo.toml ui
```

The first seed creates clean planner, critic, router, model, and policy records.
The `--drift` pass then simulates an admin-console edit: planner routing changes,
the refund policy changes, and a new entitlements resolver appears. That gives
the UI useful drift, impact, adopt, branch, PR, merge, and restore-history paths
without using proprietary data.

## MCP and agent usage

The MCP server exposes the same operations with a uniform envelope:
Expand Down Expand Up @@ -385,6 +417,7 @@ Tools include:
- `cfg_pr_show`
- `cfg_pr_close`
- `cfg_pr_merge`
- `cfg_recent_history`
- `cfg_log`
- `cfg_show`
- `cfg_adopt`
Expand Down
5 changes: 4 additions & 1 deletion docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,10 @@ cfg ui
```

The UI binds to `127.0.0.1`. It is a local operator surface over the same action
layer as the CLI and MCP server.
layer as the CLI and MCP server. Before a record is selected, the middle rail
shows recent activity across all configured records: current live drift plus the
latest cfgit history entries. Selecting one of those entries opens that record's
normal history and diff view.

## JSON mode

Expand Down
67 changes: 67 additions & 0 deletions examples/cfgit-support-demo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[project]
name = "cfgit-support-demo"

[history]
history_collection = "cfgit_demo_history"
heads_collection = "cfgit_demo_heads"

[branches]
enabled = true
refs_collection = "cfgit_demo_refs"
default_branch = "main"

[[collection]]
name = "agent_configs"
id_field = "config_id"
live_when = { is_active = true }
ignore_fields = ["_id", "is_active", "updated_at", "updated_by"]
secret_fields = []

[[collection]]
name = "modelgarden_models"
id_field = "model_path"
live_when = {}
ignore_fields = ["_id", "updated_at", "updated_by"]
secret_fields = ["provider_config.api_key"]

[[collection]]
name = "policy_rules"
id_field = "rule_id"
live_when = { active = true }
ignore_fields = ["_id", "active", "updated_at", "updated_by"]
secret_fields = []

[secrets]
block_fields = ["*_key", "*_secret", "*_token", "*api_key*", "*password*"]
block_values = ["sk-[A-Za-z0-9]{20,}", "AKIA[0-9A-Z]{16}"]
on_match = "refuse"

[author]
from = "git"

[connections]
enabled = true
share_with_ai = []
ai_provider = "openai"
warn_level = "none"
links = [
{ field = "phase_contract", means = "contract other records may rely on" },
{ field = "tools", means = "shared tool list" },
{ field = "fallback_models", means = "fallback model routing" },
{ field = "applies_to", means = "policy-to-agent linkage" },
]

[env.dev]
database = "mongo"
uri = "mongodb://localhost:27017/?replicaSet=rs0"
db = "cfgit_ui_demo"
needs_approval = false

[env.dev.identity]
mode = "open"

[env.dev.permissions]
mode = "open"
admins = []
writers = []
admin_actions = ["restore_system"]
228 changes: 228 additions & 0 deletions examples/seed_support_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
"""Seed a synthetic cfgit demo database.

This fixture is intentionally fake: support agents, model routes, and policy
rules for demo screenshots. It never reads production data.

Typical demo flow:
python examples/seed_support_demo.py --reset
cfg --config-file examples/cfgit-support-demo.toml init
cfg --config-file examples/cfgit-support-demo.toml import --all -m "initial import"
python examples/seed_support_demo.py --drift
"""
from __future__ import annotations

import argparse
from datetime import datetime, timezone

from pymongo import MongoClient


def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--uri", default="mongodb://localhost:27017/?replicaSet=rs0")
parser.add_argument("--db", default="cfgit_ui_demo")
parser.add_argument("--reset", action="store_true")
parser.add_argument(
"--drift",
action="store_true",
help="apply synthetic out-of-band edits after the base records have been imported",
)
args = parser.parse_args()

client = MongoClient(args.uri)
db = client[args.db]
if args.db in {"admin", "config", "local"}:
raise SystemExit(f"refusing to seed Mongo system database {args.db!r}")
if args.reset:
for name in [
"agent_configs",
"modelgarden_models",
"policy_rules",
"cfgit_demo_history",
"cfgit_demo_heads",
"cfgit_demo_refs",
]:
db.drop_collection(name)

now = datetime.now(timezone.utc)
if args.drift:
apply_drift(db, now)
print(f"applied synthetic cfgit demo drift in Mongo database {args.db!r}")
return

seed_base(db, now)
print(f"seeded synthetic cfgit demo data in Mongo database {args.db!r}")


def seed_base(db, now: datetime) -> None:
db.agent_configs.insert_many(
[
{
"config_id": "planner",
"is_active": True,
"role": "Support Triage Planner",
"model": "openai/gpt-4o-mini",
"tools": ["search", "calendar", "handoff"],
"fallback_models": ["anthropic/claude-haiku"],
"phase_contract": (
"Produces a support-ticket plan with risk, refund-policy, "
"and owner handoff notes."
),
"instructions": (
"Classify incoming support tickets, cite the current refund "
"policy, and hand off risky cases."
),
"updated_at": now,
"updated_by": "seed",
},
{
"config_id": "critic",
"is_active": True,
"role": "Policy Reviewer",
"model": "anthropic/claude-haiku",
"tools": ["diff", "policy_check"],
"phase_contract": (
"Reviews planner decisions for refund-policy compliance and "
"escalation risk."
),
"instructions": (
"Review planner output from planner, verify refund_window_v1, "
"and flag missing restore notes."
),
"updated_at": now,
"updated_by": "seed",
},
{
"config_id": "router",
"is_active": True,
"role": "Support Router",
"model": "openai/gpt-4o-mini",
"tools": ["modelgarden"],
"fallback_models": ["openai/gpt-4o-mini"],
"phase_contract": (
"Routes customer tickets to planner or critic according to "
"policy risk."
),
"instructions": (
"Use planner for standard tickets and critic when "
"refund_window_v1 or escalation rules apply."
),
"updated_at": now,
"updated_by": "seed",
},
]
)
db.modelgarden_models.insert_many(
[
{
"model_path": "openai/gpt-4o-mini",
"provider": "openai",
"enabled": True,
"retry_policy": "standard",
"price_per_million": 0.15,
"provider_config": {"api_key": "demo-secret-stays-live"},
},
{
"model_path": "anthropic/claude-haiku",
"provider": "anthropic",
"enabled": True,
"retry_policy": "conservative",
"price_per_million": 0.8,
"provider_config": {"api_key": "demo-secret-stays-live"},
},
]
)
db.policy_rules.insert_many(
[
{
"rule_id": "refund_window_v1",
"active": True,
"title": "Refund window",
"applies_to": ["planner", "critic", "router"],
"severity": "medium",
"rule_text": (
"Refunds are allowed within 30 days unless the ticket is "
"marked abuse_risk."
),
"updated_at": now,
"updated_by": "seed",
},
{
"rule_id": "abuse_risk_v1",
"active": True,
"title": "Abuse-risk escalation",
"applies_to": ["critic", "router"],
"severity": "high",
"rule_text": (
"Cases with repeated refund attempts require human review "
"before resolution."
),
"updated_at": now,
"updated_by": "seed",
},
]
)


def apply_drift(db, now: datetime) -> None:
db.agent_configs.update_one(
{"config_id": "planner"},
{
"$set": {
"model": "openai/gpt-4o-mini-2026-demo",
"fallback_models": ["anthropic/claude-haiku", "openai/gpt-4o-mini"],
"phase_contract": (
"Produces a support-ticket plan with refund policy, entitlement checks, "
"and escalation owner notes."
),
"instructions": (
"Classify incoming support tickets, cite refund_window_v1, check "
"entitlement tier, and hand off risky cases."
),
"updated_at": now,
"updated_by": "demo-drift",
}
},
)
db.policy_rules.update_one(
{"rule_id": "refund_window_v1"},
{
"$set": {
"rule_text": (
"Refunds are allowed within 45 days unless the ticket is marked "
"abuse_risk or enterprise_contract_override."
),
"severity": "high",
"updated_at": now,
"updated_by": "demo-drift",
}
},
)
db.agent_configs.update_one(
{"config_id": "entitlements"},
{
"$set": {
"config_id": "entitlements",
"is_active": True,
"role": "Entitlements Resolver",
"model": "openai/gpt-4o-mini",
"tools": ["search", "billing", "handoff"],
"fallback_models": ["anthropic/claude-haiku"],
"phase_contract": (
"Checks plan, renewal date, and regional refund obligations before "
"planner response."
),
"instructions": (
"Resolve customer entitlement tier and return constraints before "
"refunds are discussed."
),
"updated_at": now,
"updated_by": "demo-drift",
}
},
upsert=True,
)


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions skills/cfgit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Use cfgit as the safety layer around a live datastore. The app still reads and w
2. Inspect before mutation.
- `cfg diff <record> =HEAD =live --json`
- `cfg log <record> --json`
- Use MCP `cfg_recent_history` when you need the latest cfgit commits across all configured records before choosing a record.
- `cfg impact <record> =HEAD =live --json`
- Scoped LLM review, when explicitly needed: `cfg impact <record> =HEAD =live --against <related-record> --llm --json`
- If history lookup reports an env mismatch, re-run against the stamped env before making changes.
Expand Down Expand Up @@ -83,6 +84,7 @@ If the cfgit MCP server is available, prefer its tools over shelling out:
- `cfg_pr_show`
- `cfg_pr_close`
- `cfg_pr_merge`
- `cfg_recent_history`
- `cfg_log`
- `cfg_show`
- `cfg_adopt`
Expand Down
Loading
Loading