Skip to content

Make Edgebase an automatic work-contract runtime - #3

Merged
ychampion merged 1 commit into
mainfrom
x/feature/v1-work-contract-runtime
May 26, 2026
Merged

Make Edgebase an automatic work-contract runtime#3
ychampion merged 1 commit into
mainfrom
x/feature/v1-work-contract-runtime

Conversation

@ychampion

Copy link
Copy Markdown
Owner

Summary

  • add install-prompt/bootstrap, host capability table, Codex skill surfaces, and team mode
  • persist active Goal Capsule state for hook-driven Work Contracts, status, finish, and Patch Passports
  • expand Claude and git lifecycle hooks with warn/strict pre-edit enforcement
  • update README, architecture, agent-client, validation, and changelog docs

Tests

  • python3 -m unittest -v
  • python3 -m compileall -q src tests
  • edgebase install-prompt --agent all
  • edgebase status --json
  • edgebase finish "demo" --test "python3 -m unittest -v: pass"

Not tested

  • live Claude Code hook execution inside the actual Claude runtime
  • live Codex skill invocation inside the Codex host UI

Copilot AI review requested due to automatic review settings May 26, 2026 12:09

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces several new features to Edgebase, including copy-focused installer prompts, active Work Contract state persistence, strict and warning pre-edit enforcement modes, additional Git freshness hooks, status reporting, session-end Patch Passports, Codex skill surfaces, and a team mode configuration. The review feedback highlights several high-quality improvement opportunities: enhancing configuration portability by avoiding hardcoded absolute paths and interpreter executables in generated team-mode scripts, ensuring backward compatibility with Python versions older than 3.11 by using timezone.utc instead of UTC, eliminating duplicate helper functions in runtime.py by importing them from goal.py, and gracefully handling potential setup errors during Codex skill installations with try-except blocks.

Comment thread src/edgebase/team.py
Comment on lines +84 to +99
def write_required_hook(repo_root: Path) -> Path:
path = repo_root / ".claude" / "hooks" / "edgebase-required.sh"
path.parent.mkdir(parents=True, exist_ok=True)
command = shlex.join([sys.executable, "-m", "edgebase", "status", "--root", str(repo_root), "--json"])
path.write_text(
"#!/bin/sh\n"
"# Edgebase required-mode hook. Generated by `edgebase team init required`.\n"
f"if ! {command} >/dev/null 2>&1; then\n"
" echo '{\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Edgebase is required for AI-assisted edits in this repo. Run edgebase install-prompt --agent all.\"}'\n"
" exit 0\n"
"fi\n"
"echo '{}'\n",
encoding="utf-8",
)
path.chmod(0o755)
return path

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Hardcoding sys.executable and the absolute repo_root path in the generated shell script .claude/hooks/edgebase-required.sh makes the hook non-portable. When committed to a shared repository, it will fail for other team members who have different Python installation paths or clone the repository to a different directory. Using python3 and omitting the absolute path is much more portable.

Suggested change
def write_required_hook(repo_root: Path) -> Path:
path = repo_root / ".claude" / "hooks" / "edgebase-required.sh"
path.parent.mkdir(parents=True, exist_ok=True)
command = shlex.join([sys.executable, "-m", "edgebase", "status", "--root", str(repo_root), "--json"])
path.write_text(
"#!/bin/sh\n"
"# Edgebase required-mode hook. Generated by `edgebase team init required`.\n"
f"if ! {command} >/dev/null 2>&1; then\n"
" echo '{\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Edgebase is required for AI-assisted edits in this repo. Run edgebase install-prompt --agent all.\"}'\n"
" exit 0\n"
"fi\n"
"echo '{}'\n",
encoding="utf-8",
)
path.chmod(0o755)
return path
def write_required_hook(repo_root: Path) -> Path:
path = repo_root / ".claude" / "hooks" / "edgebase-required.sh"
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
"#!/bin/sh\n"
"# Edgebase required-mode hook. Generated by `edgebase team init required`.\n"
"if ! python3 -m edgebase status --json >/dev/null 2>&1; then\n"
" echo '{\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Edgebase is required for AI-assisted edits in this repo. Run edgebase install-prompt --agent all.\"}'\n"
" exit 0\n"
"fi\n"
"echo '{}'\n",
encoding="utf-8",
)
path.chmod(0o755)
return path

Comment thread src/edgebase/team.py
Comment on lines +116 to +119
entry = {
"matcher": "Write|Edit|MultiEdit",
"hooks": [{"type": "command", "command": shlex.quote(str(hook_path)), "timeout": 10}],
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Hardcoding the absolute hook_path in .claude/settings.json makes the configuration non-portable. Since this file is committed to the repository for team mode, it will break for other team members. Using a relative path is fully portable and robust.

    relative_path = hook_path.relative_to(repo_root)
    entry = {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [{"type": "command", "command": "./" + shlex.quote(relative_path.as_posix()), "timeout": 10}],
    }

Comment thread src/edgebase/runtime.py Outdated
import json
import os
import time
from datetime import UTC, datetime

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Importing UTC directly from datetime was introduced in Python 3.11. If the project is run on Python 3.10 or older (which is still very common in many production and development environments), this will raise an ImportError. Using timezone.utc is fully backward-compatible with all Python 3 versions.

Suggested change
from datetime import UTC, datetime
from datetime import datetime, timezone

Comment thread src/edgebase/runtime.py Outdated
Comment on lines +311 to +312
def iso_timestamp(ts: float) -> str:
return datetime.fromtimestamp(ts, tz=UTC).isoformat(timespec="seconds").replace("+00:00", "Z")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use timezone.utc instead of UTC for backward compatibility with Python versions older than 3.11.

Suggested change
def iso_timestamp(ts: float) -> str:
return datetime.fromtimestamp(ts, tz=UTC).isoformat(timespec="seconds").replace("+00:00", "Z")
def iso_timestamp(ts: float) -> str:
return datetime.fromtimestamp(ts, tz=timezone.utc).isoformat(timespec="seconds").replace("+00:00", "Z")

Comment thread src/edgebase/runtime.py Outdated
Comment on lines +315 to +316
def safe_timestamp() -> str:
return datetime.fromtimestamp(time.time(), tz=UTC).strftime("%Y%m%dT%H%M%SZ")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use timezone.utc instead of UTC for backward compatibility with Python versions older than 3.11.

Suggested change
def safe_timestamp() -> str:
return datetime.fromtimestamp(time.time(), tz=UTC).strftime("%Y%m%dT%H%M%SZ")
def safe_timestamp() -> str:
return datetime.fromtimestamp(time.time(), tz=timezone.utc).strftime("%Y%m%dT%H%M%SZ")

Comment thread src/edgebase/runtime.py Outdated
from .context import stale_files
from .git import changed_files as git_changed_files
from .git import current_commit, find_repo_root
from .goal import GoalCapsule, WorkContract, build_goal_capsule, build_patch_passport

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Import unrecorded_checks from .goal to avoid code duplication and improve maintainability.

Suggested change
from .goal import GoalCapsule, WorkContract, build_goal_capsule, build_patch_passport
from .goal import GoalCapsule, WorkContract, build_goal_capsule, build_patch_passport, unrecorded_checks

Comment thread src/edgebase/runtime.py
Comment on lines +299 to +308
def unrecorded_checks(required: list[str], tests: list[str]) -> list[str]:
recorded = {test_command(test) for test in tests}
return [check for check in required if check not in recorded]


def test_command(test: str) -> str:
if ":" not in test:
return test.strip()
command, _status = test.rsplit(":", 1)
return command.strip()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The functions unrecorded_checks and test_command are duplicated exactly from src/edgebase/goal.py. Redundant code duplication reduces maintainability. Since src/edgebase/runtime.py already imports from .goal, we can import these functions directly instead of redefining them.

Suggested change
def unrecorded_checks(required: list[str], tests: list[str]) -> list[str]:
recorded = {test_command(test) for test in tests}
return [check for check in required if check not in recorded]
def test_command(test: str) -> str:
if ":" not in test:
return test.strip()
command, _status = test.rsplit(":", 1)
return command.strip()
# unrecorded_checks and test_command are imported from .goal

Comment thread src/edgebase/setup.py Outdated
Comment on lines +324 to +327
skill_path = install_codex_skill(repo_root / ".agents" / "skills" / "edgebase" / "SKILL.md", repo_root, command, goal=False)
results.append(SetupResult(skill_path, "updated", "Codex project skill /edgebase"))
goal_skill_path = install_codex_skill(repo_root / ".agents" / "skills" / "goal" / "SKILL.md", repo_root, command, goal=True)
results.append(SetupResult(goal_skill_path, "updated", "Codex project skill /goal"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Unlike setup_claude, which wraps skill installation in a try-except block to gracefully handle and report errors (e.g., if a skill file already exists without the Edgebase marker), setup_codex does not catch RuntimeError raised by install_codex_skill. This can cause the entire setup process to crash with a traceback instead of skipping the step gracefully.

Suggested change
skill_path = install_codex_skill(repo_root / ".agents" / "skills" / "edgebase" / "SKILL.md", repo_root, command, goal=False)
results.append(SetupResult(skill_path, "updated", "Codex project skill /edgebase"))
goal_skill_path = install_codex_skill(repo_root / ".agents" / "skills" / "goal" / "SKILL.md", repo_root, command, goal=True)
results.append(SetupResult(goal_skill_path, "updated", "Codex project skill /goal"))
try:
skill_path = install_codex_skill(repo_root / ".agents" / "skills" / "edgebase" / "SKILL.md", repo_root, command, goal=False)
except RuntimeError as exc:
results.append(
SetupResult(repo_root / ".agents" / "skills" / "edgebase" / "SKILL.md", "skipped", str(exc))
)
else:
results.append(SetupResult(skill_path, "updated", "Codex project skill /edgebase"))
try:
goal_skill_path = install_codex_skill(repo_root / ".agents" / "skills" / "goal" / "SKILL.md", repo_root, command, goal=True)
except RuntimeError as exc:
results.append(
SetupResult(repo_root / ".agents" / "skills" / "goal" / "SKILL.md", "skipped", str(exc))
)
else:
results.append(SetupResult(goal_skill_path, "updated", "Codex project skill /goal"))

Comment thread src/edgebase/setup.py Outdated
Comment on lines +332 to +335
skill_path = install_codex_skill(Path.home() / ".codex" / "skills" / "edgebase" / "SKILL.md", repo_root, command, goal=False)
results.append(SetupResult(skill_path, "updated", "Codex global skill /edgebase"))
goal_skill_path = install_codex_skill(Path.home() / ".codex" / "skills" / "goal" / "SKILL.md", repo_root, command, goal=True)
results.append(SetupResult(goal_skill_path, "updated", "Codex global skill /goal"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Wrap global Codex skill installations in try-except blocks to handle potential RuntimeError gracefully, matching the robust behavior implemented for Claude.

Suggested change
skill_path = install_codex_skill(Path.home() / ".codex" / "skills" / "edgebase" / "SKILL.md", repo_root, command, goal=False)
results.append(SetupResult(skill_path, "updated", "Codex global skill /edgebase"))
goal_skill_path = install_codex_skill(Path.home() / ".codex" / "skills" / "goal" / "SKILL.md", repo_root, command, goal=True)
results.append(SetupResult(goal_skill_path, "updated", "Codex global skill /goal"))
try:
skill_path = install_codex_skill(Path.home() / ".codex" / "skills" / "edgebase" / "SKILL.md", repo_root, command, goal=False)
except RuntimeError as exc:
results.append(
SetupResult(Path.home() / ".codex" / "skills" / "edgebase" / "SKILL.md", "skipped", str(exc))
)
else:
results.append(SetupResult(skill_path, "updated", "Codex global skill /edgebase"))
try:
goal_skill_path = install_codex_skill(Path.home() / ".codex" / "skills" / "goal" / "SKILL.md", repo_root, command, goal=True)
except RuntimeError as exc:
results.append(
SetupResult(Path.home() / ".codex" / "skills" / "goal" / "SKILL.md", "skipped", str(exc))
)
else:
results.append(SetupResult(goal_skill_path, "updated", "Codex global skill /goal"))

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@ychampion
ychampion force-pushed the x/feature/v1-work-contract-runtime branch 2 times, most recently from 5d0d002 to d8b80eb Compare May 26, 2026 16:59
Add host-aware install prompts, active Work Contract state, runtime status and finish gates, warn/strict pre-edit enforcement, broader freshness hooks, Codex skill surfaces, team guidance, and docs so agents can follow the Goal Capsule loop automatically after setup.

Constraint: Preserve dirty /root/edgebase work by implementing in the clean feature worktree based on origin/main.

Rejected: Rebase the older PR commit directly | main already contained overlapping preflight, radius, hook, and graph work that needed integration instead of replay.

Confidence: high

Scope-risk: broad

Directive: Keep setup host-capability driven and keep default enforcement warn-only unless users opt into --strict.

Tested: python3 -m unittest -v; python3 -m compileall -q src tests; git diff --check; edgebase install-prompt --agent all; edgebase status --json; edgebase finish "demo" --test "python3 -m unittest -v: pass"

Not-tested: Live Claude Code/Codex hook execution inside their native hosts; verified with simulated hook payloads and generated config tests.

Co-authored-by: OmX <omx@oh-my-codex.dev>
@ychampion
ychampion force-pushed the x/feature/v1-work-contract-runtime branch from d8b80eb to be9aa8c Compare May 26, 2026 17:00
@ychampion
ychampion merged commit c3ba123 into main May 26, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants