feat: support Codex and Claude local surfaces
This commit is contained in:
@@ -267,11 +267,21 @@ Không gọi một turn là CASAN-certified nếu không có trace tương ứng
|
|||||||
|
|
||||||
## Client support
|
## Client support
|
||||||
|
|
||||||
| Client | Chat bình thường tự qua CASAN | Bước bắt buộc |
|
| Client surface | Chat bình thường tự qua CASAN | Bước bắt buộc |
|
||||||
|---|---:|---|
|
|---|---:|---|
|
||||||
| Claude Code CLI/extension | Có | Mở repository dưới dạng trusted project |
|
| Claude Code CLI | Có | Mở repository dưới dạng trusted project |
|
||||||
| Codex CLI/extension | Có | Mở `/hooks`, review và trust hook hash |
|
| Claude Code trong VS Code / JetBrains | Có | Dùng project `.claude/settings.json`; mở trusted project |
|
||||||
| GitHub Copilot Chat | Không | Cài CASAN VSIX và gửi `@casan <prompt>` |
|
| Codex desktop app — Local | Có | Mở `/hooks`, review và trust hook hash |
|
||||||
|
| Codex CLI / IDE extension — Local | Có | Mở `/hooks`, review và trust hook hash |
|
||||||
|
| Codex Cloud / Web | Chưa | Project hook local không phải cloud enforcement boundary |
|
||||||
|
| Claude Desktop / claude.ai | Chưa | Không chạy Claude Code project hooks |
|
||||||
|
| GitHub Copilot Chat | Không tự động | Cài CASAN VSIX và gửi `@casan <prompt>` |
|
||||||
|
|
||||||
|
`--client claude` cấu hình **Claude Code runtime**, dùng chung cho CLI, VS Code
|
||||||
|
extension và JetBrains integration. `--client codex` cấu hình **Codex local
|
||||||
|
runtime**, dùng chung cho desktop app, CLI và IDE extension. CASAN không tạo
|
||||||
|
adapter trùng lặp theo từng UI; cùng một project hook và cùng một integrity pin
|
||||||
|
được dùng trên các local surface.
|
||||||
|
|
||||||
Các cách chọn client:
|
Các cách chọn client:
|
||||||
|
|
||||||
|
|||||||
@@ -193,12 +193,16 @@ cấp có chủ đích bằng cách chạy lại `casan init --runtime vendored`
|
|||||||
|
|
||||||
### Capability theo client
|
### Capability theo client
|
||||||
|
|
||||||
| Lựa chọn | Trải nghiệm | Bước trust/cài đặt bắt buộc |
|
| Lựa chọn | Local surface được hỗ trợ | Bước trust/cài đặt bắt buộc |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `claude` | Claude Code CLI và extension chính thức dùng project hooks | Mở trusted project; hook chạy tự động |
|
| `claude` | Claude Code CLI, VS Code extension và JetBrains integration dùng chung project settings/hooks | Mở trusted project; hook chạy tự động |
|
||||||
| `codex` | Codex CLI và IDE extension dùng `.codex/hooks.json` | Mở `/hooks`, review và trust đúng hook hash |
|
| `codex` | Codex desktop app (Local), CLI và IDE extension (Local) dùng `.codex/hooks.json` | Mở `/hooks`, review và trust đúng hook hash |
|
||||||
| `vscode-copilot` | GitHub Copilot Chat qua route **explicit `@casan`** | Cài VSIX do init tạo/cài; dùng `@casan <prompt>` |
|
| `vscode-copilot` | GitHub Copilot Chat qua route **explicit `@casan`** | Cài VSIX do init tạo/cài; dùng `@casan <prompt>` |
|
||||||
|
|
||||||
|
Không gắn badge project-hook cho Codex Cloud/Web, Claude Desktop hoặc
|
||||||
|
claude.ai. Các surface đó không chạy local project hook; muốn support phải có
|
||||||
|
remote/managed integration riêng và một qualification suite riêng.
|
||||||
|
|
||||||
GitHub Copilot Chat mặc định **không** có public API để CASAN intercept mọi
|
GitHub Copilot Chat mặc định **không** có public API để CASAN intercept mọi
|
||||||
prompt. Chỉ route `@casan` là `casan_owned`; participant detection/built-in
|
prompt. Chỉ route `@casan` là `casan_owned`; participant detection/built-in
|
||||||
Copilot không được quảng bá là certified.
|
Copilot không được quảng bá là certified.
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ Spike tập trung trả lời bốn câu:
|
|||||||
| Claude Code managed/custom SDK | Có | Có | Có | SDK ResultMessage có usage/cost tốt hơn | **GO cho assurance cao hơn** |
|
| Claude Code managed/custom SDK | Có | Có | Có | SDK ResultMessage có usage/cost tốt hơn | **GO cho assurance cao hơn** |
|
||||||
| Codex project hooks | `UserPromptSubmit` | `PreToolUse`, `PostToolUse` | `Stop` | Cần prototype nguồn usage/cost | **GO sau Claude, có điều kiện** |
|
| Codex project hooks | `UserPromptSubmit` | `PreToolUse`, `PostToolUse` | `Stop` | Cần prototype nguồn usage/cost | **GO sau Claude, có điều kiện** |
|
||||||
| Codex managed hooks | Có thể pin bằng policy/requirements | Có, theo coverage docs | Có | Như trên | **GO cho enterprise rollout** |
|
| Codex managed hooks | Có thể pin bằng policy/requirements | Có, theo coverage docs | Có | Như trên | **GO cho enterprise rollout** |
|
||||||
| Claude/Codex VS Code extension | Chưa black-box verify | Chưa verify | Chưa verify | Chưa verify | **CONDITIONAL** |
|
| Claude Code CLI / VS Code / JetBrains | Dùng chung Claude Code project settings | Dùng chung hook contract | Dùng chung hook contract | Như Claude Code project hooks | **SUPPORTED local surfaces** |
|
||||||
|
| Codex desktop / CLI / IDE — Local | Dùng chung Codex project hook layer | Dùng chung hook contract | Dùng chung hook contract | Như Codex project hooks | **SUPPORTED local surfaces; trust bắt buộc** |
|
||||||
| VS Code Chat Participant | Participant sở hữu request được route tới nó | Participant tự điều phối tools | Participant sở hữu response | Tự ghi được usage do model API trả về | **GO cho `@casan`, không phải global interceptor** |
|
| VS Code Chat Participant | Participant sở hữu request được route tới nó | Participant tự điều phối tools | Participant sở hữu response | Tự ghi được usage do model API trả về | **GO cho `@casan`, không phải global interceptor** |
|
||||||
| VS Code participant detection | Auto-route best effort | Như participant nếu được route | Có | Có | **Không đủ để cam kết mọi prompt** |
|
| VS Code participant detection | Auto-route best effort | Như participant nếu được route | Có | Có | **Không đủ để cam kết mọi prompt** |
|
||||||
| Generic Copilot built-in chat | Không có API công khai để intercept toàn bộ prompt | Không có CASAN gate chung | Không | Không | **NO-GO cho tuyên bố transparent absolute** |
|
| Generic Copilot built-in chat | Không có API công khai để intercept toàn bộ prompt | Không có CASAN gate chung | Không | Không | **NO-GO cho tuyên bố transparent absolute** |
|
||||||
@@ -95,7 +96,7 @@ runtime event/telemetry của Codex. Nếu không có nguồn ổn định, reco
|
|||||||
**Verdict Codex:** GO có điều kiện, bắt đầu sau khi Claude adapter dùng chung lifecycle
|
**Verdict Codex:** GO có điều kiện, bắt đầu sau khi Claude adapter dùng chung lifecycle
|
||||||
contract đã pass. Managed hook là đường khuyến nghị khi tổ chức yêu cầu enforcement.
|
contract đã pass. Managed hook là đường khuyến nghị khi tổ chức yêu cầu enforcement.
|
||||||
|
|
||||||
### 4.3 VS Code, Claude/Codex extension và Copilot
|
### 4.3 Local UI surfaces và Copilot
|
||||||
|
|
||||||
VS Code Chat Participant API cho phép extension tạo participant như `@casan` và sở hữu toàn
|
VS Code Chat Participant API cho phép extension tạo participant như `@casan` và sở hữu toàn
|
||||||
bộ prompt được route tới participant đó. Participant detection có thể tự chọn participant
|
bộ prompt được route tới participant đó. Participant detection có thể tự chọn participant
|
||||||
@@ -106,14 +107,20 @@ Language Model Chat Provider chỉ xử lý request khi model/provider đó đư
|
|||||||
không phải interceptor cho mọi model/chat có sẵn. Enterprise policy còn có thể vô hiệu
|
không phải interceptor cho mọi model/chat có sẵn. Enterprise policy còn có thể vô hiệu
|
||||||
provider kiểu BYOK.
|
provider kiểu BYOK.
|
||||||
|
|
||||||
Claude Code hoặc Codex extension có thể tái sử dụng CLI/project hooks, nhưng spike chưa có
|
Claude Code settings được tài liệu chính thức xác nhận dùng chung giữa CLI và VS Code
|
||||||
black-box evidence trên máy hiện tại. Mỗi extension/version phải được test độc lập trước khi
|
extension; cùng settings precedence áp dụng cho CLI, VS Code và JetBrains. Codex official
|
||||||
gắn badge `verified`.
|
surface matrix xác định Hook áp dụng cho desktop app, CLI và IDE extension. Vì vậy CASAN
|
||||||
|
dùng một adapter theo runtime, không fork adapter theo UI.
|
||||||
|
|
||||||
|
Qualification vẫn phải phân biệt `supported contract` với `tested build`: mỗi release nên
|
||||||
|
smoke-test ít nhất một build của từng UI family và không suy rộng local project hook sang
|
||||||
|
Codex Cloud/Web, Claude Desktop hoặc claude.ai.
|
||||||
|
|
||||||
**Verdict VS Code:**
|
**Verdict VS Code:**
|
||||||
|
|
||||||
|
- GO cho Claude Code local trên CLI, VS Code và JetBrains qua shared project settings;
|
||||||
|
- GO có trust gate cho Codex local trên desktop app, CLI và IDE extension;
|
||||||
- GO cho CASAN-owned `@casan` Chat Participant;
|
- GO cho CASAN-owned `@casan` Chat Participant;
|
||||||
- CONDITIONAL cho native Claude/Codex extension tới khi black-box test pass;
|
|
||||||
- NO-GO cho tuyên bố CASAN tự động intercept mọi prompt built-in Copilot bằng public API.
|
- NO-GO cho tuyên bố CASAN tự động intercept mọi prompt built-in Copilot bằng public API.
|
||||||
|
|
||||||
## 5. Kiến trúc prototype được chọn
|
## 5. Kiến trúc prototype được chọn
|
||||||
@@ -233,8 +240,9 @@ Hướng này khả thi và nên làm theo đúng thứ tự đã yêu cầu:
|
|||||||
1. **Claude Code:** có đủ lifecycle hook để làm MVP; đây là spike/prototype đầu tiên.
|
1. **Claude Code:** có đủ lifecycle hook để làm MVP; đây là spike/prototype đầu tiên.
|
||||||
2. **Codex:** API hook phù hợp, reuse common bridge; managed hooks đáng ưu tiên cho rollout
|
2. **Codex:** API hook phù hợp, reuse common bridge; managed hooks đáng ưu tiên cho rollout
|
||||||
tổ chức.
|
tổ chức.
|
||||||
3. **VS Code:** xác minh native Claude/Codex extension riêng; bổ sung `@casan` participant
|
3. **Local UI surfaces:** dùng chung adapter theo runtime; smoke-test desktop/CLI/IDE trong
|
||||||
cho đường CASAN-owned. Không hứa intercept toàn bộ Copilot built-in chat.
|
release qualification. Bổ sung `@casan` participant cho đường CASAN-owned và không hứa
|
||||||
|
intercept toàn bộ Copilot built-in chat.
|
||||||
|
|
||||||
Kết quả được gọi là “đi qua CASAN” khi có trace/admission/finalize và certification strength
|
Kết quả được gọi là “đi qua CASAN” khi có trace/admission/finalize và certification strength
|
||||||
rõ ràng, không chỉ vì project có một file hướng dẫn hoặc hook telemetry.
|
rõ ràng, không chỉ vì project có một file hướng dẫn hoặc hook telemetry.
|
||||||
@@ -242,6 +250,10 @@ rõ ràng, không chỉ vì project có một file hướng dẫn hoặc hook te
|
|||||||
## 11. Nguồn chính thức
|
## 11. Nguồn chính thức
|
||||||
|
|
||||||
- [Claude Code hooks](https://code.claude.com/docs/en/hooks)
|
- [Claude Code hooks](https://code.claude.com/docs/en/hooks)
|
||||||
|
- [Claude Code IDE integrations](https://code.claude.com/docs/en/ide-integrations)
|
||||||
|
- [Claude Code settings](https://code.claude.com/docs/en/settings)
|
||||||
|
- [Codex hooks](https://learn.chatgpt.com/docs/hooks)
|
||||||
|
- [Codex surface glossary](https://learn.chatgpt.com/docs/glossary)
|
||||||
- [Claude Code status line](https://code.claude.com/docs/en/statusline)
|
- [Claude Code status line](https://code.claude.com/docs/en/statusline)
|
||||||
- [Claude Agent SDK cost tracking](https://code.claude.com/docs/en/agent-sdk/cost-tracking)
|
- [Claude Agent SDK cost tracking](https://code.claude.com/docs/en/agent-sdk/cost-tracking)
|
||||||
- [Codex hooks](https://learn.chatgpt.com/docs/hooks)
|
- [Codex hooks](https://learn.chatgpt.com/docs/hooks)
|
||||||
|
|||||||
@@ -60,10 +60,35 @@ VSCODE_EXTENSION_IDS = {
|
|||||||
PROMPT_MARKER_START = "<!-- CASAN_PROMPT_ENFORCEMENT_START -->"
|
PROMPT_MARKER_START = "<!-- CASAN_PROMPT_ENFORCEMENT_START -->"
|
||||||
PROMPT_MARKER_END = "<!-- CASAN_PROMPT_ENFORCEMENT_END -->"
|
PROMPT_MARKER_END = "<!-- CASAN_PROMPT_ENFORCEMENT_END -->"
|
||||||
CLIENT_LABELS = {
|
CLIENT_LABELS = {
|
||||||
"claude": "Claude Code",
|
"claude": "Claude Code (CLI + VS Code + JetBrains)",
|
||||||
"codex": "Codex",
|
"codex": "Codex local (desktop + CLI + IDE)",
|
||||||
"vscode-copilot": "VS Code / @casan",
|
"vscode-copilot": "VS Code / @casan",
|
||||||
}
|
}
|
||||||
|
CLIENT_SURFACES = {
|
||||||
|
"claude": {
|
||||||
|
"supported_local": [
|
||||||
|
"claude-code-cli",
|
||||||
|
"claude-code-vscode",
|
||||||
|
"claude-code-jetbrains",
|
||||||
|
],
|
||||||
|
"not_covered": ["claude-desktop", "claude-web"],
|
||||||
|
"contract": "shared_claude_code_project_settings",
|
||||||
|
},
|
||||||
|
"codex": {
|
||||||
|
"supported_local": [
|
||||||
|
"codex-desktop-local",
|
||||||
|
"codex-cli",
|
||||||
|
"codex-ide-extension-local",
|
||||||
|
],
|
||||||
|
"not_covered": ["codex-cloud", "codex-web"],
|
||||||
|
"contract": "shared_codex_local_project_hooks",
|
||||||
|
},
|
||||||
|
"vscode-copilot": {
|
||||||
|
"supported_local": ["vscode-copilot-explicit-at-casan"],
|
||||||
|
"not_covered": ["github-copilot-built-in-chat"],
|
||||||
|
"contract": "casan_owned_explicit_route",
|
||||||
|
},
|
||||||
|
}
|
||||||
RUNTIME_MODES = ("managed", "vendored")
|
RUNTIME_MODES = ("managed", "vendored")
|
||||||
|
|
||||||
|
|
||||||
@@ -131,7 +156,7 @@ def _render_init(result):
|
|||||||
print(_color("1", "Next steps"))
|
print(_color("1", "Next steps"))
|
||||||
print(" 1. Run `casan doctor`")
|
print(" 1. Run `casan doctor`")
|
||||||
if "codex" in result["clients"]:
|
if "codex" in result["clients"]:
|
||||||
print(" 2. In Codex, open `/hooks` and trust this project's hook.")
|
print(" 2. In a local Codex client, open `/hooks` and trust this project's hook.")
|
||||||
|
|
||||||
|
|
||||||
def _render_verify(result):
|
def _render_verify(result):
|
||||||
@@ -173,6 +198,9 @@ def _render_doctor(result):
|
|||||||
for client, item in result["client_checks"].items():
|
for client, item in result["client_checks"].items():
|
||||||
label = CLIENT_LABELS.get(client, client)
|
label = CLIENT_LABELS.get(client, client)
|
||||||
print(" %s %s" % (_mark(item.get("ready", False)), label))
|
print(" %s %s" % (_mark(item.get("ready", False)), label))
|
||||||
|
surfaces = item.get("supported_surfaces") or []
|
||||||
|
if surfaces:
|
||||||
|
print(" Surfaces: %s" % ", ".join(surfaces))
|
||||||
reason = item.get("smoke", {}).get("reason")
|
reason = item.get("smoke", {}).get("reason")
|
||||||
if reason and not item.get("ready"):
|
if reason and not item.get("ready"):
|
||||||
print(" %s" % reason)
|
print(" %s" % reason)
|
||||||
@@ -903,8 +931,8 @@ def select_clients(values, interactive):
|
|||||||
while True:
|
while True:
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
"\nClient integrations\n\n"
|
"\nClient integrations\n\n"
|
||||||
" 1) Claude Code (CLI + official VS Code extension)\n"
|
" 1) Claude Code (CLI + VS Code + JetBrains)\n"
|
||||||
" 2) Codex (CLI + official VS Code extension)\n"
|
" 2) Codex local (desktop app + CLI + IDE extension)\n"
|
||||||
" 3) GitHub Copilot in VS Code via explicit @casan route\n\n"
|
" 3) GitHub Copilot in VS Code via explicit @casan route\n\n"
|
||||||
"Select clients (comma-separated) [1,2]: ")
|
"Select clients (comma-separated) [1,2]: ")
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
@@ -1108,7 +1136,7 @@ def cmd_init(args):
|
|||||||
# ── .casan/config.json ──
|
# ── .casan/config.json ──
|
||||||
cfg_dir = os.path.join(target, ".casan")
|
cfg_dir = os.path.join(target, ".casan")
|
||||||
cfg = {
|
cfg = {
|
||||||
"schema_version": "21.1",
|
"schema_version": "21.2",
|
||||||
"project_id": project,
|
"project_id": project,
|
||||||
"created_at": now_iso(),
|
"created_at": now_iso(),
|
||||||
"enforcement_mode": args.mode,
|
"enforcement_mode": args.mode,
|
||||||
@@ -1121,6 +1149,13 @@ def cmd_init(args):
|
|||||||
if "vscode-copilot" in clients else "disabled",
|
if "vscode-copilot" in clients else "disabled",
|
||||||
"vscode-native-copilot": "unsupported_global_interception",
|
"vscode-native-copilot": "unsupported_global_interception",
|
||||||
},
|
},
|
||||||
|
"client_surfaces": {
|
||||||
|
client: {
|
||||||
|
"enabled": client in clients,
|
||||||
|
**surface,
|
||||||
|
}
|
||||||
|
for client, surface in CLIENT_SURFACES.items()
|
||||||
|
},
|
||||||
"harness_version": version,
|
"harness_version": version,
|
||||||
"adoption_model": (
|
"adoption_model": (
|
||||||
"managed-global" if runtime_mode == "managed"
|
"managed-global" if runtime_mode == "managed"
|
||||||
@@ -1511,7 +1546,13 @@ def cmd_doctor(args):
|
|||||||
line.strip().lower() for line in listed.stdout.splitlines() if line.strip()}
|
line.strip().lower() for line in listed.stdout.splitlines() if line.strip()}
|
||||||
|
|
||||||
for client in clients:
|
for client in clients:
|
||||||
item = {"configured": True}
|
surface = CLIENT_SURFACES.get(client, {})
|
||||||
|
item = {
|
||||||
|
"configured": True,
|
||||||
|
"supported_surfaces": list(surface.get("supported_local", [])),
|
||||||
|
"not_covered_surfaces": list(surface.get("not_covered", [])),
|
||||||
|
"surface_contract": surface.get("contract"),
|
||||||
|
}
|
||||||
if client == "claude":
|
if client == "claude":
|
||||||
commands = _commands_in_hooks(
|
commands = _commands_in_hooks(
|
||||||
os.path.join(target, ".claude", "settings.json"))
|
os.path.join(target, ".claude", "settings.json"))
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ installer does this). It wires the four Claude Code lifecycle hooks
|
|||||||
bridge so a developer can type prompts normally while every certified turn still
|
bridge so a developer can type prompts normally while every certified turn still
|
||||||
carries an H1→H7 trace and an H6 record.
|
carries an H1→H7 trace and an H6 record.
|
||||||
|
|
||||||
|
Claude Code shares these project settings across its CLI, official VS Code
|
||||||
|
extension, and JetBrains integration. Claude Desktop and claude.ai are separate
|
||||||
|
products and are not covered by this local project-hook integration.
|
||||||
|
|
||||||
- The hook command resolves the repo root via `$CLAUDE_PROJECT_DIR`, so **no
|
- The hook command resolves the repo root via `$CLAUDE_PROJECT_DIR`, so **no
|
||||||
machine-specific path is committed**.
|
machine-specific path is committed**.
|
||||||
- It invokes `.casan/casan-hook.py`. That bootstrap reads project config,
|
- It invokes `.casan/casan-hook.py`. That bootstrap reads project config,
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ handler calls `.casan/casan-hook.py`, which resolves and verifies the pinned
|
|||||||
global harness before dispatching the Codex adapter. No custom `[casan]` TOML
|
global harness before dispatching the Codex adapter. No custom `[casan]` TOML
|
||||||
keys are required.
|
keys are required.
|
||||||
|
|
||||||
|
This one integration covers the local Codex runtime across the desktop app,
|
||||||
|
CLI, and IDE extension. It does not claim coverage for Codex Cloud/Web, where a
|
||||||
|
repo-local process and its `.casan/casan-hook.py` bootstrap are not the local
|
||||||
|
execution boundary.
|
||||||
|
|
||||||
Important Codex-specific facts (Spike-20 §4.2):
|
Important Codex-specific facts (Spike-20 §4.2):
|
||||||
|
|
||||||
- **Trust review is mandatory.** Codex loads project-local hooks only after the
|
- **Trust review is mandatory.** Codex loads project-local hooks only after the
|
||||||
|
|||||||
@@ -151,6 +151,23 @@ PROJ="$WORK/proj/my-app"; mkdir -p "$PROJ"; echo '{"name":"x"}' > "$PROJ/package
|
|||||||
for f in .casan/config.json .casan/version.lock .casan/agentic.env .casan/casan-hook.py .claude/settings.json .codex/hooks.json .vscode/extensions.json .specify/.gitignore; do
|
for f in .casan/config.json .casan/version.lock .casan/agentic.env .casan/casan-hook.py .claude/settings.json .codex/hooks.json .vscode/extensions.json .specify/.gitignore; do
|
||||||
[[ -f "$PROJ/$f" ]] && pass "init wrote $f" || fail "init missing $f"
|
[[ -f "$PROJ/$f" ]] && pass "init wrote $f" || fail "init missing $f"
|
||||||
done
|
done
|
||||||
|
python3 - "$PROJ/.casan/config.json" <<'PY' \
|
||||||
|
&& pass "config declares supported and excluded client surfaces" \
|
||||||
|
|| fail "config client surface contract is incomplete"
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
d = json.load(open(sys.argv[1], encoding="utf-8"))
|
||||||
|
assert d["schema_version"] == "21.2"
|
||||||
|
assert d["client_surfaces"]["claude"]["supported_local"] == [
|
||||||
|
"claude-code-cli", "claude-code-vscode", "claude-code-jetbrains",
|
||||||
|
]
|
||||||
|
assert d["client_surfaces"]["codex"]["supported_local"] == [
|
||||||
|
"codex-desktop-local", "codex-cli", "codex-ide-extension-local",
|
||||||
|
]
|
||||||
|
assert "claude-desktop" in d["client_surfaces"]["claude"]["not_covered"]
|
||||||
|
assert "codex-cloud" in d["client_surfaces"]["codex"]["not_covered"]
|
||||||
|
PY
|
||||||
if [[ -d "$PROJ/packages/casan-harness" ]]; then fail "harness was copied into the repo (should not be)"; else pass "harness NOT copied into repo (hybrid model)"; fi
|
if [[ -d "$PROJ/packages/casan-harness" ]]; then fail "harness was copied into the repo (should not be)"; else pass "harness NOT copied into repo (hybrid model)"; fi
|
||||||
if grep -R -q 'packages/casan-harness/adapters' "$PROJ/.claude/settings.json" "$PROJ/.codex/hooks.json"; then fail "generated hooks still target a repo-local harness"; else pass "generated hooks target the stable project bootstrap"; fi
|
if grep -R -q 'packages/casan-harness/adapters' "$PROJ/.claude/settings.json" "$PROJ/.codex/hooks.json"; then fail "generated hooks still target a repo-local harness"; else pass "generated hooks target the stable project bootstrap"; fi
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ S="$CASAN_HARNESS_ROOT/scripts/bash/upgrade-compatibility.sh"
|
|||||||
M="$CASAN_APP_ROOT/infra/production/compatibility-matrix.json"
|
M="$CASAN_APP_ROOT/infra/production/compatibility-matrix.json"
|
||||||
PASS=0; FAIL=0
|
PASS=0; FAIL=0
|
||||||
pass(){ echo "PASS: $1"; PASS=$((PASS+1)); }; fail(){ echo "FAIL: $1"; FAIL=$((FAIL+1)); }
|
pass(){ echo "PASS: $1"; PASS=$((PASS+1)); }; fail(){ echo "FAIL: $1"; FAIL=$((FAIL+1)); }
|
||||||
if bash "$S" check --from 1.0.0 --to 1.0.1 --matrix "$M" | grep -q '"decision": "allow"'; then pass "patch upgrade is explicitly allowed"; else fail "patch upgrade denied"; fi
|
if bash "$S" check --from 1.0.1 --to 1.0.2 --matrix "$M" | grep -q '"decision": "allow"'; then pass "patch upgrade is explicitly allowed"; else fail "patch upgrade denied"; fi
|
||||||
if bash "$S" check --from 1.0.1 --to 1.0.0 --rollback --matrix "$M" | grep -q '"rollback": true'; then pass "approved rollback is explicitly allowed"; else fail "approved rollback denied"; fi
|
if bash "$S" check --from 1.0.2 --to 1.0.1 --rollback --matrix "$M" | grep -q '"rollback": true'; then pass "approved rollback is explicitly allowed"; else fail "approved rollback denied"; fi
|
||||||
if bash "$S" check --from 1.0.1 --to 2.0.0 --matrix "$M" >/dev/null 2>&1; then fail "unreviewed major upgrade accepted"; else pass "unreviewed major upgrade denied"; fi
|
if bash "$S" check --from 1.0.2 --to 2.0.0 --matrix "$M" >/dev/null 2>&1; then fail "unreviewed major upgrade accepted"; else pass "unreviewed major upgrade denied"; fi
|
||||||
if bash "$S" check --from invalid --to 1.0.0 --matrix "$M" >/dev/null 2>&1; then fail "invalid version accepted"; else pass "invalid version denied"; fi
|
if bash "$S" check --from invalid --to 1.0.0 --matrix "$M" >/dev/null 2>&1; then fail "invalid version accepted"; else pass "invalid version denied"; fi
|
||||||
echo "===== UPGRADE COMPATIBILITY SUMMARY: PASS=$PASS FAIL=$FAIL ====="; [[ "$FAIL" -eq 0 ]]
|
echo "===== UPGRADE COMPATIBILITY SUMMARY: PASS=$PASS FAIL=$FAIL ====="; [[ "$FAIL" -eq 0 ]]
|
||||||
|
|||||||
Reference in New Issue
Block a user