Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2dcc3b372 | ||
|
|
602cc9ee28 | ||
|
|
6dbd5cbe07 | ||
|
|
a56caf46f6 | ||
|
|
05025480ad |
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"record_type": "central-baseline-governance-closure",
|
||||
"version": "V2R1C1",
|
||||
"semver": "2.1.0-rc.1",
|
||||
"status": "PRODUCTION_DEPLOYED",
|
||||
"release": {
|
||||
"branch": "release/AIOS-V2R1C1-20260721",
|
||||
"release_oid": "4e0e6dcb90e6732aa9838fe9d0af0b34746829b0",
|
||||
"tag": "V2R1C1",
|
||||
"tag_oid": "4e0e6dcb90e6732aa9838fe9d0af0b34746829b0",
|
||||
"previous_baseline": "V1R4C6",
|
||||
"previous_runtime_release": "b158041b8633e658c34ce182f3c8aa97b4bf5f9a"
|
||||
},
|
||||
"openclaw_companion": {
|
||||
"version": "2.1.0-rc.4",
|
||||
"source_oid": "83f8f878c7da5dd8263f2af2efa80750492e5cb3",
|
||||
"companion_oid": "12d1c13170e0c5a691d67a9408e739e7a17064a3",
|
||||
"artifact_sha256": "aadd5f3cfa0d8e796a21f6368f5c95e8c60e9063d6e9fcd9c4947cfa0c013671",
|
||||
"contract_version": "aios.openclaw.command.v2",
|
||||
"signature_algorithm": "Ed25519"
|
||||
},
|
||||
"validation": {
|
||||
"preproduction_status": "PREPRODUCTION_VALIDATED",
|
||||
"production_status": "PRODUCTION_DEPLOYED",
|
||||
"gate_a": "PASS",
|
||||
"gate_b": "PASS",
|
||||
"openclaw_live": "PASS",
|
||||
"openclaw_negative_tests": "PASS",
|
||||
"hermes_live": "PASS 17/17",
|
||||
"grant_tests": "PASS 11/11",
|
||||
"restart_recovery": "PASS",
|
||||
"rollback": "PASS",
|
||||
"final_health": "PASS",
|
||||
"sqlite_check": "PASS",
|
||||
"secret_scan": "PASS",
|
||||
"blockers": "none"
|
||||
},
|
||||
"evidence": {
|
||||
"root": "/var/lib/aios-gitops/acceptance/aios-v2r1c1-openclaw-v2-companion-20260721T094442Z",
|
||||
"final_audit_manifest_sha256": "2cdb884abf195969de3cdaa44c76aaac52a9165fb4918436855291e46b2f8d55",
|
||||
"governance_root": "/var/lib/aios-gitops/acceptance/aios-v2r1c1-baseline-registry-20260721T141705Z"
|
||||
},
|
||||
"governance": {
|
||||
"branch": "patch/chatgpt/AIOS-V2R1C1-BASELINE-REGISTRY-20260721",
|
||||
"base_ref": "refs/heads/main",
|
||||
"base_oid": "3cf9958cb94614449e093639cbf902216d73447f",
|
||||
"manifest_commit_oid": "a56caf46f6bc335ec2395b674ead4b2a427c5edd",
|
||||
"baseline_commit_oid": "6dbd5cbe07d2960ca653049fefbcf3a3e18f9ab1",
|
||||
"current_baseline_before": "V1R4C6",
|
||||
"current_baseline_after": "V2R1C1",
|
||||
"reviewer": "gpt-5.3-codex-spark validator and docs_reporter",
|
||||
"review_status": "PASS",
|
||||
"reviewed_at": "2026-07-21T14:23:00Z"
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "GET https://cnnetmkt.top/api/run; GET /api/base; GET /api/ver",
|
||||
"start_time": "2026-07-21T14:09:40Z",
|
||||
"end_time": "2026-07-21T14:09:40Z",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"command": "bash /tmp/registry-create-branch.sh",
|
||||
"start_time": "2026-07-21T14:17:05Z",
|
||||
"end_time": "2026-07-21T14:17:05Z",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"command": "bash /tmp/registry-validate-core.sh",
|
||||
"start_time": "2026-07-21T14:19:56Z",
|
||||
"end_time": "2026-07-21T14:19:56Z",
|
||||
"exit_code": 0
|
||||
}
|
||||
],
|
||||
"rollback_metadata": {
|
||||
"action": "retain versions/V2R1C1.json and restore only baseline/current.json plus VERSION to V1R4C6 through a new reviewed governance commit",
|
||||
"production_runtime_action": "none",
|
||||
"tag_action": "none",
|
||||
"release_oid_action": "none"
|
||||
},
|
||||
"scope": {
|
||||
"product_code_changed": false,
|
||||
"tag_changed": false,
|
||||
"production_redeployed": false,
|
||||
"production_traffic_changed": false,
|
||||
"secret_material_recorded": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# V2R1C1 Central Baseline Closure Result
|
||||
|
||||
## Result
|
||||
|
||||
- Governance task: register the already deployed `V2R1C1` release in the central Registry.
|
||||
- Baseline: `V1R4C6` -> `V2R1C1`.
|
||||
- Release OID and tag OID: `4e0e6dcb90e6732aa9838fe9d0af0b34746829b0`.
|
||||
- Companion OID: `12d1c13170e0c5a691d67a9408e739e7a17064a3`.
|
||||
- Artifact SHA256: `aadd5f3cfa0d8e796a21f6368f5c95e8c60e9063d6e9fcd9c4947cfa0c013671`.
|
||||
- Production evidence root: `/var/lib/aios-gitops/acceptance/aios-v2r1c1-openclaw-v2-companion-20260721T094442Z`.
|
||||
- Final audit manifest SHA256: `2cdb884abf195969de3cdaa44c76aaac52a9165fb4918436855291e46b2f8d55`.
|
||||
- Production status: `PRODUCTION_DEPLOYED`.
|
||||
|
||||
## Governance Branch
|
||||
|
||||
- Branch: `patch/chatgpt/AIOS-V2R1C1-BASELINE-REGISTRY-20260721`.
|
||||
- Base ref: `refs/heads/main`.
|
||||
- Base OID: `3cf9958cb94614449e093639cbf902216d73447f`.
|
||||
- Manifest commit: `a56caf46f6bc335ec2395b674ead4b2a427c5edd`.
|
||||
- Baseline commit: `6dbd5cbe07d2960ca653049fefbcf3a3e18f9ab1`.
|
||||
- Review: `PASS` at `2026-07-21T14:23:00Z` by `gpt-5.3-codex-spark` validator and docs reporter.
|
||||
|
||||
## Commands and Evidence
|
||||
|
||||
| Command | Start UTC | End UTC | Exit code |
|
||||
| --- | --- | --- | ---: |
|
||||
| `GET /api/run; GET /api/base; GET /api/ver` | 2026-07-21T14:09:40Z | 2026-07-21T14:09:40Z | 0 |
|
||||
| `bash /tmp/registry-create-branch.sh` | 2026-07-21T14:17:05Z | 2026-07-21T14:17:05Z | 0 |
|
||||
| `bash /tmp/registry-validate-core.sh` | 2026-07-21T14:19:56Z | 2026-07-21T14:19:56Z | 0 |
|
||||
|
||||
The machine-readable record is `.agent/results/releases/V2R1C1-BASELINE-CLOSURE.json`. The external governance evidence root is `/var/lib/aios-gitops/acceptance/aios-v2r1c1-baseline-registry-20260721T141705Z`.
|
||||
|
||||
## Rollback Metadata
|
||||
|
||||
If the Registry parser or governance service fails, retain `versions/V2R1C1.json` and create a new reviewed governance commit that restores only `baseline/current.json` and `VERSION` to `V1R4C6`. Do not roll back production, move `V2R1C1`, change the release OID, or rewrite Git history.
|
||||
|
||||
## Scope Protection
|
||||
|
||||
- `product_code_changed=false`
|
||||
- `tag_changed=false`
|
||||
- `production_redeployed=false`
|
||||
- `production_traffic_changed=false`
|
||||
- `secret_material_recorded=false`
|
||||
@@ -7,6 +7,7 @@ from typing import Any
|
||||
|
||||
sys.path.insert(0, str(Path(os.getenv('AIOS_GITOPS_WORKSPACE', '/data/aios-gitops/workspace')) / 'runtime' / 'agent-os'))
|
||||
from orchestrator.action_monitor import ActionMonitor
|
||||
import threading
|
||||
|
||||
ROOT = Path(os.getenv('AIOS_GITOPS_ROOT', '/data/aios-gitops'))
|
||||
WS = Path(os.getenv('AIOS_GITOPS_WORKSPACE', str(ROOT / 'workspace')))
|
||||
@@ -17,6 +18,7 @@ PORT = int(os.getenv('AIOS_GITOPS_PORT', '18890'))
|
||||
OPENAPI = WS / 'docs' / 'gitops' / 'openapi-chatgpt-actions.yaml'
|
||||
BARE_REPO = Path(os.getenv('AIOS_GITOPS_BARE_REPO', str(ROOT / 'runtime' / 'gitea-bin' / 'data' / 'repositories' / 'aios' / 'aios.git')))
|
||||
monitor = ActionMonitor()
|
||||
WRITE_LOCK = threading.RLock()
|
||||
|
||||
BASE_MODELS = [
|
||||
{'id':'base.deepseekv4-flash','provider':'one-api','model':'deepseekv4-flash','display_name':'DeepSeek V4 Flash','scope':'global'},
|
||||
@@ -117,28 +119,211 @@ def write_file(path, content):
|
||||
if content_has_secret(content): raise ValueError('secret_rejected: use secret_ref; plaintext API keys must not enter Git')
|
||||
r=relpath(path); t=WS/r; t.parent.mkdir(parents=True, exist_ok=True); t.write_text(content, encoding='utf-8'); return r
|
||||
def write_json(path, data): return write_file(path, json.dumps(data, ensure_ascii=False, indent=2) + '\n')
|
||||
def controller_status() -> dict:
|
||||
branch = sh(
|
||||
['branch', '--show-current']
|
||||
)['stdout'] or None
|
||||
|
||||
head = sh(
|
||||
['rev-parse', 'HEAD'],
|
||||
True,
|
||||
)['stdout']
|
||||
|
||||
dirty = bool(
|
||||
sh(
|
||||
['status', '--porcelain']
|
||||
)['stdout']
|
||||
)
|
||||
|
||||
return {
|
||||
'workspace_mode': (
|
||||
'branch'
|
||||
if branch
|
||||
else 'detached'
|
||||
),
|
||||
'workspace_branch': branch,
|
||||
'workspace_ref': head,
|
||||
'workspace_dirty': dirty,
|
||||
}
|
||||
|
||||
|
||||
def detach_controller() -> dict:
|
||||
state = controller_status()
|
||||
|
||||
if state['workspace_branch']:
|
||||
sh(
|
||||
[
|
||||
'switch',
|
||||
'--detach',
|
||||
'HEAD',
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
return controller_status()
|
||||
|
||||
|
||||
def sync_branch(branch: str) -> dict:
|
||||
if not branch or branch in ('main','master') or branch.startswith('main/') or branch.startswith('master/'):
|
||||
return {'status':'skipped','reason':'protected_branch','branch':branch}
|
||||
if (
|
||||
not branch
|
||||
or branch in ('main', 'master')
|
||||
or branch.startswith('main/')
|
||||
or branch.startswith('master/')
|
||||
):
|
||||
return {
|
||||
'status': 'skipped',
|
||||
'reason': 'protected_branch',
|
||||
'branch': branch,
|
||||
}
|
||||
|
||||
state = detach_controller()
|
||||
|
||||
common_git_dir = Path(
|
||||
sh(
|
||||
[
|
||||
'rev-parse',
|
||||
'--path-format=absolute',
|
||||
'--git-common-dir',
|
||||
],
|
||||
True,
|
||||
)['stdout']
|
||||
).resolve()
|
||||
|
||||
if BARE_REPO.exists():
|
||||
ref=f'refs/heads/{branch}:refs/heads/{branch}'
|
||||
p=subprocess.run(['git','--git-dir',str(BARE_REPO),'fetch',str(WS),ref], text=True, capture_output=True)
|
||||
if p.returncode != 0: raise RuntimeError(p.stderr or p.stdout or f'git sync failed for {branch}')
|
||||
return {'status':'synced','target':'bare','branch':branch,'stdout':p.stdout.strip(),'stderr':p.stderr.strip()}
|
||||
p=subprocess.run(['git','push','origin',f'HEAD:refs/heads/{branch}'], cwd=str(WS), text=True, capture_output=True)
|
||||
if p.returncode != 0: raise RuntimeError(p.stderr or p.stdout or f'git push failed for {branch}')
|
||||
return {'status':'synced','target':'origin','branch':branch,'stdout':p.stdout.strip(),'stderr':p.stderr.strip()}
|
||||
target = BARE_REPO.resolve()
|
||||
|
||||
if target == common_git_dir:
|
||||
return {
|
||||
'status': 'synced',
|
||||
'target': 'common_git_dir',
|
||||
'branch': branch,
|
||||
**state,
|
||||
}
|
||||
|
||||
proc = subprocess.run(
|
||||
[
|
||||
'git',
|
||||
'push',
|
||||
'--porcelain',
|
||||
str(target),
|
||||
f'HEAD:refs/heads/{branch}',
|
||||
],
|
||||
cwd=str(WS),
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
else:
|
||||
proc = subprocess.run(
|
||||
[
|
||||
'git',
|
||||
'push',
|
||||
'--porcelain',
|
||||
'origin',
|
||||
f'HEAD:refs/heads/{branch}',
|
||||
],
|
||||
cwd=str(WS),
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(
|
||||
proc.stderr
|
||||
or proc.stdout
|
||||
or (
|
||||
'git push failed for branch '
|
||||
+ branch
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
'status': 'synced',
|
||||
'target': (
|
||||
str(BARE_REPO.resolve())
|
||||
if BARE_REPO.exists()
|
||||
else 'origin'
|
||||
),
|
||||
'branch': branch,
|
||||
'stdout': proc.stdout.strip(),
|
||||
'stderr': proc.stderr.strip(),
|
||||
**state,
|
||||
}
|
||||
|
||||
def sync_tag(tag: str) -> dict:
|
||||
if not tag: return {'status':'skipped','reason':'empty_tag'}
|
||||
state = detach_controller()
|
||||
|
||||
common_git_dir = Path(
|
||||
sh(
|
||||
[
|
||||
'rev-parse',
|
||||
'--path-format=absolute',
|
||||
'--git-common-dir',
|
||||
],
|
||||
True,
|
||||
)['stdout']
|
||||
).resolve()
|
||||
|
||||
tag_ref = f'refs/tags/{tag}'
|
||||
|
||||
if BARE_REPO.exists():
|
||||
ref=f'refs/tags/{tag}:refs/tags/{tag}'
|
||||
p=subprocess.run(['git','--git-dir',str(BARE_REPO),'fetch',str(WS),ref], text=True, capture_output=True)
|
||||
if p.returncode != 0: raise RuntimeError(p.stderr or p.stdout or f'git tag sync failed for {tag}')
|
||||
return {'status':'synced','target':'bare','tag':tag,'stdout':p.stdout.strip(),'stderr':p.stderr.strip()}
|
||||
p=subprocess.run(['git','push','origin',tag], cwd=str(WS), text=True, capture_output=True)
|
||||
if p.returncode != 0: raise RuntimeError(p.stderr or p.stdout or f'git tag push failed for {tag}')
|
||||
return {'status':'synced','target':'origin','tag':tag,'stdout':p.stdout.strip(),'stderr':p.stderr.strip()}
|
||||
target = BARE_REPO.resolve()
|
||||
|
||||
if target == common_git_dir:
|
||||
return {
|
||||
'status': 'synced',
|
||||
'target': 'common_git_dir',
|
||||
'tag': tag,
|
||||
**state,
|
||||
}
|
||||
|
||||
proc = subprocess.run(
|
||||
[
|
||||
'git',
|
||||
'push',
|
||||
'--porcelain',
|
||||
str(target),
|
||||
f'{tag_ref}:{tag_ref}',
|
||||
],
|
||||
cwd=str(WS),
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
else:
|
||||
proc = subprocess.run(
|
||||
[
|
||||
'git',
|
||||
'push',
|
||||
'--porcelain',
|
||||
'origin',
|
||||
f'{tag_ref}:{tag_ref}',
|
||||
],
|
||||
cwd=str(WS),
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(
|
||||
proc.stderr
|
||||
or proc.stdout
|
||||
or (
|
||||
'git push failed for tag '
|
||||
+ tag
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
'status': 'synced',
|
||||
'target': (
|
||||
str(BARE_REPO.resolve())
|
||||
if BARE_REPO.exists()
|
||||
else 'origin'
|
||||
),
|
||||
'tag': tag,
|
||||
'stdout': proc.stdout.strip(),
|
||||
'stderr': proc.stderr.strip(),
|
||||
**state,
|
||||
}
|
||||
|
||||
def commit(paths, msg):
|
||||
ensure_git(); paths = [relpath(p) for p in paths] if paths else ['.']; sh(['add'] + paths, True)
|
||||
@@ -210,25 +395,123 @@ class H(BaseHTTPRequestHandler):
|
||||
monitor.record_failure('do_GET', str(e), {'path':path})
|
||||
return self.sendj({'error':str(e)},500)
|
||||
def do_PUT(self):
|
||||
path=urllib.parse.urlparse(self.path).path.rstrip('/'); data=self.body()
|
||||
if path == '/api/model-priority': return self.model_priority(data)
|
||||
return self.sendj({'error':'not_found','path':path},404)
|
||||
def do_POST(self):
|
||||
path=urllib.parse.urlparse(self.path).path.rstrip('/'); data=self.body()
|
||||
path = urllib.parse.urlparse(
|
||||
self.path
|
||||
).path.rstrip('/')
|
||||
|
||||
data = self.body()
|
||||
|
||||
try:
|
||||
if path in ('/api/branch','/api/create-branch'): return self.branch(data)
|
||||
if path in ('/api/file','/api/commit-file'): return self.file(data)
|
||||
if path == '/api/commit': return self.commit(data)
|
||||
if path in ('/api/patch','/api/create-patch'): return self.patch(data)
|
||||
if path in ('/api/tag','/api/create-tag'): return self.tag(data)
|
||||
if path == '/api/rollback': return self.rollback(data)
|
||||
if path == '/api/model': return self.model(data)
|
||||
if path == '/api/model-priority': return self.model_priority(data)
|
||||
if path == '/api/nl': return self.nl(data)
|
||||
return self.sendj({'error':'not_found','path':path},404)
|
||||
except Exception as e:
|
||||
monitor.record_failure('do_POST', str(e), {'path':path})
|
||||
return self.sendj({'error':str(e)},500)
|
||||
with WRITE_LOCK:
|
||||
if path == '/api/model-priority':
|
||||
return self.model_priority(data)
|
||||
|
||||
return self.sendj(
|
||||
{
|
||||
'error': 'not_found',
|
||||
'path': path,
|
||||
},
|
||||
404,
|
||||
)
|
||||
except Exception as exc:
|
||||
monitor.record_failure(
|
||||
'do_PUT',
|
||||
str(exc),
|
||||
{'path': path},
|
||||
)
|
||||
return self.sendj(
|
||||
{'error': str(exc)},
|
||||
500,
|
||||
)
|
||||
finally:
|
||||
try:
|
||||
detach_controller()
|
||||
except Exception as detach_error:
|
||||
monitor.record_failure(
|
||||
'detach_controller',
|
||||
str(detach_error),
|
||||
{
|
||||
'path': path,
|
||||
'method': 'PUT',
|
||||
},
|
||||
)
|
||||
def do_POST(self):
|
||||
path = urllib.parse.urlparse(
|
||||
self.path
|
||||
).path.rstrip('/')
|
||||
|
||||
data = self.body()
|
||||
|
||||
try:
|
||||
with WRITE_LOCK:
|
||||
if path in (
|
||||
'/api/branch',
|
||||
'/api/create-branch',
|
||||
):
|
||||
return self.branch(data)
|
||||
|
||||
if path in (
|
||||
'/api/file',
|
||||
'/api/commit-file',
|
||||
):
|
||||
return self.file(data)
|
||||
|
||||
if path == '/api/commit':
|
||||
return self.commit(data)
|
||||
|
||||
if path in (
|
||||
'/api/patch',
|
||||
'/api/create-patch',
|
||||
):
|
||||
return self.patch(data)
|
||||
|
||||
if path in (
|
||||
'/api/tag',
|
||||
'/api/create-tag',
|
||||
):
|
||||
return self.tag(data)
|
||||
|
||||
if path == '/api/rollback':
|
||||
return self.rollback(data)
|
||||
|
||||
if path == '/api/model':
|
||||
return self.model(data)
|
||||
|
||||
if path == '/api/model-priority':
|
||||
return self.model_priority(data)
|
||||
|
||||
if path == '/api/nl':
|
||||
return self.nl(data)
|
||||
|
||||
return self.sendj(
|
||||
{
|
||||
'error': 'not_found',
|
||||
'path': path,
|
||||
},
|
||||
404,
|
||||
)
|
||||
except Exception as exc:
|
||||
monitor.record_failure(
|
||||
'do_POST',
|
||||
str(exc),
|
||||
{'path': path},
|
||||
)
|
||||
return self.sendj(
|
||||
{'error': str(exc)},
|
||||
500,
|
||||
)
|
||||
finally:
|
||||
try:
|
||||
detach_controller()
|
||||
except Exception as detach_error:
|
||||
monitor.record_failure(
|
||||
'detach_controller',
|
||||
str(detach_error),
|
||||
{
|
||||
'path': path,
|
||||
'method': 'POST',
|
||||
},
|
||||
)
|
||||
def branch(self,data):
|
||||
a=self.need('branch:create');
|
||||
if not a: return
|
||||
@@ -393,6 +676,25 @@ def api_spec():
|
||||
return {'version':'V1R1C0-chatgpt-actions','openapi':'/api/openapi.yaml','read':['GET /api/base','GET /api/ver','GET /api/diff','GET /api/patch','GET /api/model','GET /api/run','GET /admin/ai-evolution'],'write':['POST /api/branch','POST /api/file','POST /api/commit','POST /api/patch','POST /api/tag','POST /api/rollback','POST /api/model','PUT /api/model-priority','POST /api/nl'],'policy':{'bearer_required_for_write':True,'direct_main_push':False,'delete_tag':False,'owner_approval_required_for_v_level':True,'api_keys_in_git':False,'git_secret_field':'secret_ref','online_git_sync':True}}
|
||||
|
||||
def main():
|
||||
print(f'AIOS ChatGPT GitOps adapter listening on {HOST}:{PORT}')
|
||||
ThreadingHTTPServer((HOST,PORT), H).serve_forever()
|
||||
state = controller_status()
|
||||
|
||||
if state['workspace_dirty']:
|
||||
raise RuntimeError(
|
||||
'controller workspace is dirty; '
|
||||
'refusing to start GitOps writes'
|
||||
)
|
||||
|
||||
state = detach_controller()
|
||||
|
||||
print(
|
||||
'AIOS ChatGPT GitOps adapter listening '
|
||||
f'on {HOST}:{PORT}; '
|
||||
f'workspace_mode={state["workspace_mode"]}; '
|
||||
f'workspace_ref={state["workspace_ref"]}'
|
||||
)
|
||||
|
||||
ThreadingHTTPServer(
|
||||
(HOST, PORT),
|
||||
H,
|
||||
).serve_forever()
|
||||
if __name__ == '__main__': main()
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
WT="${AIOS_GITOPS_WORKSPACE:-/data/aios-gitops/worktrees/controller}"
|
||||
BRANCH="${AIOS_GITOPS_CONTROLLER_BRANCH:-main}"
|
||||
|
||||
git -C "$WT" rev-parse \
|
||||
--is-inside-work-tree >/dev/null
|
||||
|
||||
DIRTY="$(
|
||||
git -C "$WT" status --porcelain=v1
|
||||
)"
|
||||
|
||||
if [[ -n "$DIRTY" ]]; then
|
||||
echo "controller workspace is dirty" >&2
|
||||
echo "$DIRTY" >&2
|
||||
exit 73
|
||||
fi
|
||||
|
||||
git -C "$WT" fetch --prune origin \
|
||||
"+refs/heads/${BRANCH}:refs/remotes/origin/${BRANCH}"
|
||||
|
||||
git -C "$WT" switch --detach \
|
||||
"refs/remotes/origin/${BRANCH}"
|
||||
|
||||
COMMON="$(
|
||||
git -C "$WT" \
|
||||
rev-parse \
|
||||
--path-format=absolute \
|
||||
--git-common-dir
|
||||
)"
|
||||
|
||||
git --git-dir="$COMMON" \
|
||||
worktree prune --expire now
|
||||
|
||||
CURRENT_BRANCH="$(
|
||||
git -C "$WT" branch --show-current
|
||||
)"
|
||||
|
||||
if [[ -n "$CURRENT_BRANCH" ]]; then
|
||||
echo "controller remains attached: $CURRENT_BRANCH" >&2
|
||||
exit 74
|
||||
fi
|
||||
|
||||
DIRTY_AFTER="$(
|
||||
git -C "$WT" status --porcelain=v1
|
||||
)"
|
||||
|
||||
if [[ -n "$DIRTY_AFTER" ]]; then
|
||||
echo "controller became dirty" >&2
|
||||
echo "$DIRTY_AFTER" >&2
|
||||
exit 75
|
||||
fi
|
||||
|
||||
echo "workspace_mode=detached"
|
||||
echo "workspace_branch=null"
|
||||
echo "workspace_dirty=false"
|
||||
echo "workspace_ref=$(git -C "$WT" rev-parse --short HEAD)"
|
||||
+19
-12
@@ -1,13 +1,20 @@
|
||||
{
|
||||
"current_baseline": "V1R4C6",
|
||||
"previous_baseline": "V1R4C5",
|
||||
"promoted_at": "2026-07-11T21:00:00+08:00",
|
||||
"git": "e204a90398bf646d46245b3088226749fa056edd",
|
||||
"promoted_by": "openclaw/admin",
|
||||
"promotion_reason": "V1R4C6 admin search agent integration, search gateway, agent platform, VERSION bump, merge tests deploy pipeline — not yet deployed to production",
|
||||
"constraints": {
|
||||
"memory_search_enabled": false,
|
||||
"force_push_allowed": false,
|
||||
"gitea_db_mutation_requires_owner_approval": true
|
||||
}
|
||||
}
|
||||
"current_baseline": "V2R1C1",
|
||||
"previous_baseline": "V1R4C6",
|
||||
"promoted_at": "2026-07-21T14:17:05Z",
|
||||
"git": "4e0e6dcb90e6732aa9838fe9d0af0b34746829b0",
|
||||
"promoted_by": "chatgpt/owner-approved-governance",
|
||||
"promotion_reason": "Register the already validated and deployed V2R1C1 production release in the central GitOps baseline Registry.",
|
||||
"version": "V2R1C1",
|
||||
"release_oid": "4e0e6dcb90e6732aa9838fe9d0af0b34746829b0",
|
||||
"tag_oid": "4e0e6dcb90e6732aa9838fe9d0af0b34746829b0",
|
||||
"status": "PRODUCTION_DEPLOYED",
|
||||
"constraints": {
|
||||
"product_code_changed": false,
|
||||
"tag_changed": false,
|
||||
"production_redeployed": false,
|
||||
"production_traffic_changed": false,
|
||||
"force_push_allowed": false,
|
||||
"gitea_db_mutation_requires_owner_approval": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# V2R1C1 中央 Baseline 治理收口
|
||||
|
||||
## 收口范围
|
||||
|
||||
本次变更只登记已经完成生产验证并上线的 `V2R1C1`。它新增中央版本 Manifest、把中央 Baseline Pointer 从 `V1R4C6` 切换为 `V2R1C1`,并建立 Release、Companion、Artifact 与最终生产 Evidence 的追溯关系。
|
||||
|
||||
本次不修改产品实现,不移动或重建 `V2R1C1` Tag,不改变 Release OID,不重新部署生产,不切换生产流量,也不重写历史证据。
|
||||
|
||||
## 权威标识
|
||||
|
||||
- Release:`release/AIOS-V2R1C1-20260721`
|
||||
- Release OID:`4e0e6dcb90e6732aa9838fe9d0af0b34746829b0`
|
||||
- Tag / Tag OID:`V2R1C1` / `4e0e6dcb90e6732aa9838fe9d0af0b34746829b0`
|
||||
- SemVer:`2.1.0-rc.1`
|
||||
- OpenClaw Companion:`2.1.0-rc.4`
|
||||
- Companion Source OID:`83f8f878c7da5dd8263f2af2efa80750492e5cb3`
|
||||
- Companion OID:`12d1c13170e0c5a691d67a9408e739e7a17064a3`
|
||||
- Artifact SHA256:`aadd5f3cfa0d8e796a21f6368f5c95e8c60e9063d6e9fcd9c4947cfa0c013671`
|
||||
- Contract:`aios.openclaw.command.v2` / `Ed25519`
|
||||
- Evidence Root:`/var/lib/aios-gitops/acceptance/aios-v2r1c1-openclaw-v2-companion-20260721T094442Z`
|
||||
- Final Audit Manifest SHA256:`2cdb884abf195969de3cdaa44c76aaac52a9165fb4918436855291e46b2f8d55`
|
||||
- Production Status:`PRODUCTION_DEPLOYED`
|
||||
|
||||
## 治理路径
|
||||
|
||||
- Governance Branch:`patch/chatgpt/AIOS-V2R1C1-BASELINE-REGISTRY-20260721`
|
||||
- Base Ref:`refs/heads/main`
|
||||
- Base OID:`3cf9958cb94614449e093639cbf902216d73447f`
|
||||
- Manifest Commit:`a56caf46f6bc335ec2395b674ead4b2a427c5edd`
|
||||
- Baseline Commit:`6dbd5cbe07d2960ca653049fefbcf3a3e18f9ab1`
|
||||
- Reviewer:`gpt-5.3-codex-spark validator and docs_reporter`
|
||||
- Review:`PASS`(`2026-07-21T14:23:00Z`)
|
||||
|
||||
## 命令与退出码
|
||||
|
||||
| 阶段 | 命令 | 开始 UTC | 结束 UTC | Exit Code |
|
||||
| --- | --- | --- | --- | ---: |
|
||||
| 中央只读核对 | `GET /api/run; GET /api/base; GET /api/ver` | 2026-07-21T14:09:40Z | 2026-07-21T14:09:40Z | 0 |
|
||||
| 创建治理分支 | `bash /tmp/registry-create-branch.sh` | 2026-07-21T14:17:05Z | 2026-07-21T14:17:05Z | 0 |
|
||||
| 核心元数据验证 | `bash /tmp/registry-validate-core.sh` | 2026-07-21T14:19:56Z | 2026-07-21T14:19:56Z | 0 |
|
||||
|
||||
完整机读记录位于 `.agent/results/releases/V2R1C1-BASELINE-CLOSURE.json`,治理运行证据位于 `/var/lib/aios-gitops/acceptance/aios-v2r1c1-baseline-registry-20260721T141705Z`。
|
||||
|
||||
## 元数据回滚
|
||||
|
||||
若中央 Registry 解析或治理服务异常,保留 `versions/V2R1C1.json`,通过新的受审治理提交把 `baseline/current.json` 和 `VERSION` 临时恢复到 `V1R4C6`。该操作不得回滚生产系统、移动 Tag、修改 Release OID 或重写历史。
|
||||
|
||||
## 范围证明
|
||||
|
||||
```text
|
||||
product_code_changed=false
|
||||
tag_changed=false
|
||||
production_redeployed=false
|
||||
production_traffic_changed=false
|
||||
secret_material_recorded=false
|
||||
```
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"version": "V2R1C1",
|
||||
"codename": "openclaw-command-v2-production-baseline",
|
||||
"status": "PRODUCTION_DEPLOYED",
|
||||
"tag": "V2R1C1",
|
||||
"tag_ref": "refs/tags/V2R1C1",
|
||||
"tag_object": "4e0e6dcb90e6732aa9838fe9d0af0b34746829b0",
|
||||
"previous_version": "V1R4C6",
|
||||
"rollback_version": "V1R4C6",
|
||||
"baseline": "V1R4C6",
|
||||
"release_reason": "Register the already deployed AIOS V2R1C1 release, immutable tag, OpenClaw Command v2 companion, and final production evidence in the central GitOps Registry.",
|
||||
"purpose": "Close the central version-governance gap without changing product implementation, the V2R1C1 tag, production release, deployment, or traffic.",
|
||||
"changes": [
|
||||
"governance: add the canonical V2R1C1 version manifest",
|
||||
"governance: advance the central baseline pointer from V1R4C6 to V2R1C1",
|
||||
"governance: link release, companion, artifact, validation, and final audit evidence"
|
||||
],
|
||||
"artifacts": [
|
||||
"docs/releases/V2R1C1-BASELINE-CLOSURE.md",
|
||||
".agent/results/releases/V2R1C1-BASELINE-CLOSURE.json",
|
||||
".agent/results/releases/V2R1C1-BASELINE-CLOSURE.md",
|
||||
"/var/lib/aios-gitops/acceptance/aios-v2r1c1-openclaw-v2-companion-20260721T094442Z"
|
||||
],
|
||||
"validated_actions": [
|
||||
"release and tag OID equality",
|
||||
"local and Gitea ref synchronization",
|
||||
"OpenClaw Command v2 positive and negative validation",
|
||||
"Gate A and Gate B",
|
||||
"Hermes lifecycle 17/17",
|
||||
"grant isolation 11/11",
|
||||
"restart recovery",
|
||||
"rollback and forward recovery",
|
||||
"SQLite quick_check",
|
||||
"secret scan with zero violations",
|
||||
"final production healthcheck"
|
||||
],
|
||||
"secret_plaintext_written": false,
|
||||
"owner_approval_required": true,
|
||||
"owner_notification_channels": [
|
||||
"codex_owner_instruction",
|
||||
"gitops_audit"
|
||||
],
|
||||
"constraints_original": {
|
||||
"product_code_changed": false,
|
||||
"tag_changed": false,
|
||||
"production_redeployed": false,
|
||||
"production_traffic_changed": false,
|
||||
"force_push_allowed": false,
|
||||
"incubator_in_release_graph": false
|
||||
},
|
||||
"promoted_at": "2026-07-21T14:17:05Z",
|
||||
"promoted_by": "chatgpt/owner-approved-governance",
|
||||
"created_at": "2026-07-21T14:17:05Z",
|
||||
"author": "chatgpt"
|
||||
}
|
||||
Reference in New Issue
Block a user