Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a90023e8d1 | ||
|
|
db72e3184f | ||
|
|
4487a774fd | ||
|
|
a8e7a97f2d | ||
|
|
2f1d63c147 | ||
|
|
662ccf36f8 | ||
|
|
102d2306f0 | ||
|
|
28eca15d75 |
@@ -0,0 +1,10 @@
|
||||
# Independent Validator Result
|
||||
|
||||
- Model: `gpt-5.3-codex-spark`
|
||||
- Reviewer identity: `gpt-5.3-codex-spark-validator`
|
||||
- Broad live-review attempt: `TIMEOUT` after 600 seconds; no decision was accepted from that attempt.
|
||||
- Narrow immutable evidence-bundle review: `SUCCESS`
|
||||
- Cleanup review: `PASS`
|
||||
- Release decision: `STOP_PREPRODUCTION`
|
||||
- Verified: 10 Manifest records, 7 removed records, 3 retained roots, 7 local/remote Archive OID pairs, product OID equality, release/tag absence, and global dirty/locked/prunable all zero.
|
||||
- Blocking finding: archived Portal Adapter `portal-surface` and `seed-ucc` commands returned non-zero.
|
||||
@@ -0,0 +1,317 @@
|
||||
{
|
||||
"schema_version": "aios.incubator.worktree-archive.v1",
|
||||
"status": "CLEANUP_REVIEWED_RELEASE_STOPPED",
|
||||
"release_gate": "STOP_PREPRODUCTION",
|
||||
"release_gate_reasons": [
|
||||
"archived Portal Adapter portal-surface contract tests failed",
|
||||
"archived Portal Adapter Django test could not execute because Django is absent on the validation host"
|
||||
],
|
||||
"inventory_evidence_root": "/var/lib/aios-gitops/acceptance/aios-v2r1c1-archive-20260721T064301Z",
|
||||
"archive_evidence_root": "/var/lib/aios-gitops/acceptance/aios-v2r1c1-task-archive-20260721T070301Z",
|
||||
"removal_evidence_root": "/var/lib/aios-gitops/acceptance/aios-v2r1c1-task-removal-20260721T070843Z",
|
||||
"controller_evidence_root": "/var/lib/aios-gitops/acceptance/aios-v2r1c1-controller-migration-20260721T070749Z",
|
||||
"longterm_evidence_root": "/var/lib/aios-gitops/acceptance/aios-v2r1c1-longterm-clean-20260721T071346Z",
|
||||
"global_gate_evidence_root": "/var/lib/aios-gitops/acceptance/aios-v2r1c1-global-gate-20260721T071446Z",
|
||||
"release_source": false,
|
||||
"reviewed_count": 10,
|
||||
"removed_count": 7,
|
||||
"retained_active_count": 3,
|
||||
"global_dirty_count": 0,
|
||||
"locked_count": 0,
|
||||
"prunable_count": 0,
|
||||
"archive_validation": {
|
||||
"passed_commands": 7,
|
||||
"failed_commands": 1,
|
||||
"blocked_commands": 1
|
||||
},
|
||||
"worktrees": [
|
||||
{
|
||||
"worktree_path": "/data/aios-gitops/worktrees/chatgpt/TASK-V2R1-PERSONAL-WEIXIN-GOLDEN-CONTRACT-R2-20260715",
|
||||
"owner": "chatgpt",
|
||||
"task_id": "TASK-V2R1-PERSONAL-WEIXIN-GOLDEN-CONTRACT-R2-20260715",
|
||||
"source_branch": "design/v2r1-personal-weixin-golden-contract-r2-20260715",
|
||||
"source_oid": "68d27879103990aac692207a8512b3725c6d0d41",
|
||||
"remote_oid": "ABSENT_AT_INVENTORY",
|
||||
"dirty_files_before": 36,
|
||||
"secret_scan": {
|
||||
"initial_violations": 0,
|
||||
"final_violations": 0,
|
||||
"secrets_recorded": false
|
||||
},
|
||||
"adopted_items": [
|
||||
"channel-neutral versioned contract SDK and deterministic fixture validation pattern"
|
||||
],
|
||||
"excluded_items": [
|
||||
"Personal-Weixin schemas, fixtures, security document and release-specific plan"
|
||||
],
|
||||
"archive_branch": "archive/chatgpt/TASK-V2R1-PERSONAL-WEIXIN-GOLDEN-CONTRACT-R2-20260715-20260721",
|
||||
"archive_oid": "fe86e35257113853ba08b1438bc820bb4554e6e8",
|
||||
"archive_remote_oid": "fe86e35257113853ba08b1438bc820bb4554e6e8",
|
||||
"cleanup_method": "archive_commit_push_verify_then_worktree_remove_and_prune",
|
||||
"removed_at": "2026-07-21T07:08:45Z",
|
||||
"validation": "PASS: contract harness",
|
||||
"reviewer": "gpt-5.3-codex-spark-validator"
|
||||
},
|
||||
{
|
||||
"worktree_path": "/data/aios-gitops/worktrees/chatgpt/TASK-V2R1-PERSONAL-WEIXIN-PORTAL-ADAPTER-20260715",
|
||||
"owner": "chatgpt",
|
||||
"task_id": "TASK-V2R1-PERSONAL-WEIXIN-PORTAL-ADAPTER-20260715",
|
||||
"source_branch": "fix/v2r1-personal-weixin-portal-adapter-20260715",
|
||||
"source_oid": "64270e74ba0835971ee20bf4d2b12b1d20b05533",
|
||||
"remote_oid": "ABSENT_AT_INVENTORY",
|
||||
"dirty_files_before": 2,
|
||||
"secret_scan": {
|
||||
"initial_violations": 1,
|
||||
"final_violations": 0,
|
||||
"secrets_recorded": false
|
||||
},
|
||||
"adopted_items": [
|
||||
"generic auth-session state, expiry, challenge, replay and fail-closed policy"
|
||||
],
|
||||
"excluded_items": [
|
||||
"UCC/OpenClaw/Weixin adapter implementation; incomplete portal surface; runtime-specific binding code"
|
||||
],
|
||||
"archive_branch": "archive/chatgpt/TASK-V2R1-PERSONAL-WEIXIN-PORTAL-ADAPTER-20260715-20260721",
|
||||
"archive_oid": "79f529a91f8bf7cf3237feb01e98807ab5ad10b3",
|
||||
"archive_remote_oid": "79f529a91f8bf7cf3237feb01e98807ab5ad10b3",
|
||||
"cleanup_method": "archive_commit_push_verify_then_worktree_remove_and_prune",
|
||||
"removed_at": "2026-07-21T07:08:46Z",
|
||||
"validation": "REJECTED_FOR_ADOPTION: official-login tests pass; portal-surface tests fail; Django test blocked by missing dependency",
|
||||
"reviewer": "gpt-5.3-codex-spark-validator"
|
||||
},
|
||||
{
|
||||
"worktree_path": "/data/aios-gitops/worktrees/chatgpt/TASK-WORKTREE-CONCURRENCY-CHATGPT-20260715T055106UTC8",
|
||||
"owner": "chatgpt",
|
||||
"task_id": "TASK-WORKTREE-CONCURRENCY-CHATGPT-20260715T055106UTC8",
|
||||
"source_branch": "task/chatgpt/TASK-WORKTREE-CONCURRENCY-CHATGPT-20260715T055106UTC8",
|
||||
"source_oid": "b05eb1babe9c657e8776c09eec71302166902cc3",
|
||||
"remote_oid": "ABSENT_AT_INVENTORY",
|
||||
"dirty_files_before": 2,
|
||||
"secret_scan": {
|
||||
"initial_violations": 0,
|
||||
"final_violations": 0,
|
||||
"secrets_recorded": false
|
||||
},
|
||||
"adopted_items": [
|
||||
"registered-worktree classification and dirty-count governance model"
|
||||
],
|
||||
"excluded_items": [
|
||||
"agent task/result documents and ACL probe evidence"
|
||||
],
|
||||
"archive_branch": "archive/chatgpt/TASK-WORKTREE-CONCURRENCY-CHATGPT-20260715T055106UTC8-20260721",
|
||||
"archive_oid": "f0feed397dc67edab7daa16f0cdb5b553eca5668",
|
||||
"archive_remote_oid": "f0feed397dc67edab7daa16f0cdb5b553eca5668",
|
||||
"cleanup_method": "archive_commit_push_verify_then_worktree_remove_and_prune",
|
||||
"removed_at": "2026-07-21T07:08:47Z",
|
||||
"validation": "PASS: evidence integrity and zero-secret scan",
|
||||
"reviewer": "gpt-5.3-codex-spark-validator"
|
||||
},
|
||||
{
|
||||
"worktree_path": "/data/aios-gitops/worktrees/codex/TASK-CODEX-AIOS-SPARK-RUNNER-PROVISIONING-20260717T103500UTC8",
|
||||
"owner": "codex",
|
||||
"task_id": "TASK-CODEX-AIOS-SPARK-RUNNER-PROVISIONING-20260717T103500UTC8",
|
||||
"source_branch": "patch/chatgpt/v2r1-personal-weixin-b3-repair3-owner-20260717T102830UTC8",
|
||||
"source_oid": "ef20c71247c6de6659e18fc57315da19d8a3834e",
|
||||
"remote_oid": "ABSENT_AT_INVENTORY",
|
||||
"dirty_files_before": 6,
|
||||
"secret_scan": {
|
||||
"initial_violations": 0,
|
||||
"final_violations": 0,
|
||||
"secrets_recorded": false
|
||||
},
|
||||
"adopted_items": [
|
||||
"generic runner-provider preflight, immutable digest and reference-only credential contracts"
|
||||
],
|
||||
"excluded_items": [
|
||||
"Spark-specific systemd, provisioning, rollback and production wiring"
|
||||
],
|
||||
"archive_branch": "archive/codex/TASK-CODEX-AIOS-SPARK-RUNNER-PROVISIONING-20260717T103500UTC8-20260721",
|
||||
"archive_oid": "d97cd3642528bd3373fe4cf5161a5d99c0b9285f",
|
||||
"archive_remote_oid": "d97cd3642528bd3373fe4cf5161a5d99c0b9285f",
|
||||
"cleanup_method": "archive_commit_push_verify_then_worktree_remove_and_prune",
|
||||
"removed_at": "2026-07-21T07:08:48Z",
|
||||
"validation": "PASS: 27 pytest tests",
|
||||
"reviewer": "gpt-5.3-codex-spark-validator"
|
||||
},
|
||||
{
|
||||
"worktree_path": "/data/aios-gitops/worktrees/openclaw/TASK-OPENCLAW-V2R1-PERSONAL-WEIXIN-B3-EVIDENCE-HARDEN-20260716T133000UTC8",
|
||||
"owner": "openclaw",
|
||||
"task_id": "TASK-OPENCLAW-V2R1-PERSONAL-WEIXIN-B3-EVIDENCE-HARDEN-20260716T133000UTC8",
|
||||
"source_branch": "patch/openclaw/v2r1-personal-weixin-b3-evidence-hardening-20260716T133000UTC8",
|
||||
"source_oid": "8d9c6da90fc8caa69b691d243c6fefb40b6b0575",
|
||||
"remote_oid": "8d9c6da90fc8caa69b691d243c6fefb40b6b0575",
|
||||
"dirty_files_before": 3,
|
||||
"secret_scan": {
|
||||
"initial_violations": 0,
|
||||
"final_violations": 0,
|
||||
"secrets_recorded": false
|
||||
},
|
||||
"adopted_items": [
|
||||
"canonical evidence envelope, streaming hash and independent-review integrity policy"
|
||||
],
|
||||
"excluded_items": [
|
||||
"Personal-Weixin B3 evidence tool, empty owner facts and release-specific output path"
|
||||
],
|
||||
"archive_branch": "archive/openclaw/TASK-OPENCLAW-V2R1-PERSONAL-WEIXIN-B3-EVIDENCE-HARDEN-20260716T133000UTC8-20260721",
|
||||
"archive_oid": "2ddbc456124c02707dc23e8d6c241826d7026575",
|
||||
"archive_remote_oid": "2ddbc456124c02707dc23e8d6c241826d7026575",
|
||||
"cleanup_method": "archive_commit_push_verify_then_worktree_remove_and_prune",
|
||||
"removed_at": "2026-07-21T07:08:49Z",
|
||||
"validation": "PASS: compile, canonical template JSON and CLI help",
|
||||
"reviewer": "gpt-5.3-codex-spark-validator"
|
||||
},
|
||||
{
|
||||
"worktree_path": "/data/aios-gitops/worktrees/openclaw/TASK-V2R1-OPENCLAW-WEIXIN-WEB-LOGIN-CONTRACT-20260715",
|
||||
"owner": "openclaw",
|
||||
"task_id": "TASK-V2R1-OPENCLAW-WEIXIN-WEB-LOGIN-CONTRACT-20260715",
|
||||
"source_branch": "fix/v2r1-openclaw-weixin-web-login-contract-20260715",
|
||||
"source_oid": "68d27879103990aac692207a8512b3725c6d0d41",
|
||||
"remote_oid": "ABSENT_AT_INVENTORY",
|
||||
"dirty_files_before": 20,
|
||||
"secret_scan": {
|
||||
"initial_violations": 0,
|
||||
"final_violations": 0,
|
||||
"secrets_recorded": false
|
||||
},
|
||||
"adopted_items": [
|
||||
"generic auth-session state transitions and readiness/revocation contracts"
|
||||
],
|
||||
"excluded_items": [
|
||||
"OpenClaw and Tencent Weixin patches, candidate evidence and channel-specific code"
|
||||
],
|
||||
"archive_branch": "archive/openclaw/TASK-V2R1-OPENCLAW-WEIXIN-WEB-LOGIN-CONTRACT-20260715-20260721",
|
||||
"archive_oid": "d03b4ee34debe6e30ce2af233e2226b02e6dd029",
|
||||
"archive_remote_oid": "d03b4ee34debe6e30ce2af233e2226b02e6dd029",
|
||||
"cleanup_method": "archive_commit_push_verify_then_worktree_remove_and_prune",
|
||||
"removed_at": "2026-07-21T07:08:50Z",
|
||||
"validation": "PASS: Node contract tests",
|
||||
"reviewer": "gpt-5.3-codex-spark-validator"
|
||||
},
|
||||
{
|
||||
"worktree_path": "/data/aios-gitops/worktrees/openclaw/TASK-WORKTREE-CONCURRENCY-OPENCLAW-20260715T055106UTC8",
|
||||
"owner": "openclaw",
|
||||
"task_id": "TASK-WORKTREE-CONCURRENCY-OPENCLAW-20260715T055106UTC8",
|
||||
"source_branch": "task/openclaw/TASK-WORKTREE-CONCURRENCY-OPENCLAW-20260715T055106UTC8",
|
||||
"source_oid": "b05eb1babe9c657e8776c09eec71302166902cc3",
|
||||
"remote_oid": "ABSENT_AT_INVENTORY",
|
||||
"dirty_files_before": 3,
|
||||
"secret_scan": {
|
||||
"initial_violations": 0,
|
||||
"final_violations": 0,
|
||||
"secrets_recorded": false
|
||||
},
|
||||
"adopted_items": [
|
||||
"registered-worktree classification and cleanup authorization boundary"
|
||||
],
|
||||
"excluded_items": [
|
||||
"agent task/result documents and ACL probe evidence"
|
||||
],
|
||||
"archive_branch": "archive/openclaw/TASK-WORKTREE-CONCURRENCY-OPENCLAW-20260715T055106UTC8-20260721",
|
||||
"archive_oid": "d9a85bb86cc09c117d63de803a404ee5a864e7c1",
|
||||
"archive_remote_oid": "d9a85bb86cc09c117d63de803a404ee5a864e7c1",
|
||||
"cleanup_method": "archive_commit_push_verify_then_worktree_remove_and_prune",
|
||||
"removed_at": "2026-07-21T07:08:51Z",
|
||||
"validation": "PASS: evidence integrity and zero-secret scan",
|
||||
"reviewer": "gpt-5.3-codex-spark-validator"
|
||||
},
|
||||
{
|
||||
"worktree_path": "/data/aios-gitops/worktrees/codex",
|
||||
"owner": "codex",
|
||||
"task_id": "codex-long-term-root",
|
||||
"source_branch": "acceptance/codex/TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8-FINAL",
|
||||
"source_oid": "b87266d63f4d16ba25b9c0041d6769822dfa99d4",
|
||||
"remote_oid": "ABSENT_AT_INVENTORY",
|
||||
"dirty_files_before": 56,
|
||||
"secret_scan": {
|
||||
"initial_violations": 0,
|
||||
"final_violations": 0,
|
||||
"secrets_recorded": false
|
||||
},
|
||||
"adopted_items": [
|
||||
"registered child-worktree governance classification"
|
||||
],
|
||||
"excluded_items": [
|
||||
"all 55 retained registered child worktrees and their contents"
|
||||
],
|
||||
"archive_branch": "acceptance/codex/TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8-FINAL",
|
||||
"archive_oid": "0193a12b57c4974888b25753fd2bfb1f2028909b",
|
||||
"archive_remote_oid": "0193a12b57c4974888b25753fd2bfb1f2028909b",
|
||||
"cleanup_method": "retain active long-term root; commit exact registered-child ignore list",
|
||||
"removed_at": null,
|
||||
"validation": "PASS: 55 untracked entries exactly matched 55 registered child worktrees; final clean",
|
||||
"reviewer": "gpt-5.3-codex-spark-validator"
|
||||
},
|
||||
{
|
||||
"worktree_path": "/data/aios-gitops/worktrees/controller",
|
||||
"owner": "controller",
|
||||
"task_id": "controller-long-term-root",
|
||||
"source_branch": "DETACHED",
|
||||
"source_oid": "102d2306f05397374fc91c711767021c5d9f41f9",
|
||||
"remote_oid": "ABSENT_AT_INVENTORY",
|
||||
"dirty_files_before": 11,
|
||||
"secret_scan": {
|
||||
"initial_violations": 0,
|
||||
"final_violations": 0,
|
||||
"secrets_recorded": false
|
||||
},
|
||||
"adopted_items": [
|
||||
"runtime-state separation policy"
|
||||
],
|
||||
"excluded_items": [
|
||||
"generated health/watcher state and duplicate controller-run audit records"
|
||||
],
|
||||
"archive_branch": "archive/controller/runtime-baseline-20260721",
|
||||
"archive_oid": "102d2306f05397374fc91c711767021c5d9f41f9",
|
||||
"archive_remote_oid": "102d2306f05397374fc91c711767021c5d9f41f9",
|
||||
"cleanup_method": "retain active source root; migrate watcher state to runtime directory; restore tracked generated state; remove only hash-matched duplicates",
|
||||
"removed_at": null,
|
||||
"validation": "PASS: watcher, model API and autonomous controller active; final clean",
|
||||
"reviewer": "gpt-5.3-codex-spark-validator"
|
||||
},
|
||||
{
|
||||
"worktree_path": "/data/aios-gitops/worktrees/openclaw",
|
||||
"owner": "openclaw",
|
||||
"task_id": "openclaw-long-term-root",
|
||||
"source_branch": "acceptance/openclaw/TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8-FINAL",
|
||||
"source_oid": "6cd0f5aa0ad28f767ffd5396c29d1a9182f88c43",
|
||||
"remote_oid": "ABSENT_AT_INVENTORY",
|
||||
"dirty_files_before": 5,
|
||||
"secret_scan": {
|
||||
"initial_violations": 0,
|
||||
"final_violations": 0,
|
||||
"secrets_recorded": false
|
||||
},
|
||||
"adopted_items": [
|
||||
"registered child-worktree governance classification"
|
||||
],
|
||||
"excluded_items": [
|
||||
"both retained registered child worktrees and their contents"
|
||||
],
|
||||
"archive_branch": "acceptance/openclaw/TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8-FINAL",
|
||||
"archive_oid": "633c8308f53b0ad04ce054ceafb624cb0904cbf4",
|
||||
"archive_remote_oid": "633c8308f53b0ad04ce054ceafb624cb0904cbf4",
|
||||
"cleanup_method": "retain active long-term root; commit exact registered-child ignore list",
|
||||
"removed_at": null,
|
||||
"validation": "PASS: 2 untracked entries exactly matched 2 registered child worktrees; final clean",
|
||||
"reviewer": "gpt-5.3-codex-spark-validator"
|
||||
}
|
||||
],
|
||||
"unknown_value_policy": "UNVERIFIED_OWNER_INPUT_REQUIRED",
|
||||
"secrets_recorded": false,
|
||||
"reviewer": "gpt-5.3-codex-spark-validator",
|
||||
"reviewer_decision": {
|
||||
"cleanup_review": "PASS",
|
||||
"release_decision": "STOP_PREPRODUCTION",
|
||||
"verified_counts": {
|
||||
"manifest_records": 10,
|
||||
"removed_records": 7,
|
||||
"retained_roots": 3,
|
||||
"archive_oid_local_remote_pairs": 7,
|
||||
"global_dirty": 0,
|
||||
"global_locked": 0,
|
||||
"global_prunable": 0
|
||||
},
|
||||
"finding": "portal-surface and seed-ucc archive validation commands returned non-zero",
|
||||
"review_scope": "compact immutable evidence bundle after one broader live-review timeout"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# Worktree Cleanup Result
|
||||
|
||||
Status: `CLEANUP_REVIEWED_RELEASE_STOPPED`
|
||||
|
||||
- Reviewed: `10/10`
|
||||
- Archived and removed: `7`
|
||||
- Retained active and cleaned: `3`
|
||||
- Global dirty worktrees: `0`
|
||||
- Locked: `0`
|
||||
- Prunable: `0`
|
||||
- Product OID: `fb739f6e8ff9c031d319221ace68dce157694086`
|
||||
- Incubator OID before this manifest commit: `4487a774fdeec51bc49253c2ad494c19d1b1c3e2`
|
||||
- Release branch created: `false`
|
||||
- V2R1C1 tag created: `false`
|
||||
- Release decision: `STOP_PREPRODUCTION`
|
||||
|
||||
The stop decision is evidence-driven: the archived Portal Adapter portal-surface contract suite failed, and its Django test could not run because Django is absent on the host. No failed archive candidate was adopted into the incubator or product branch.
|
||||
|
||||
Independent reviewer: `gpt-5.3-codex-spark-validator`; cleanup `PASS`; release `STOP_PREPRODUCTION`.
|
||||
@@ -0,0 +1,3 @@
|
||||
"""Future AIOS capabilities isolated from the V2R1C1 release line."""
|
||||
|
||||
__all__: list[str] = []
|
||||
@@ -0,0 +1,64 @@
|
||||
"""Channel-neutral adapter contracts for future evaluation."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Any, Mapping, Protocol, Sequence, runtime_checkable
|
||||
|
||||
|
||||
class AdapterHealthStatus(str, Enum):
|
||||
HEALTHY = "healthy"
|
||||
DEGRADED = "degraded"
|
||||
UNAVAILABLE = "unavailable"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AdapterHealth:
|
||||
status: AdapterHealthStatus
|
||||
observed_at: str
|
||||
details: Mapping[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AdapterCapability:
|
||||
name: str
|
||||
version: str = "1"
|
||||
metadata: Mapping[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AdapterSession:
|
||||
session_id: str
|
||||
tenant_id: str
|
||||
state: str
|
||||
expires_at: str | None = None
|
||||
metadata: Mapping[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AdapterResult:
|
||||
ok: bool
|
||||
state: str
|
||||
output: Mapping[str, Any] = field(default_factory=dict)
|
||||
error: Mapping[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class ChannelAdapter(Protocol):
|
||||
def health(self) -> AdapterHealth: ...
|
||||
|
||||
def capabilities(self) -> Sequence[AdapterCapability]: ...
|
||||
|
||||
def open_session(self, *, tenant_id: str) -> AdapterSession: ...
|
||||
|
||||
def execute(
|
||||
self, *, session: AdapterSession, action: str, payload: Mapping[str, Any]
|
||||
) -> AdapterResult: ...
|
||||
|
||||
def cancel(self, *, session: AdapterSession) -> AdapterResult: ...
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class PortalAdapter(ChannelAdapter, Protocol):
|
||||
"""Marker protocol for user-facing portal adapters."""
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
"""Platform-neutral authentication-session state machine."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, replace
|
||||
from enum import Enum
|
||||
from typing import Mapping, Any
|
||||
|
||||
|
||||
class AuthSessionState(str, Enum):
|
||||
CREATED = "CREATED"
|
||||
WAITING_FOR_USER = "WAITING_FOR_USER"
|
||||
USER_CONFIRMED = "USER_CONFIRMED"
|
||||
AUTHENTICATED = "AUTHENTICATED"
|
||||
EXPIRED = "EXPIRED"
|
||||
CANCELLED = "CANCELLED"
|
||||
FAILED = "FAILED"
|
||||
|
||||
|
||||
_TRANSITIONS = {
|
||||
AuthSessionState.CREATED: {
|
||||
AuthSessionState.WAITING_FOR_USER,
|
||||
AuthSessionState.CANCELLED,
|
||||
AuthSessionState.FAILED,
|
||||
},
|
||||
AuthSessionState.WAITING_FOR_USER: {
|
||||
AuthSessionState.USER_CONFIRMED,
|
||||
AuthSessionState.EXPIRED,
|
||||
AuthSessionState.CANCELLED,
|
||||
AuthSessionState.FAILED,
|
||||
},
|
||||
AuthSessionState.USER_CONFIRMED: {
|
||||
AuthSessionState.AUTHENTICATED,
|
||||
AuthSessionState.EXPIRED,
|
||||
AuthSessionState.CANCELLED,
|
||||
AuthSessionState.FAILED,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class InvalidAuthSessionTransition(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AuthSession:
|
||||
session_id: str
|
||||
tenant_id: str
|
||||
state: AuthSessionState = AuthSessionState.CREATED
|
||||
expires_at: str | None = None
|
||||
metadata: Mapping[str, Any] | None = None
|
||||
|
||||
def transition(self, target: AuthSessionState) -> "AuthSession":
|
||||
allowed = _TRANSITIONS.get(self.state, set())
|
||||
if target not in allowed:
|
||||
raise InvalidAuthSessionTransition(f"{self.state.value} -> {target.value}")
|
||||
return replace(self, state=target)
|
||||
|
||||
@property
|
||||
def terminal(self) -> bool:
|
||||
return self.state in {
|
||||
AuthSessionState.AUTHENTICATED,
|
||||
AuthSessionState.EXPIRED,
|
||||
AuthSessionState.CANCELLED,
|
||||
AuthSessionState.FAILED,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
"""Incubator-only versioned contract primitives.
|
||||
|
||||
This module distills the reusable contract-first practices observed in the
|
||||
Personal Weixin golden-contract work without importing channel-specific fields
|
||||
into the AIOS control plane. It is intentionally not imported by production
|
||||
AIOS V2R1 modules.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Any, Mapping, Sequence
|
||||
|
||||
|
||||
class ContractViolation(ValueError):
|
||||
"""A payload does not satisfy its declared contract."""
|
||||
|
||||
|
||||
class Compatibility(str, Enum):
|
||||
EXACT = "exact"
|
||||
BACKWARD_COMPATIBLE = "backward_compatible"
|
||||
INCOMPATIBLE = "incompatible"
|
||||
|
||||
|
||||
def canonical_json_bytes(value: Mapping[str, Any]) -> bytes:
|
||||
return json.dumps(
|
||||
dict(value),
|
||||
ensure_ascii=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
default=str,
|
||||
).encode("utf-8")
|
||||
|
||||
|
||||
def payload_digest(value: Mapping[str, Any]) -> str:
|
||||
return hashlib.sha256(canonical_json_bytes(value)).hexdigest()
|
||||
|
||||
|
||||
@dataclass(frozen=True, order=True)
|
||||
class ContractVersion:
|
||||
major: int
|
||||
minor: int
|
||||
patch: int = 0
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if min(self.major, self.minor, self.patch) < 0:
|
||||
raise ValueError("contract version components must be non-negative")
|
||||
|
||||
@classmethod
|
||||
def parse(cls, value: str) -> "ContractVersion":
|
||||
parts = str(value or "").strip().split(".")
|
||||
if len(parts) not in {2, 3} or any(not item.isdigit() for item in parts):
|
||||
raise ValueError("contract version must use MAJOR.MINOR[.PATCH]")
|
||||
numbers = [int(item) for item in parts]
|
||||
if len(numbers) == 2:
|
||||
numbers.append(0)
|
||||
return cls(*numbers)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.major}.{self.minor}.{self.patch}"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class VersionedContract:
|
||||
name: str
|
||||
version: ContractVersion
|
||||
required_fields: Sequence[str]
|
||||
optional_fields: Sequence[str] = ()
|
||||
forbidden_fields: Sequence[str] = ()
|
||||
secret_fields: Sequence[str] = ()
|
||||
allow_unknown_fields: bool = False
|
||||
metadata: Mapping[str, Any] = field(default_factory=dict)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
name = str(self.name or "").strip()
|
||||
if not name:
|
||||
raise ValueError("contract name is required")
|
||||
object.__setattr__(self, "name", name)
|
||||
required = frozenset(str(item).strip() for item in self.required_fields if str(item).strip())
|
||||
optional = frozenset(str(item).strip() for item in self.optional_fields if str(item).strip())
|
||||
forbidden = frozenset(str(item).strip() for item in self.forbidden_fields if str(item).strip())
|
||||
secrets = frozenset(str(item).strip() for item in self.secret_fields if str(item).strip())
|
||||
if required & forbidden:
|
||||
raise ValueError("required fields cannot also be forbidden")
|
||||
if required & optional:
|
||||
raise ValueError("required and optional fields must be disjoint")
|
||||
object.__setattr__(self, "required_fields", tuple(sorted(required)))
|
||||
object.__setattr__(self, "optional_fields", tuple(sorted(optional)))
|
||||
object.__setattr__(self, "forbidden_fields", tuple(sorted(forbidden)))
|
||||
object.__setattr__(self, "secret_fields", tuple(sorted(secrets)))
|
||||
object.__setattr__(self, "metadata", json.loads(json.dumps(dict(self.metadata), default=str)))
|
||||
|
||||
@property
|
||||
def allowed_fields(self) -> frozenset[str]:
|
||||
return frozenset((*self.required_fields, *self.optional_fields))
|
||||
|
||||
def validate(self, payload: Mapping[str, Any]) -> dict[str, Any]:
|
||||
normalized = json.loads(json.dumps(dict(payload), ensure_ascii=False, default=str))
|
||||
fields = set(normalized)
|
||||
missing = sorted(set(self.required_fields) - fields)
|
||||
forbidden = sorted(set(self.forbidden_fields) & fields)
|
||||
unknown = sorted(fields - self.allowed_fields)
|
||||
if missing:
|
||||
raise ContractViolation(f"missing required fields: {missing}")
|
||||
if forbidden:
|
||||
raise ContractViolation(f"forbidden fields present: {forbidden}")
|
||||
if unknown and not self.allow_unknown_fields:
|
||||
raise ContractViolation(f"unknown fields present: {unknown}")
|
||||
return normalized
|
||||
|
||||
def redact(self, payload: Mapping[str, Any]) -> dict[str, Any]:
|
||||
normalized = json.loads(json.dumps(dict(payload), ensure_ascii=False, default=str))
|
||||
for field_name in self.secret_fields:
|
||||
if field_name in normalized:
|
||||
normalized[field_name] = "***"
|
||||
return normalized
|
||||
|
||||
def compatibility_with(self, previous: "VersionedContract") -> Compatibility:
|
||||
if self.name != previous.name:
|
||||
return Compatibility.INCOMPATIBLE
|
||||
if self.version == previous.version and self == previous:
|
||||
return Compatibility.EXACT
|
||||
if self.version.major != previous.version.major:
|
||||
return Compatibility.INCOMPATIBLE
|
||||
if not set(previous.required_fields).issubset(self.allowed_fields):
|
||||
return Compatibility.INCOMPATIBLE
|
||||
if set(previous.allowed_fields) & set(self.forbidden_fields):
|
||||
return Compatibility.INCOMPATIBLE
|
||||
if not self.allow_unknown_fields and not set(previous.allowed_fields).issubset(self.allowed_fields):
|
||||
return Compatibility.INCOMPATIBLE
|
||||
return Compatibility.BACKWARD_COMPATIBLE
|
||||
|
||||
def descriptor(self) -> dict[str, Any]:
|
||||
value = {
|
||||
"name": self.name,
|
||||
"version": str(self.version),
|
||||
"required_fields": list(self.required_fields),
|
||||
"optional_fields": list(self.optional_fields),
|
||||
"forbidden_fields": list(self.forbidden_fields),
|
||||
"secret_fields": list(self.secret_fields),
|
||||
"allow_unknown_fields": self.allow_unknown_fields,
|
||||
"metadata": dict(self.metadata),
|
||||
}
|
||||
value["descriptor_digest"] = payload_digest(value)
|
||||
return value
|
||||
@@ -0,0 +1,90 @@
|
||||
"""Evidence integrity and replacement-audit primitives."""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from dataclasses import dataclass, field, replace
|
||||
from pathlib import Path
|
||||
from typing import Any, Mapping, Sequence
|
||||
|
||||
|
||||
class EvidenceIntegrityError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def sha256_file(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as stream:
|
||||
for chunk in iter(lambda: stream.read(65536), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LogReplacement:
|
||||
artifact: str
|
||||
previous_sha256: str
|
||||
replacement_sha256: str
|
||||
reason: str
|
||||
replaced_at: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EvidenceManifest:
|
||||
command: Sequence[str]
|
||||
start_time: str
|
||||
end_time: str
|
||||
exit_code: int
|
||||
artifacts: Mapping[str, str]
|
||||
result_summary: Mapping[str, Any] = field(default_factory=dict)
|
||||
reviewer_status: str = "PENDING"
|
||||
supersedes: Sequence[str] = ()
|
||||
replacement_audit: Sequence[LogReplacement] = ()
|
||||
|
||||
def validate(self, root: Path) -> None:
|
||||
for relative, expected in self.artifacts.items():
|
||||
path = (root / relative).resolve()
|
||||
if root.resolve() not in path.parents:
|
||||
raise EvidenceIntegrityError("artifact escapes evidence root")
|
||||
if not path.is_file():
|
||||
raise EvidenceIntegrityError(f"artifact missing: {relative}")
|
||||
actual = sha256_file(path)
|
||||
if actual != expected:
|
||||
raise EvidenceIntegrityError(f"artifact hash mismatch: {relative}")
|
||||
|
||||
def replace_artifact(
|
||||
self, *, artifact: str, replacement_sha256: str, reason: str, replaced_at: str
|
||||
) -> "EvidenceManifest":
|
||||
previous = self.artifacts.get(artifact)
|
||||
if previous is None:
|
||||
raise EvidenceIntegrityError("replacement target is not registered")
|
||||
if not reason.strip():
|
||||
raise EvidenceIntegrityError("replacement reason is required")
|
||||
updated = dict(self.artifacts)
|
||||
updated[artifact] = replacement_sha256
|
||||
audit = LogReplacement(
|
||||
artifact=artifact,
|
||||
previous_sha256=previous,
|
||||
replacement_sha256=replacement_sha256,
|
||||
reason=reason,
|
||||
replaced_at=replaced_at,
|
||||
)
|
||||
return replace(
|
||||
self,
|
||||
artifacts=updated,
|
||||
replacement_audit=(*self.replacement_audit, audit),
|
||||
)
|
||||
|
||||
def as_dict(self) -> dict[str, Any]:
|
||||
return {
|
||||
"command": list(self.command),
|
||||
"start_time": self.start_time,
|
||||
"end_time": self.end_time,
|
||||
"exit_code": self.exit_code,
|
||||
"artifacts": dict(self.artifacts),
|
||||
"result_summary": json.loads(json.dumps(dict(self.result_summary), default=str)),
|
||||
"reviewer_status": self.reviewer_status,
|
||||
"supersedes": list(self.supersedes),
|
||||
"replacement_audit": [item.__dict__ for item in self.replacement_audit],
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
"""Provider-neutral runner lifecycle contracts."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Any, Mapping, Protocol, runtime_checkable
|
||||
|
||||
|
||||
class RunnerStatus(str, Enum):
|
||||
PROVISIONING = "provisioning"
|
||||
READY = "ready"
|
||||
RUNNING = "running"
|
||||
CANCELLED = "cancelled"
|
||||
FAILED = "failed"
|
||||
DEPROVISIONED = "deprovisioned"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RunnerHandle:
|
||||
runner_id: str
|
||||
provider: str
|
||||
status: RunnerStatus
|
||||
metadata: Mapping[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RunnerHealth:
|
||||
ok: bool
|
||||
status: RunnerStatus
|
||||
observed_at: str
|
||||
details: Mapping[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RunnerExecution:
|
||||
execution_id: str
|
||||
runner_id: str
|
||||
status: RunnerStatus
|
||||
exit_code: int | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RunnerEvidence:
|
||||
execution_id: str
|
||||
artifacts: Mapping[str, str]
|
||||
summary: Mapping[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class RunnerProvider(Protocol):
|
||||
def provision(self, request: Mapping[str, Any]) -> RunnerHandle: ...
|
||||
|
||||
def health(self, handle: RunnerHandle) -> RunnerHealth: ...
|
||||
|
||||
def execute(
|
||||
self, handle: RunnerHandle, request: Mapping[str, Any]
|
||||
) -> RunnerExecution: ...
|
||||
|
||||
def cancel(self, execution: RunnerExecution) -> RunnerExecution: ...
|
||||
|
||||
def collect_evidence(self, execution: RunnerExecution) -> RunnerEvidence: ...
|
||||
|
||||
def deprovision(self, handle: RunnerHandle) -> RunnerHandle: ...
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
"""Explicit worktree ownership and release-blocking classification."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, replace
|
||||
from enum import Enum
|
||||
from pathlib import PurePosixPath
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
class WorktreeState(str, Enum):
|
||||
ACTIVE = "ACTIVE"
|
||||
READY_TO_COMMIT = "READY_TO_COMMIT"
|
||||
COMMITTED = "COMMITTED"
|
||||
ARCHIVED = "ARCHIVED"
|
||||
OWNER_REVIEW_REQUIRED = "OWNER_REVIEW_REQUIRED"
|
||||
TRUSTED_RUNTIME_DIRTY = "TRUSTED_RUNTIME_DIRTY"
|
||||
STALE_CLEANUP_REQUIRED = "STALE_CLEANUP_REQUIRED"
|
||||
REMOVED = "REMOVED"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class WorktreeRecord:
|
||||
path: str
|
||||
owner: str
|
||||
task_id: str
|
||||
branch: str
|
||||
base_oid: str
|
||||
current_oid: str
|
||||
heartbeat_at: str | None = None
|
||||
dirty_count: int = 0
|
||||
locked: bool = False
|
||||
prunable: bool = False
|
||||
active_runtime: bool = False
|
||||
archive_oid: str | None = None
|
||||
removed_at: str | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if not PurePosixPath(self.path).is_absolute():
|
||||
raise ValueError("worktree path must be absolute")
|
||||
if not self.owner or not self.task_id or not self.current_oid:
|
||||
raise ValueError("owner, task_id and current_oid are required")
|
||||
if self.dirty_count < 0:
|
||||
raise ValueError("dirty_count cannot be negative")
|
||||
|
||||
@property
|
||||
def state(self) -> WorktreeState:
|
||||
if self.removed_at:
|
||||
return WorktreeState.REMOVED
|
||||
if self.locked or self.prunable:
|
||||
return WorktreeState.STALE_CLEANUP_REQUIRED
|
||||
if self.dirty_count and self.active_runtime:
|
||||
return WorktreeState.TRUSTED_RUNTIME_DIRTY
|
||||
if self.dirty_count:
|
||||
return WorktreeState.OWNER_REVIEW_REQUIRED
|
||||
if self.archive_oid:
|
||||
return WorktreeState.ARCHIVED
|
||||
if self.current_oid != self.base_oid:
|
||||
return WorktreeState.READY_TO_COMMIT
|
||||
return WorktreeState.ACTIVE
|
||||
|
||||
@property
|
||||
def release_blocking(self) -> bool:
|
||||
return self.state in {
|
||||
WorktreeState.OWNER_REVIEW_REQUIRED,
|
||||
WorktreeState.TRUSTED_RUNTIME_DIRTY,
|
||||
WorktreeState.STALE_CLEANUP_REQUIRED,
|
||||
}
|
||||
|
||||
|
||||
class WorktreeRegistry:
|
||||
def __init__(self, records: Iterable[WorktreeRecord] = ()) -> None:
|
||||
self._records: dict[str, WorktreeRecord] = {}
|
||||
for record in records:
|
||||
self.upsert(record)
|
||||
|
||||
def upsert(self, record: WorktreeRecord) -> None:
|
||||
self._records[record.path] = record
|
||||
|
||||
def get(self, path: str) -> WorktreeRecord:
|
||||
return self._records[path]
|
||||
|
||||
def mark_archived(self, path: str, archive_oid: str) -> WorktreeRecord:
|
||||
if not archive_oid:
|
||||
raise ValueError("archive_oid is required")
|
||||
current = self.get(path)
|
||||
if current.dirty_count:
|
||||
raise ValueError("dirty worktree cannot be marked archived")
|
||||
updated = replace(current, archive_oid=archive_oid)
|
||||
self.upsert(updated)
|
||||
return updated
|
||||
|
||||
def release_blockers(self) -> tuple[WorktreeRecord, ...]:
|
||||
return tuple(
|
||||
sorted(
|
||||
(record for record in self._records.values() if record.release_blocking),
|
||||
key=lambda item: item.path,
|
||||
)
|
||||
)
|
||||
|
||||
def snapshot(self) -> tuple[WorktreeRecord, ...]:
|
||||
return tuple(sorted(self._records.values(), key=lambda item: item.path))
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Adoption Plan
|
||||
|
||||
1. Keep all candidates on the future incubator branch.
|
||||
2. Require a named product requirement and threat model for each candidate.
|
||||
3. Validate API stability, tenant isolation, secret handling and rollback independently.
|
||||
4. Adopt one module at a time through a future release branch.
|
||||
5. Never merge this branch into `V2R1C1`.
|
||||
|
||||
Current status for every module is `INCUBATING`; none is approved for production.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Incubator Architecture
|
||||
|
||||
The incubator is a sibling capability area under `active-runtime/app/aios_incubator`. It has no import edge from `aios_v2`, `aios_openclaw` or `aios_hermes`.
|
||||
|
||||
| Module | Boundary |
|
||||
|---|---|
|
||||
| `contracts` | Versioning, required/optional/forbidden fields, compatibility and redaction |
|
||||
| `adapters` | Channel-neutral health, capability, session and result protocols |
|
||||
| `worktree_governance` | Registry facts, state classification and release blockers |
|
||||
| `runners` | Provider-neutral provision/health/execute/cancel/evidence/deprovision contract |
|
||||
| `evidence` | Artifact hashes, reviewer state, supersession and log replacement audit |
|
||||
| `auth_session` | Generic user-mediated authentication lifecycle |
|
||||
|
||||
No module owns a production queue, lease, task state machine, credential store or deployment controller.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Feature Matrix
|
||||
|
||||
| Capability | Source category | Adopted | Explicit exclusions | Status |
|
||||
|---|---|---|---|---|
|
||||
| Contract SDK | Golden Contract | schema semantics, compatibility, redaction, tests | Weixin fields and flows | INCUBATING |
|
||||
| Adapter protocols | Portal Adapter | generic health/capability/session/result | Weixin implementation | INCUBATING |
|
||||
| Worktree governance | Concurrency tasks | owner/task/OID/heartbeat/dirty/lock/prune/release blocking | task-specific ACL probes | INCUBATING |
|
||||
| Runner provider | Spark provisioning | generic lifecycle methods | Spark credentials and fixed host paths | INCUBATING |
|
||||
| Evidence framework | B3 hardening | manifest/hash/reviewer/supersession/replacement audit | Personal-Weixin evidence facts | INCUBATING |
|
||||
| Auth session | Web login contract | generic states and transitions | QR, cookies and account identifiers | INCUBATING |
|
||||
@@ -0,0 +1,14 @@
|
||||
# AIOS Future Incubator
|
||||
|
||||
This tree contains channel-neutral capabilities distilled from archived AIOS task worktrees. It is intentionally excluded from `V2R1C1` and must not be imported by production AIOS modules without a separate adoption review.
|
||||
|
||||
Current candidates:
|
||||
|
||||
- versioned contract SDK;
|
||||
- channel/portal adapter protocols;
|
||||
- worktree ownership and release-blocking registry;
|
||||
- provider-neutral runner lifecycle;
|
||||
- evidence integrity and replacement audit;
|
||||
- authentication-session state machine.
|
||||
|
||||
Platform implementations, credentials, cookies, QR payloads, account fields, runtime logs and fixed deployment paths are excluded.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Worktree Archive Manifest
|
||||
|
||||
Status: `CLEANUP_REVIEWED_RELEASE_STOPPED`
|
||||
|
||||
Release gate: `STOP_PREPRODUCTION`
|
||||
|
||||
The machine-readable authority is `.agent/results/incubator/WORKTREE-ARCHIVE-MANIFEST.json`. Ten of ten named worktrees were reviewed: seven task worktrees were archived, pushed, verified and removed; three active long-term roots were retained and made clean.
|
||||
|
||||
The incubator is not a release source and was not merged into V2R1C1. The archived Portal Adapter is explicitly rejected for adoption because its portal-surface contract tests fail; its Django test is also blocked by a missing host dependency.
|
||||
|
||||
| # | Worktree | Decision | Archive / retained OID | Validation |
|
||||
|---:|---|---|---|---|
|
||||
| 1 | `TASK-V2R1-PERSONAL-WEIXIN-GOLDEN-CONTRACT-R2-20260715` | removed | `fe86e35257113853ba08b1438bc820bb4554e6e8` | PASS: contract harness |
|
||||
| 2 | `TASK-V2R1-PERSONAL-WEIXIN-PORTAL-ADAPTER-20260715` | removed | `79f529a91f8bf7cf3237feb01e98807ab5ad10b3` | REJECTED_FOR_ADOPTION: official-login tests pass; portal-surface tests fail; Django test blocked by missing dependency |
|
||||
| 3 | `TASK-WORKTREE-CONCURRENCY-CHATGPT-20260715T055106UTC8` | removed | `f0feed397dc67edab7daa16f0cdb5b553eca5668` | PASS: evidence integrity and zero-secret scan |
|
||||
| 4 | `TASK-CODEX-AIOS-SPARK-RUNNER-PROVISIONING-20260717T103500UTC8` | removed | `d97cd3642528bd3373fe4cf5161a5d99c0b9285f` | PASS: 27 pytest tests |
|
||||
| 5 | `TASK-OPENCLAW-V2R1-PERSONAL-WEIXIN-B3-EVIDENCE-HARDEN-20260716T133000UTC8` | removed | `2ddbc456124c02707dc23e8d6c241826d7026575` | PASS: compile, canonical template JSON and CLI help |
|
||||
| 6 | `TASK-V2R1-OPENCLAW-WEIXIN-WEB-LOGIN-CONTRACT-20260715` | removed | `d03b4ee34debe6e30ce2af233e2226b02e6dd029` | PASS: Node contract tests |
|
||||
| 7 | `TASK-WORKTREE-CONCURRENCY-OPENCLAW-20260715T055106UTC8` | removed | `d9a85bb86cc09c117d63de803a404ee5a864e7c1` | PASS: evidence integrity and zero-secret scan |
|
||||
| 8 | `codex-long-term-root` | retained-active | `0193a12b57c4974888b25753fd2bfb1f2028909b` | PASS: 55 untracked entries exactly matched 55 registered child worktrees; final clean |
|
||||
| 9 | `controller-long-term-root` | retained-active | `102d2306f05397374fc91c711767021c5d9f41f9` | PASS: watcher, model API and autonomous controller active; final clean |
|
||||
| 10 | `openclaw-long-term-root` | retained-active | `633c8308f53b0ad04ce054ceafb624cb0904cbf4` | PASS: 2 untracked entries exactly matched 2 registered child worktrees; final clean |
|
||||
|
||||
## Independent review
|
||||
|
||||
Reviewer: `gpt-5.3-codex-spark-validator`
|
||||
|
||||
Cleanup review: `PASS`; release decision: `STOP_PREPRODUCTION`. The reviewer matched ten Manifest records, seven archive/removal OID pairs and three retained roots, then preserved the stop decision because `portal-surface` and `seed-ucc` returned non-zero.
|
||||
@@ -0,0 +1,12 @@
|
||||
# Worktree Cleanup Authorization
|
||||
|
||||
Owner authorization source: `AIOS V2R1C1 最终归档、清理、验证与上线执行命令`, received `2026-07-21`.
|
||||
|
||||
The authorization is limited to the ten named dirty worktrees. Before removal, each task worktree requires status/diff evidence, zero secret violations, a pushed archive OID, local/remote OID equality and a clean workspace. Active long-term worktrees must be retained when a service depends on them and must be cleaned without deleting the service path.
|
||||
|
||||
Forbidden cleanup methods:
|
||||
|
||||
- `git reset --hard`;
|
||||
- `git clean -fdx`;
|
||||
- deleting active Controller or OpenClaw roots;
|
||||
- deleting content before its archive OID and artifact hashes are verified.
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "branch",
|
||||
"branch": "patch/chatgpt/AIOS-V2R1-FUTURE-INCUBATOR-20260721",
|
||||
"base": "fb739f6e8ff9c031d319221ace68dce157694086",
|
||||
"created_by": "chatgpt-gitops-bot",
|
||||
"created_at": "2026-07-21T06:30:22Z",
|
||||
"direct_main_push": false,
|
||||
"owner_approval_required": true
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
"""Incubator-only test suite."""
|
||||
@@ -0,0 +1,30 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
APP = Path(__file__).resolve().parents[2] / "active-runtime" / "app"
|
||||
sys.path.insert(0, str(APP))
|
||||
|
||||
from aios_incubator.adapters import AdapterCapability, AdapterResult, AdapterSession
|
||||
from aios_incubator.runners import RunnerExecution, RunnerStatus
|
||||
|
||||
|
||||
class AdapterAndRunnerContractTests(unittest.TestCase):
|
||||
def test_adapter_values_are_channel_neutral(self) -> None:
|
||||
session = AdapterSession("session-1", "tenant-1", "waiting")
|
||||
capability = AdapterCapability("messages.read")
|
||||
result = AdapterResult(True, "completed", {"count": 1})
|
||||
self.assertEqual(session.tenant_id, "tenant-1")
|
||||
self.assertEqual(capability.version, "1")
|
||||
self.assertTrue(result.ok)
|
||||
|
||||
def test_runner_execution_has_provider_neutral_status(self) -> None:
|
||||
execution = RunnerExecution("exec-1", "runner-1", RunnerStatus.RUNNING)
|
||||
self.assertEqual(execution.status, RunnerStatus.RUNNING)
|
||||
self.assertIsNone(execution.exit_code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,34 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
APP = Path(__file__).resolve().parents[2] / "active-runtime" / "app"
|
||||
sys.path.insert(0, str(APP))
|
||||
|
||||
from aios_incubator.auth_session import (
|
||||
AuthSession,
|
||||
AuthSessionState,
|
||||
InvalidAuthSessionTransition,
|
||||
)
|
||||
|
||||
|
||||
class AuthSessionTests(unittest.TestCase):
|
||||
def test_successful_authentication_flow(self) -> None:
|
||||
session = AuthSession("session-1", "tenant-1")
|
||||
session = session.transition(AuthSessionState.WAITING_FOR_USER)
|
||||
session = session.transition(AuthSessionState.USER_CONFIRMED)
|
||||
session = session.transition(AuthSessionState.AUTHENTICATED)
|
||||
self.assertTrue(session.terminal)
|
||||
|
||||
def test_expiry_cancel_failure_and_invalid_transition(self) -> None:
|
||||
waiting = AuthSession("s", "t").transition(AuthSessionState.WAITING_FOR_USER)
|
||||
for target in (AuthSessionState.EXPIRED, AuthSessionState.CANCELLED, AuthSessionState.FAILED):
|
||||
self.assertTrue(waiting.transition(target).terminal)
|
||||
with self.assertRaises(InvalidAuthSessionTransition):
|
||||
waiting.transition(AuthSessionState.AUTHENTICATED)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,54 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
APP = Path(__file__).resolve().parents[2] / "active-runtime" / "app"
|
||||
sys.path.insert(0, str(APP))
|
||||
|
||||
from aios_incubator.contracts import (
|
||||
Compatibility,
|
||||
ContractVersion,
|
||||
ContractViolation,
|
||||
VersionedContract,
|
||||
)
|
||||
|
||||
|
||||
class ContractTests(unittest.TestCase):
|
||||
def test_validation_redaction_and_descriptor(self) -> None:
|
||||
contract = VersionedContract(
|
||||
name="channel.request",
|
||||
version=ContractVersion.parse("1.2"),
|
||||
required_fields=("request_id",),
|
||||
optional_fields=("credential_ref",),
|
||||
forbidden_fields=("credential_plain",),
|
||||
secret_fields=("credential_ref",),
|
||||
)
|
||||
value = contract.validate(
|
||||
{"request_id": "request-1", "credential_ref": "secretref:channel/test"}
|
||||
)
|
||||
self.assertEqual(contract.redact(value)["credential_ref"], "***")
|
||||
self.assertEqual(len(contract.descriptor()["descriptor_digest"]), 64)
|
||||
|
||||
def test_missing_forbidden_and_unknown_fields_fail(self) -> None:
|
||||
contract = VersionedContract(
|
||||
name="channel.request",
|
||||
version=ContractVersion(1, 0),
|
||||
required_fields=("request_id",),
|
||||
forbidden_fields=("credential_plain",),
|
||||
)
|
||||
for payload in ({}, {"request_id": "x", "credential_plain": "x"}, {"request_id": "x", "extra": 1}):
|
||||
with self.subTest(payload=payload), self.assertRaises(ContractViolation):
|
||||
contract.validate(payload)
|
||||
|
||||
def test_compatibility_is_explicit(self) -> None:
|
||||
previous = VersionedContract("event", ContractVersion(1, 0), ("id",), ("kind",))
|
||||
compatible = VersionedContract("event", ContractVersion(1, 1), ("id",), ("kind", "trace"))
|
||||
breaking = VersionedContract("event", ContractVersion(2, 0), ("id",))
|
||||
self.assertEqual(compatible.compatibility_with(previous), Compatibility.BACKWARD_COMPATIBLE)
|
||||
self.assertEqual(breaking.compatibility_with(previous), Compatibility.INCOMPATIBLE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,49 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
APP = Path(__file__).resolve().parents[2] / "active-runtime" / "app"
|
||||
sys.path.insert(0, str(APP))
|
||||
|
||||
from aios_incubator.evidence import EvidenceIntegrityError, EvidenceManifest, sha256_file
|
||||
|
||||
|
||||
class EvidenceManifestTests(unittest.TestCase):
|
||||
def test_hash_validation_and_replacement_audit(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
log = root / "step.log"
|
||||
log.write_text("first\n", encoding="utf-8")
|
||||
manifest = EvidenceManifest(
|
||||
command=("validate",),
|
||||
start_time="2026-07-21T00:00:00Z",
|
||||
end_time="2026-07-21T00:00:01Z",
|
||||
exit_code=0,
|
||||
artifacts={"step.log": sha256_file(log)},
|
||||
)
|
||||
manifest.validate(root)
|
||||
log.write_text("replacement\n", encoding="utf-8")
|
||||
with self.assertRaises(EvidenceIntegrityError):
|
||||
manifest.validate(root)
|
||||
replacement = manifest.replace_artifact(
|
||||
artifact="step.log",
|
||||
replacement_sha256=sha256_file(log),
|
||||
reason="superseded invalid harness output",
|
||||
replaced_at="2026-07-21T00:01:00Z",
|
||||
)
|
||||
replacement.validate(root)
|
||||
self.assertEqual(len(replacement.replacement_audit), 1)
|
||||
|
||||
def test_missing_replacement_reason_is_rejected(self) -> None:
|
||||
manifest = EvidenceManifest(("x",), "s", "e", 0, {"a": "0" * 64})
|
||||
with self.assertRaises(EvidenceIntegrityError):
|
||||
manifest.replace_artifact(
|
||||
artifact="a", replacement_sha256="1" * 64, reason="", replaced_at="now"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,58 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
|
||||
APP = Path(__file__).resolve().parents[2] / "active-runtime" / "app"
|
||||
sys.path.insert(0, str(APP))
|
||||
|
||||
from aios_incubator.worktree_governance import (
|
||||
WorktreeRecord,
|
||||
WorktreeRegistry,
|
||||
WorktreeState,
|
||||
)
|
||||
|
||||
|
||||
def record(**values) -> WorktreeRecord:
|
||||
defaults = dict(
|
||||
path="/data/worktrees/task-1",
|
||||
owner="codex",
|
||||
task_id="task-1",
|
||||
branch="task/one",
|
||||
base_oid="a" * 40,
|
||||
current_oid="a" * 40,
|
||||
)
|
||||
defaults.update(values)
|
||||
return WorktreeRecord(**defaults)
|
||||
|
||||
|
||||
class WorktreeGovernanceTests(unittest.TestCase):
|
||||
def test_dirty_and_runtime_dirty_are_release_blocking(self) -> None:
|
||||
dirty = record(dirty_count=2)
|
||||
runtime = replace(dirty, active_runtime=True)
|
||||
self.assertEqual(dirty.state, WorktreeState.OWNER_REVIEW_REQUIRED)
|
||||
self.assertEqual(runtime.state, WorktreeState.TRUSTED_RUNTIME_DIRTY)
|
||||
self.assertTrue(dirty.release_blocking)
|
||||
self.assertTrue(runtime.release_blocking)
|
||||
|
||||
def test_locked_or_prunable_requires_cleanup(self) -> None:
|
||||
self.assertEqual(record(locked=True).state, WorktreeState.STALE_CLEANUP_REQUIRED)
|
||||
self.assertEqual(record(prunable=True).state, WorktreeState.STALE_CLEANUP_REQUIRED)
|
||||
|
||||
def test_registry_refuses_dirty_archive_and_lists_blockers(self) -> None:
|
||||
registry = WorktreeRegistry((record(dirty_count=1),))
|
||||
self.assertEqual(len(registry.release_blockers()), 1)
|
||||
with self.assertRaises(ValueError):
|
||||
registry.mark_archived("/data/worktrees/task-1", "b" * 40)
|
||||
|
||||
def test_clean_record_can_be_archived(self) -> None:
|
||||
registry = WorktreeRegistry((record(),))
|
||||
archived = registry.mark_archived("/data/worktrees/task-1", "b" * 40)
|
||||
self.assertEqual(archived.state, WorktreeState.ARCHIVED)
|
||||
self.assertFalse(archived.release_blocking)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user