fix(seedgo): DPLAN-0244 trust files to 100% standards — json_handler + justified bypasses
CI seedgo gate is strict 100%. trust_registry.py now uses json_handler for registry I/O (log_operation on writes only — no per-hook-event flood); unused_function bypassed (cross-branch public API, static analysis can't see callers). trust.py gained print_introspection + --help/no-args gates; genuinely-N/A standards (json_structure delegated to trust_registry, frozen cross-branch import) bypassed with justification. Both branches 100%, all tests green, live acceptance re-verified (attack still blocked both gates). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YEAyLFCuo4uD934fwFxocz
This commit is contained in:
@@ -375,6 +375,41 @@
|
||||
"file": "shared/json_ops.py",
|
||||
"standard": "unused_function",
|
||||
"reason": "backup_json() is consumed by @spawn (cross-branch caller). Appears unused in @aipass-only scan but is a shared API."
|
||||
},
|
||||
{
|
||||
"file": "apps/modules/trust.py",
|
||||
"standard": "encapsulation",
|
||||
"reason": "Imports frozen trust_registry interface (enroll/revoke/is_trusted/read_registry) from @hooks by DPLAN-0244 design. Cross-branch import required — the registry module lives in hooks, consumers live in aipass."
|
||||
},
|
||||
{
|
||||
"file": "apps/modules/trust.py",
|
||||
"standard": "json_structure",
|
||||
"reason": "No JSON file operations — trust.py is a thin CLI wrapper that delegates all JSON I/O to the trust_registry module in @hooks. No json_handler needed."
|
||||
},
|
||||
{
|
||||
"file": "apps/modules/trust.py",
|
||||
"standard": "introspection",
|
||||
"reason": "aipass is binary-invoked: bare 'aipass trust' shows registry table; introspection via --info"
|
||||
},
|
||||
{
|
||||
"file": "apps/handlers/init/bootstrap.py",
|
||||
"standard": "encapsulation",
|
||||
"reason": "Imports enroll() from @hooks trust_registry (DPLAN-0244 frozen interface). Cross-branch import required — init must enroll projects in the trust registry after writing hooks.json."
|
||||
},
|
||||
{
|
||||
"file": "apps/handlers/init/bootstrap.py",
|
||||
"standard": "handlers",
|
||||
"reason": "Imports enroll() from @hooks trust_registry by DPLAN-0244 design. Cross-handler import required — bootstrap auto-enrolls projects after hooks.json creation/merge."
|
||||
},
|
||||
{
|
||||
"file": "tests/test_trust.py",
|
||||
"standard": "architecture",
|
||||
"reason": "Test file lives in tests/ by convention — not in apps/. Standard 3-layer structure applies to production code only."
|
||||
},
|
||||
{
|
||||
"file": "tests/test_trust.py",
|
||||
"standard": "encapsulation",
|
||||
"reason": "Tests import trust_registry directly to verify enrollment/revocation in isolation with monkeypatched REGISTRY_PATH."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ aipass/
|
||||
│ │ ├── help_chat.py # README-backed Q&A (reads via readme_map handler)
|
||||
│ │ ├── init_flow.py # 10-stage guided setup
|
||||
│ │ ├── install.py # aipass install — one-command bootstrap (clone + setup + init)
|
||||
│ │ └── profile.py # User profile read/write
|
||||
│ │ ├── profile.py # User profile read/write
|
||||
│ │ └── trust.py # Trust registry — aipass trust / aipass revoke
|
||||
│ ├── handlers/
|
||||
│ │ ├── cross_os/ # Cross-OS pre-flight: gap_registry, preflight, run_record
|
||||
│ │ ├── handoff_platform/ # Platform-specific handoff detection
|
||||
@@ -56,6 +57,8 @@ aipass/
|
||||
| `aipass init` | 10-stage guided setup (resumable) |
|
||||
| `aipass install` | One-command bootstrap — clone + setup.sh + hooks, then hand off to init (`--no-init`/`--with-init`/`--path`/`--here`) |
|
||||
| `aipass profile` | Show/edit user profile |
|
||||
| `aipass trust [path]` | Show enrolled projects or enroll a project in the trust registry |
|
||||
| `aipass revoke <path>` | Remove a project from the trust registry |
|
||||
| `aipass --version` | Version |
|
||||
|
||||
## Integration Points
|
||||
|
||||
@@ -19,27 +19,61 @@ from __future__ import annotations
|
||||
from pathlib import Path
|
||||
|
||||
from aipass.cli.apps.modules import console, error, success
|
||||
from aipass.hooks.apps.handlers.config.trust_registry import enroll, revoke
|
||||
from aipass.hooks.apps.handlers.config.trust_registry import (
|
||||
enroll,
|
||||
read_registry,
|
||||
revoke,
|
||||
)
|
||||
from aipass.prax import logger
|
||||
|
||||
COMMAND = "trust"
|
||||
_COMMAND_REVOKE = "revoke"
|
||||
|
||||
|
||||
def _print_help() -> None:
|
||||
def print_introspection() -> None:
|
||||
"""Display the current trusted-project registry."""
|
||||
from rich.table import Table
|
||||
|
||||
registry = read_registry()
|
||||
projects = registry.get("projects", {})
|
||||
|
||||
console.print()
|
||||
console.print("[bold cyan]aipass trust[/bold cyan] — trusted-project registry")
|
||||
console.print()
|
||||
|
||||
if not projects:
|
||||
console.print("[dim]No projects enrolled.[/dim]")
|
||||
else:
|
||||
table = Table(show_header=True, header_style="bold yellow")
|
||||
table.add_column("Project", style="cyan")
|
||||
table.add_column("Hash", style="dim", max_width=24)
|
||||
table.add_column("Enrolled")
|
||||
for path, entry in projects.items():
|
||||
short_hash = entry.get("config_hash", "")[:18] + "..."
|
||||
table.add_row(path, short_hash, entry.get("enrolled", ""))
|
||||
console.print(table)
|
||||
|
||||
console.print()
|
||||
console.print("[dim]Use 'aipass trust <path>' to enroll or 'aipass revoke <path>' to remove.[/dim]")
|
||||
console.print()
|
||||
|
||||
|
||||
def print_help() -> None:
|
||||
"""Print usage help for the trust/revoke commands."""
|
||||
console.print()
|
||||
console.print("[bold cyan]aipass trust / revoke[/bold cyan] — trusted-project registry")
|
||||
console.print()
|
||||
console.print("[yellow]USAGE:[/yellow]")
|
||||
console.print(" [green]aipass trust <path>[/green] [dim]# Enroll a project (requires .aipass/hooks.json)[/dim]")
|
||||
console.print(" [green]aipass revoke <path>[/green] [dim]# Remove a project from the registry[/dim]")
|
||||
console.print(" [green]aipass trust[/green] [dim]# Show enrolled projects[/dim]")
|
||||
console.print(
|
||||
" [green]aipass trust <path>[/green] [dim]# Enroll a project (requires .aipass/hooks.json)[/dim]"
|
||||
)
|
||||
console.print(" [green]aipass revoke <path>[/green] [dim]# Remove a project from the registry[/dim]")
|
||||
console.print()
|
||||
|
||||
|
||||
def _handle_trust(args: list[str]) -> bool:
|
||||
if not args or args[0] in ("--help", "-h"):
|
||||
_print_help()
|
||||
return True
|
||||
def _do_trust(args: list[str]) -> bool:
|
||||
"""Execute the trust enrollment for a given path."""
|
||||
target = Path(args[0]).resolve()
|
||||
if not target.is_dir():
|
||||
error(f"Not a directory: {target}")
|
||||
@@ -56,10 +90,8 @@ def _handle_trust(args: list[str]) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def _handle_revoke(args: list[str]) -> bool:
|
||||
if not args or args[0] in ("--help", "-h"):
|
||||
_print_help()
|
||||
return True
|
||||
def _do_revoke(args: list[str]) -> bool:
|
||||
"""Execute the revocation for a given path."""
|
||||
target = Path(args[0]).resolve()
|
||||
if revoke(str(target)):
|
||||
success(f"Revoked {target}")
|
||||
@@ -72,7 +104,19 @@ def _handle_revoke(args: list[str]) -> bool:
|
||||
def handle_command(command: str, args: list[str]) -> bool:
|
||||
"""Route trust/revoke subcommands. Returns True if handled."""
|
||||
if command == COMMAND:
|
||||
return _handle_trust(args)
|
||||
if not args:
|
||||
print_introspection()
|
||||
return True
|
||||
if args[0] in ("--help", "-h", "help"):
|
||||
print_help()
|
||||
return True
|
||||
if args[0] == "--info":
|
||||
print_introspection()
|
||||
return True
|
||||
return _do_trust(args)
|
||||
if command == _COMMAND_REVOKE:
|
||||
return _handle_revoke(args)
|
||||
if not args or args[0] in ("--help", "-h", "help"):
|
||||
print_help()
|
||||
return True
|
||||
return _do_revoke(args)
|
||||
return False
|
||||
|
||||
@@ -406,6 +406,11 @@
|
||||
"standard": "json_structure",
|
||||
"reason": "Sound handler \u2014 no JSON operations, plays WAV files."
|
||||
},
|
||||
{
|
||||
"file": "apps/handlers/config/trust_registry.py",
|
||||
"standard": "unused_function",
|
||||
"reason": "enroll() and revoke() are the public API consumed CROSS-BRANCH by @aipass CLI (init/trust/revoke commands, DPLAN-0244 phase 2). seedgo's intra-branch static analysis cannot see cross-branch callers. read_registry() also exported for @aipass CLI use."
|
||||
},
|
||||
{
|
||||
"file": "apps/handlers/config/loader.py",
|
||||
"standard": "json_structure",
|
||||
|
||||
@@ -21,6 +21,7 @@ import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from aipass.hooks.apps.handlers.json import json_handler
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
|
||||
REGISTRY_PATH = Path.home() / ".aipass" / "trusted_projects.json"
|
||||
@@ -37,7 +38,7 @@ def read_registry() -> dict:
|
||||
if not REGISTRY_PATH.exists():
|
||||
return {"version": 1, "projects": {}}
|
||||
try:
|
||||
data = json.loads(REGISTRY_PATH.read_text(encoding="utf-8"))
|
||||
data = json_handler.read_json_file(REGISTRY_PATH)
|
||||
if not isinstance(data.get("projects"), dict):
|
||||
return {"version": 1, "projects": {}}
|
||||
return data
|
||||
@@ -49,10 +50,7 @@ def read_registry() -> dict:
|
||||
def _write_registry(registry: dict) -> None:
|
||||
"""Write the registry to disk, creating parent dirs if needed."""
|
||||
REGISTRY_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
REGISTRY_PATH.write_text(
|
||||
json.dumps(registry, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
json_handler.write_json_file(REGISTRY_PATH, registry)
|
||||
|
||||
|
||||
def enroll(project_dir: str) -> bool:
|
||||
@@ -70,6 +68,7 @@ def enroll(project_dir: str) -> bool:
|
||||
"config_path": str(config_path),
|
||||
}
|
||||
_write_registry(registry)
|
||||
json_handler.log_operation("enroll", {"project": str(project_path)}, module_name="trust_registry")
|
||||
logger.info("[HOOKS] enrolled %s (hash=%s)", project_path, config_hash)
|
||||
return True
|
||||
|
||||
@@ -82,6 +81,7 @@ def revoke(project_dir: str) -> bool:
|
||||
return False
|
||||
del registry["projects"][project_path]
|
||||
_write_registry(registry)
|
||||
json_handler.log_operation("revoke", {"project": project_path}, module_name="trust_registry")
|
||||
logger.info("[HOOKS] revoked %s", project_path)
|
||||
return True
|
||||
|
||||
@@ -126,6 +126,7 @@ def bootstrap() -> bool:
|
||||
"config_path": str(config_path),
|
||||
}
|
||||
_write_registry(registry)
|
||||
json_handler.log_operation("bootstrap", {"aipass_home": str(aipass_path)}, module_name="trust_registry")
|
||||
logger.info("[HOOKS] registry bootstrapped, enrolled AIPass install: %s", aipass_path)
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user