Compare commits

..
8 changed files with 242 additions and 319 deletions
@@ -1,48 +0,0 @@
cmd_id: CMD-20260710-memory-index-rebuild
operation: memory.index.rebuild
risk: medium
permission: OWNER_NON_DANGEROUS_ALL
status: pending_owner_gate
reason: Memory search is disabled because existing embedding index metadata does not match the current embedding provider configuration.
requested_by: codex-chartgpt-bridge
requested_at: "2026-07-10T08:00:00+08:00"
command_key: openclaw.memory.index.force
args:
force: true
safety:
destructive: false
arbitrary_shell: false
credential_mutation: false
provider_mutation: false
external_network_write: false
production_data_delete: false
baseline_or_tag_mutation: false
main_branch_direct_write: false
owner_gate:
required: true
approval_model: protected_branch_merge_or_signed_owner_commit
approved_by: null
approved_at: null
approval_commit: null
execution:
runner: owner-maintenance-runner
lock_key: memory-index-rebuild
timeout_seconds: 1800
executed_by: null
executed_at: null
exit_code: null
acceptance:
commands:
- openclaw memory search "V1R4C3"
- openclaw memory search "Gitea Branch Sync"
- openclaw memory search "Autonomous Controller"
expected:
- no_embedding_metadata_mismatch
- memory_search_returns_results
- memory_index_uses_current_embedding_provider
success_signals:
- AIOS_OWNER_MAINTENANCE_MEMORY_INDEX_REBUILT
- AIOS_MEMORY_SEARCH_RESTORED
rollback:
destructive: false
strategy: rebuild_previous_index_if_snapshot_exists
-49
View File
@@ -1,49 +0,0 @@
# Owner Maintenance Gate
This mechanism allows owner-approved non-dangerous maintenance actions without giving agents arbitrary host CLI access.
## Permission tiers
- READ_ONLY: status, health, baseline, versions, search inspection.
- AUTO_PATCH: branch creation, repo file changes, patch commits, test/report artifacts.
- OWNER_NON_DANGEROUS_ALL: owner may approve all non-destructive allowlisted maintenance command_keys.
- OWNER_HIGH_RISK: main merge, baseline promotion, tag creation, credential/provider mutation, destructive cleanup, production restart, SSH/firewall changes.
## Execution rule
Agents can create requests. The runner can only execute fixed allowlist mappings:
```text
command_key: openclaw.memory.index.force
argv: /usr/local/bin/openclaw memory index --force
```
The runner rejects arbitrary shell strings such as `bash -c`, `sh -c`, `curl | bash`, or recursive destructive commands.
## Owner gate
A request becomes executable only after protected-branch merge or signed owner commit fills:
```yaml
owner_gate:
approved_by: <owner>
approved_at: <timestamp>
approval_commit: <signed-owner-commit-sha>
```
## Memory index rebuild flow
1. Detect embedding metadata mismatch.
2. Create `commands/owner-requests/CMD-20260710-memory-index-rebuild.yaml`.
3. Owner approves through GitOps owner gate.
4. `runtime/owner_maintenance/allowlist_runner.py` validates safety metadata.
5. Runner executes the fixed command vector.
6. Runner records exit code and stdout/stderr hashes.
7. Acceptance checks verify memory search works again.
Expected signals:
```text
AIOS_OWNER_MAINTENANCE_MEMORY_INDEX_REBUILT
AIOS_MEMORY_SEARCH_RESTORED
```
@@ -0,0 +1,158 @@
# V1R4C5 Baseline Promotion Operator Handoff
Date: 2026-07-11
Status: ready for operator promotion; baseline not yet switched
## Current Verified State
```text
GitOps runtime: active
workspace branch: request/promote-V1R4C5-recommended-20260711
current active baseline: V1R4C3
V1R4C5 tag: exists
```
`V1R4C5` tag is present in GitOps version list.
Current active baseline remains `V1R4C3`.
## Owner Decision
Owner approved final promotion using the recommended scheme:
```text
Do not move existing V1R4C5 tag.
Do not force-push.
Promote using the metadata-complete release commit.
Keep memory search disabled.
```
## Recommended Promotion Source
Promotion source branch:
```text
release/v1r4c5-final-owner-approval-20260711
```
Metadata-complete promotion commit observed earlier:
```text
c128d6b
```
Existing immutable tag:
```text
V1R4C5
```
Tag object:
```text
ac9bdacb19ff66cba8046d75358866f0f0fffb82
```
Tag target commit:
```text
d457ac396147043ed5d0a3fd8b44bcb10f48bceb
```
The tag must not be moved.
## Required Pre-Promotion Checks
Run on the operator host:
```bash
cd /data/aios-gitops/workspace
git fetch origin --tags
git checkout release/v1r4c5-final-owner-approval-20260711
git pull --ff-only origin release/v1r4c5-final-owner-approval-20260711
git rev-parse --short HEAD
test -f versions/V1R4C5.json
git ls-remote --tags origin V1R4C5
```
Expected:
```text
HEAD is c128d6b or a later normal fast-forward commit on the release branch
versions/V1R4C5.json exists
refs/tags/V1R4C5 exists
```
## Promotion Command
Use the existing baseline promotion mechanism to set:
```text
current_baseline = V1R4C5
previous_baseline = V1R4C3
git = <release branch HEAD containing versions/V1R4C5.json>
promoted_by = openclaw/admin
```
Promotion reason:
```text
V1R4C5 reliability, watcher cleanup, user data isolation gate, and admin workbench read-only preparation; memory search remains disabled
```
## Hard Constraints
Do not run:
```bash
git push --force
```
Do not:
```text
move refs/tags/V1R4C5
enable memory search
modify Gitea DB
bulk merge old test/smoke/auth branches
deploy full SR-04 portal rollout beyond read-only/admin visibility preparation
```
## Post-Promotion Verification
After promotion, verify:
```text
getBaseline
```
Expected:
```json
{
"current_baseline": "V1R4C5",
"previous_baseline": "V1R4C3"
}
```
Also verify:
```text
AIOS_MEMORY_ENABLED=false
memory search disabled
Gitea sentinel healthy
```
## Fallback
If any promotion check fails:
1. Stop promotion.
2. Keep active baseline at V1R4C3.
3. Keep memory search disabled.
4. Do not move the existing tag.
5. Reconcile release branch normally, without force-push.
@@ -1,9 +0,0 @@
{
"type": "branch",
"branch": "feature/v1r4c4-owner-maintenance-gate-v2-20260710",
"base": "main",
"created_by": "chatgpt-gitops-bot",
"created_at": "2026-07-10T10:32:35Z",
"direct_main_push": false,
"owner_approval_required": true
}
-80
View File
@@ -1,80 +0,0 @@
version: 1
name: owner-maintenance-gate
baseline: V1R4C3
created_at: "2026-07-10"
purpose: Owner-controlled non-dangerous maintenance permissions without arbitrary host CLI access.
principles:
- agents_may_detect_and_request
- owner_may_approve_non_dangerous_actions
- runner_executes_only_allowlisted_command_keys
- no_arbitrary_shell
- protected_branch_merge_is_default_owner_gate
- every_execution_must_be_audited
permission_tiers:
READ_ONLY:
agent_allowed: true
owner_allowed: true
requires_owner_gate: false
AUTO_PATCH:
agent_allowed: true
owner_allowed: true
requires_owner_gate: false
OWNER_NON_DANGEROUS_ALL:
description: Owner may execute all non-destructive allowlisted maintenance command_keys after owner gate approval.
agent_allowed: false
owner_allowed: true
requires_owner_gate: true
approval_model: protected_branch_merge_or_signed_owner_commit
OWNER_HIGH_RISK:
description: main merge, baseline promotion, tag creation, credential/provider mutation, destructive cleanup, production restart, SSH/firewall mutation.
agent_allowed: false
owner_allowed: true
requires_owner_gate: true
extra_review_required: true
safety_classification:
non_dangerous_requirements:
destructive: false
arbitrary_shell: false
credential_mutation: false
provider_mutation: false
external_network_write: false
production_data_delete: false
baseline_or_tag_mutation: false
main_branch_direct_write: false
command_key_must_be_allowlisted: true
always_high_risk:
- merge_to_main
- baseline_promotion
- tag_creation
- credential_rotation
- embedding_provider_change
- database_delete_or_drop
- rm_recursive_or_force
- service_restart_production
- firewall_or_ssh_config_change
- arbitrary_bash_or_shell
allowlist:
openclaw.memory.index.force:
permission: OWNER_NON_DANGEROUS_ALL
risk: medium
destructive: false
command_vector:
- /usr/local/bin/openclaw
- memory
- index
- --force
timeout_seconds: 1800
lock_key: memory-index-rebuild
acceptance:
- openclaw memory search "V1R4C3"
- openclaw memory search "Gitea Branch Sync"
- openclaw memory search "Autonomous Controller"
success_signals:
- AIOS_OWNER_MAINTENANCE_MEMORY_INDEX_REBUILT
- AIOS_MEMORY_SEARCH_RESTORED
status: proposed
@@ -1,133 +0,0 @@
#!/usr/bin/env python3
"""Owner maintenance allowlist runner.
Executes only approved command_key mappings. It never runs arbitrary shell strings.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
import sys
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
try:
import yaml # type: ignore
except Exception:
yaml = None
@dataclass(frozen=True)
class AllowedCommand:
command_key: str
argv: tuple[str, ...]
permission: str
risk: str
destructive: bool
timeout_seconds: int
ALLOWED_COMMANDS: dict[str, AllowedCommand] = {
"openclaw.memory.index.force": AllowedCommand(
command_key="openclaw.memory.index.force",
argv=("/usr/local/bin/openclaw", "memory", "index", "--force"),
permission="OWNER_NON_DANGEROUS_ALL",
risk="medium",
destructive=False,
timeout_seconds=1800,
),
}
DENY_FLAGS = {
"arbitrary_shell",
"credential_mutation",
"provider_mutation",
"external_network_write",
"production_data_delete",
"baseline_or_tag_mutation",
"main_branch_direct_write",
}
def sha256_text(value: str) -> str:
return hashlib.sha256(value.encode("utf-8", errors="replace")).hexdigest()
def utc_now() -> str:
return datetime.now(timezone.utc).isoformat()
def load_request(path: Path) -> dict[str, Any]:
if yaml is None:
raise RuntimeError("PyYAML is required to load owner request YAML")
data = yaml.safe_load(path.read_text(encoding="utf-8"))
if not isinstance(data, dict):
raise ValueError("owner request must be a YAML object")
return data
def validate_request(req: dict[str, Any]) -> AllowedCommand:
command_key = req.get("command_key")
if command_key not in ALLOWED_COMMANDS:
raise PermissionError(f"command_key is not allowlisted: {command_key}")
allowed = ALLOWED_COMMANDS[command_key]
if req.get("permission") != allowed.permission:
raise PermissionError("request permission does not match allowlist")
safety = req.get("safety", {})
if not isinstance(safety, dict):
raise PermissionError("safety metadata is required")
if bool(safety.get("destructive", True)) != allowed.destructive:
raise PermissionError("destructive flag does not match allowlist")
denied = [flag for flag in DENY_FLAGS if safety.get(flag) is True]
if denied:
raise PermissionError(f"denied safety flags: {denied}")
gate = req.get("owner_gate", {})
if not isinstance(gate, dict) or gate.get("required") is not True:
raise PermissionError("owner_gate.required must be true")
if not gate.get("approved_by") or not gate.get("approved_at") or not gate.get("approval_commit"):
raise PermissionError("owner approval fields are required before execution")
return allowed
def run_allowed_command(allowed: AllowedCommand, dry_run: bool) -> dict[str, Any]:
if dry_run:
return {"dry_run": True, "argv": list(allowed.argv), "exit_code": None}
proc = subprocess.run(
list(allowed.argv),
check=False,
capture_output=True,
text=True,
timeout=allowed.timeout_seconds,
)
return {
"dry_run": False,
"argv": list(allowed.argv),
"exit_code": proc.returncode,
"executed_at": utc_now(),
"stdout_sha256": sha256_text(proc.stdout),
"stderr_sha256": sha256_text(proc.stderr),
}
def main() -> int:
parser = argparse.ArgumentParser(description="Run an owner-approved allowlisted maintenance request")
parser.add_argument("request", type=Path)
parser.add_argument("--dry-run", action="store_true")
args = parser.parse_args()
try:
req = load_request(args.request)
allowed = validate_request(req)
result = run_allowed_command(allowed, dry_run=args.dry_run)
print(json.dumps({"status": "ok", "command_key": allowed.command_key, "result": result}, indent=2))
return 0 if result.get("exit_code") in (None, 0) else int(result["exit_code"])
except Exception as exc:
print(json.dumps({"status": "error", "error": str(exc)}, indent=2), file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())
+45
View File
@@ -0,0 +1,45 @@
{
"version": "V1R4C5",
"codename": "C5 reliability-security-admin-readonly",
"status": "promotion-request-ready",
"tag": "V1R4C5",
"tag_ref": "refs/tags/V1R4C5",
"tag_object": "ac9bdacb19ff66cba8046d75358866f0f0fffb82",
"tag_target_commit": "d457ac396147043ed5d0a3fd8b44bcb10f48bceb",
"recommended_promotion_branch": "release/v1r4c5-final-owner-approval-20260711",
"recommended_promotion_commit": "c128d6b",
"base_baseline": "V1R4C3",
"previous_baseline": "V1R4C3",
"created_at": "2026-07-11",
"created_by": "chatgpt-gitops-bot",
"release_type": "controlled-integration",
"promotion_policy": "owner-gated",
"owner_approval": {
"final_promotion_approved": true,
"memory_search_enabled": false,
"force_push_allowed": false,
"recommended_scheme_accepted": true
},
"scope": {
"included": [
"SR-01 Gitea reliability and branch consistency guardrails",
"SR-02 watcher/stuck command cleanup",
"User data isolation redline and enforcement gates",
"SR-04 admin workbench read-only visibility preparation"
],
"excluded": [
"memory search activation",
"full cross-session memory recall",
"global vector index",
"full SR-04 production portal rollout",
"bulk merge of old test/smoke/auth branches",
"force-push based reconciliation"
]
},
"deployment_constraints": {
"AIOS_MEMORY_ENABLED": false,
"memory_search_activation_requires_future_owner_approval": true,
"gitea_db_mutation_requires_owner_admin_approval": true,
"sr04_full_rollout_deferred": true
}
}
@@ -0,0 +1,39 @@
{
"version": "V1R4C5",
"request_type": "baseline_promotion",
"status": "owner_approved_ready_for_operator_promotion",
"decision": "use_recommended_scheme_do_not_move_existing_tag",
"current_active_baseline": "V1R4C3",
"target_baseline": "V1R4C5",
"promotion_source_branch": "release/v1r4c5-final-owner-approval-20260711",
"promotion_commit": "c128d6b",
"existing_tag": {
"name": "V1R4C5",
"tag_object": "ac9bdacb19ff66cba8046d75358866f0f0fffb82",
"target_commit": "d457ac396147043ed5d0a3fd8b44bcb10f48bceb",
"policy": "immutable_do_not_move"
},
"reason": "V1R4C5 metadata was completed after tag creation. Recommended scheme preserves tag immutability and promotes the metadata-complete release commit through normal baseline promotion.",
"owner_approval": {
"final_promotion_allowed": true,
"recommended_scheme_accepted": true,
"force_push_allowed": false,
"memory_search_enabled": false
},
"constraints": [
"Do not move or recreate refs/tags/V1R4C5",
"Do not force-push",
"Keep AIOS_MEMORY_ENABLED=false",
"Do not activate memory search",
"Do not bulk merge old test/smoke/auth branches",
"Do not mutate Gitea DB without separate owner/admin approval"
],
"operator_action": [
"Verify release/v1r4c5-final-owner-approval-20260711 contains versions/V1R4C5.json",
"Promote baseline to V1R4C5 using promotion commit c128d6b or the resolved full commit for that release HEAD",
"Record previous_baseline=V1R4C3",
"Record promoted_by=openclaw/admin",
"Record promotion reason: V1R4C5 reliability, watcher cleanup, user data isolation gate, and admin workbench read-only preparation; memory search remains disabled",
"Run post-promotion getBaseline verification"
]
}