Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/cs-rg-qualification.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ env -i HOME="$HOME" TMPDIR="$TMPDIR" PATH="<Rust 1.95.0>/bin:/usr/bin:/bin:/usr/

`--modes` limits a suite to `in-process` or `uds`; both run by default. The installed-service suites default to the service's socket, journal, log, `host.toml` and the consumer's credential file, whose path is handed to the gateway and never read by the harness. Exit status: 0 passed or qualified, 1 failed, 2 incomplete or inconclusive, 3 the harness failed, 130 interrupted; `verify` exits 0 when the run checks out and 1 when it does not.

**Terminal output.** Each scenario, phase, interval, repetition and run prints one `qualify:` line with its verdict. On a terminal the verdict is in color: green when it passed or qualified, red when it failed or the harness failed, yellow when it is inconclusive, incomplete or not run, and magenta when it was interrupted. A status line under the output shows what runs and for how long: in a measurement interval, the time into its planned length and the load's runs, and in the recovery measurement also the gateway that serves. `--color auto`, the default, colors a terminal unless `NO_COLOR` is set, and `always` or `never` overrides it. `--console-log <new file>` keeps a copy of everything the run prints, without color or the status line. A pipe to `tee` makes the output a pipe, which turns both off, so a run started from a terminal keeps its console log this way. The evidence, `verify`'s JSON and the console log never hold color, and output to a file or a pipe is exactly what it was before.

The harness talks to `codespace-mcp` over standard input and output as any MCP client does (protocol `2025-11-25`), with its own small client, so a timing is the tool call's local round trip from the harness and includes no network. It reads DevGuard's journal read-only, as DevGuard's own measurement does, and takes a governed launch's attempt ID and scope root from the gateway's log.

<a id="functional-matrix"></a>
Expand Down
2 changes: 2 additions & 0 deletions docs/ko/cs-rg-qualification.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ env -i HOME="$HOME" TMPDIR="$TMPDIR" PATH="<Rust 1.95.0>/bin:/usr/bin:/bin:/usr/

`--modes`로 스위트를 `in-process`나 `uds` 하나로 줄일 수 있으며, 기본은 둘 다입니다. 설치된 서비스를 쓰는 스위트는 기본으로 서비스의 socket, journal, 로그, `host.toml`, 소비자 credential 파일을 씁니다. credential 파일은 경로만 gateway에 넘기고 하네스는 읽지 않습니다. 종료 상태는 0 통과 또는 qualification, 1 실패, 2 미완료 또는 판정 불가, 3 하네스 실패, 130 중단입니다. `verify`는 실행 기록이 맞으면 0, 아니면 1로 끝납니다.

**터미널 출력.** 시나리오, 단계, 구간, 반복, 실행마다 판정을 담은 `qualify:` 줄이 하나씩 나옵니다. 터미널에서는 판정에 색이 들어갑니다. 통과하거나 qualification을 마치면 초록, 실패하거나 하네스가 실패하면 빨강, 판정 불가, 미완료, 실행 안 함이면 노랑, 중단되면 자홍색입니다. 출력 아래의 상태 줄은 무엇이 얼마나 오래 실행 중인지 보여 줍니다. 측정 구간에서는 계획한 길이 가운데 지난 시간과 부하의 실행 수를, 복구 측정에서는 서비스 중인 gateway도 보여 줍니다. 기본값 `--color auto`는 `NO_COLOR`가 설정되지 않은 터미널에만 색을 넣고, `always`나 `never`로 이를 바꿀 수 있습니다. `--console-log <새 파일>`은 실행이 출력하는 모든 내용을 색과 상태 줄 없이 복사해 둡니다. `tee`로 파이프를 연결하면 출력이 파이프가 되어 색과 상태 줄이 모두 꺼지므로, 터미널에서 시작한 실행은 이 방법으로 콘솔 로그를 남깁니다. 증거, `verify`의 JSON, 콘솔 로그에는 색이 들어가지 않으며, 파일이나 파이프로 보낸 출력은 이전과 똑같습니다.

하네스는 다른 MCP client와 마찬가지로 표준 입출력으로 `codespace-mcp`와 통신하며(protocol `2025-11-25`), 자체의 작은 client를 씁니다. 따라서 시간 값은 하네스에서 잰 도구 호출의 로컬 왕복 시간이고 네트워크 시간은 들어가지 않습니다. DevGuard의 journal은 DevGuard 자신의 측정처럼 읽기 전용으로 읽고, 관리 대상 실행의 attempt ID와 scope root는 gateway 로그에서 가져옵니다.

<a id="functional-matrix"></a>
Expand Down
2 changes: 1 addition & 1 deletion docs/ko/recovery-qualification.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

이 문서는 Gateway 복구(P1-RECOVERY)의 opt-in 모드인 [독립 Runner](runner-isolation.md#independent-runner)를 qualification하는 절차입니다. 어떤 조합을 지원한다고 주장하는지, 각 주장을 어떻게 시험하고 측정하는지, 무엇이 통과인지, 실행이 어떤 증거를 남기는지를 정합니다. [CS-RG qualification 절차](cs-rg-qualification.md)를 따르며, 그 후보 조합, 증거, 확인 방식을 그대로 쓰고, 하네스도 같은 [`scripts/qualify-devguard.py`](../../scripts/qualify-devguard.py)입니다. 이 절차는 첫 qualification 실행 전에 하네스와 함께 고정합니다. 첫 실행 뒤에 둘 중 하나라도 바꾸면 새 절차가 되며, 다른 변경처럼 검토하고 영향받는 실행은 다시 합니다. 각 단위가 무엇을 만들었는지는 [결합 로드맵](devguard-integration.md#resource-gateway-recovery)에 있고, 이 문서는 그 결과가 qualification을 통과하는지를 판단합니다.

**현재 상태:** 첫 qualification 실행 전에 하네스와 함께 고정했습니다([#112](https://github.com/novelKR/CodeSpace/pull/112)). 통과한 실행이 나오기 전에 하네스 결함 세 개를 고쳤고([#113](https://github.com/novelKR/CodeSpace/pull/113), [#114](https://github.com/novelKR/CodeSpace/pull/114), [#115](https://github.com/novelKR/CodeSpace/pull/115)), 고칠 때마다 새 절차와 새 후보가 되어 실행을 다시 했습니다. 후보 `1c0eadd`의 실행이 통과해, 독립 Runner는 그 조합으로 qualification을 마쳤습니다. 결과와 증거는 [qualification 보고서](recovery-qualification-report.md)에 있고, 수정 내용은 그 [수정 기록](recovery-qualification-report.md#recovery-report-amendments)에 있습니다.
**현재 상태:** 첫 qualification 실행 전에 하네스와 함께 고정했습니다([#112](https://github.com/novelKR/CodeSpace/pull/112)). 통과한 실행이 나오기 전에 하네스 결함 세 개를 고쳤고([#113](https://github.com/novelKR/CodeSpace/pull/113), [#114](https://github.com/novelKR/CodeSpace/pull/114), [#115](https://github.com/novelKR/CodeSpace/pull/115)), 고칠 때마다 새 절차와 새 후보가 되어 실행을 다시 했습니다. 후보 `1c0eadd`의 실행이 통과해, 독립 Runner는 그 조합으로 qualification을 마쳤습니다. 결과와 증거는 [qualification 보고서](recovery-qualification-report.md)에 있고, 수정 내용은 그 [수정 기록](recovery-qualification-report.md#recovery-report-amendments)에 있습니다. 그 뒤로는 하네스와 검사 스크립트가 터미널에 출력하는 방식만 바뀌었습니다([하네스 실행](cs-rg-qualification.md#running)). 이 변경으로 이후 main commit은 code tree가 달라지므로, qualification을 마친 빌드는 계속 `1c0eadd` 자체이며 다음 qualification은 바뀐 하네스로 실행합니다.

<a id="recovery-candidate"></a>

Expand Down
3 changes: 3 additions & 0 deletions docs/ko/upstream-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ CI도 같은 이름의 단계를 병렬 실행하며 목록은 `--help`로 확
실패한 테스트와 panic 위치, 테스트 수, 실패한 cargo나 unittest 대상도 출력하므로
CI job 로그에서 바로 확인할 수 있습니다. 테스트 자체의 출력은 자식 프로세스의
환경을 담을 수 있어 로그에만 남깁니다.
터미널에서는 실행 중인 단계와 걸린 시간을 상태 줄에 보여 주고 실패를 색으로
강조합니다. `--color never`를 주면 색을 넣지 않으며, 파일이나 파이프로 보낸
출력에는 색이 없습니다.
시도마다 `--output`으로 다른 Git 무시 디렉터리를 지정하거나 이전 결과를 보관합니다.
보고서에는 소스 HEAD, Codex SHA, Rust 호스트, 소스와 lockfile 해시가 기록되며
실행 중 입력이 바뀌면 실패합니다. `passed`는 기록된 단계만의 통과를 뜻합니다.
Expand Down
2 changes: 1 addition & 1 deletion docs/recovery-qualification.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

This page is the protocol for qualifying the [independent Runner](runner-isolation.md#independent-runner), the opt-in mode of Gateway recovery (P1-RECOVERY): which combinations are claimed, how each claim is tested and measured, what makes a run pass, and what evidence a run leaves. It follows the [CS-RG qualification protocol](cs-rg-qualification.md), whose candidate, evidence and verification it reuses, and its harness is the same [`scripts/qualify-devguard.py`](../scripts/qualify-devguard.py). It is frozen together with the harness before any qualification run; a change to either after the first run is a new protocol, reviewed like any other change, and the runs it affects are repeated. The [integration roadmap](devguard-integration.md#resource-gateway-recovery) describes what each unit built; this page decides whether the result qualifies.

**Status:** frozen with its harness ([#112](https://github.com/novelKR/CodeSpace/pull/112)) before the first qualification run. Before any run qualified, three harness defects were corrected ([#113](https://github.com/novelKR/CodeSpace/pull/113), [#114](https://github.com/novelKR/CodeSpace/pull/114), [#115](https://github.com/novelKR/CodeSpace/pull/115)); each correction made a new protocol and a new candidate, and the runs were repeated on it. The runs on the candidate `1c0eadd` passed, and the independent Runner is qualified for that combination; the [qualification report](recovery-qualification-report.md) has the results and the evidence, and its [amendments](recovery-qualification-report.md#recovery-report-amendments) record the corrections.
**Status:** frozen with its harness ([#112](https://github.com/novelKR/CodeSpace/pull/112)) before the first qualification run. Before any run qualified, three harness defects were corrected ([#113](https://github.com/novelKR/CodeSpace/pull/113), [#114](https://github.com/novelKR/CodeSpace/pull/114), [#115](https://github.com/novelKR/CodeSpace/pull/115)); each correction made a new protocol and a new candidate, and the runs were repeated on it. The runs on the candidate `1c0eadd` passed, and the independent Runner is qualified for that combination; the [qualification report](recovery-qualification-report.md) has the results and the evidence, and its [amendments](recovery-qualification-report.md#recovery-report-amendments) record the corrections. Since then only what the harness and the checks print on a terminal has changed ([running the harness](cs-rg-qualification.md#running)). That gives later commits of main another code tree, so the qualified build stays `1c0eadd` itself, and the next qualification runs on the changed harness.

<a id="recovery-candidate"></a>

Expand Down
12 changes: 6 additions & 6 deletions docs/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@
"회귀와-동등성",
"후보-조합"
],
"source_sha256": "dc0a5ce31fcd3fc4ac9b27b53ed04e04cb448b14f13c8f0fe26f1bf0edc5d283",
"translation_sha256": "6396e29330c89d2b231cfef742ec7dc7927c2297ecefec52a93a67bf210497fb"
"source_sha256": "6bc422fd2b3212dc89055655b0fe25c6bce10ca8e93f6761e2488e6476d17de2",
"translation_sha256": "11f66274ae7e0ab09a88121a99274d68b2b7db78fe3b9114659fca397dbc3792"
},
{
"id": "cs-rg-qualification-report",
Expand Down Expand Up @@ -249,8 +249,8 @@
"회귀",
"후보-조합"
],
"source_sha256": "a85f4cb3fef6228e77ab7613e796bfd211732b66a88a639dc986c2a10292fad5",
"translation_sha256": "a249e22d93e288e16c88f4605c63b9faa2b722819049f3a619edac8508eaf415"
"source_sha256": "e857af544c09ab653a7c703b44978be3c7545b3056e89f898e047bcb6d7f2f6e",
"translation_sha256": "01c3f33207f7b678b8e5e40d1f971ac2d7d4c7aa59bbab98b17646995dba63c7"
},
{
"id": "recovery-qualification-report",
Expand Down Expand Up @@ -917,8 +917,8 @@
"향후-crate와-백엔드의-경계",
"후보-검토"
],
"source_sha256": "f0846a7de343b43410f0df86873ea3773b863baabc2379c27eb2f69d9bcd7409",
"translation_sha256": "9a99fb130cbd1099f3b58134ba0907f9edcd11f917bd1e3445209509bfc5098b"
"source_sha256": "7099124f690fb9a59ca6a9d3734405a8d1445bda46786e28bb89dd933c85699b",
"translation_sha256": "1f4148815546388e68621c1510b015ad82977b43710283e5a616fabdbf0ae11d"
},
{
"id": "upstream-patches",
Expand Down
3 changes: 3 additions & 0 deletions docs/upstream-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ When a stage fails, the script also prints the lines of that stage's log that
name what failed: the failed tests and where they panicked, the test counts, and
the failed cargo or unittest target, so a CI job log names them. A test's own
output stays in the log, since its messages can carry a child's environment.
On a terminal the script also shows the running stage and its time on a status
line, and failures in color; `--color never` leaves the color out, and output to
a file or a pipe is plain.
Choose a different ignored directory with `--output` for each attempt; preserve
previous reports before repeating a run. Reports record the source HEAD, Codex
SHA, Rust host, and source/lockfile hashes. A changed input invalidates the run.
Expand Down
18 changes: 12 additions & 6 deletions scripts/check-no-model-deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
# Never skip the tree because the range could not be computed.
set -euo pipefail

# Outcomes in color on a terminal, unless NO_COLOR is set or TERM is dumb, as scripts/console.py
# chooses; redirected, every line is as before.
colors() { [[ -t "$1" && -z "${NO_COLOR:-}" && "${TERM:-}" != dumb ]]; }
if colors 1; then OK=$'\e[1;32mok\e[0m'; else OK=ok; fi
if colors 2; then FORBIDDEN=$'\e[1;31mforbidden\e[0m'; else FORBIDDEN=forbidden; fi

ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT"

Expand Down Expand Up @@ -237,7 +243,7 @@ scan_manifest() {
out="$(grep -nE '^[[:space:]]*codex-[A-Za-z0-9_-]+[[:space:]]*=' "$file" || true)"
if [[ -n "$out" ]]; then
{
echo "forbidden (codex- cargo dep) in $file:"
echo "$FORBIDDEN (codex- cargo dep) in $file:"
echo "$out"
} >"$outfile"
fi
Expand Down Expand Up @@ -286,7 +292,7 @@ scan_adapter_manifest() {
done < <(grep -nE '^[[:space:]]*codex-[A-Za-z0-9_-]+[[:space:]]*=' "$file" || true)
if [[ -n "$bad" ]]; then
{
echo "forbidden (codex- key not on $kind allowlist) in $file:"
echo "$FORBIDDEN (codex- key not on $kind allowlist) in $file:"
printf '%s' "$bad"
} >"$outfile"
fi
Expand All @@ -302,7 +308,7 @@ scan_source() {
out="$(grep -R -n -E --exclude-dir=target --exclude-dir=.git -e "$pattern" "$dir" || true)"
if [[ -n "$out" ]]; then
{
echo "forbidden ($label) in $dir:"
echo "$FORBIDDEN ($label) in $dir:"
echo "$out"
} >"$outfile"
fi
Expand All @@ -318,7 +324,7 @@ scan_no_helper_lib() {
out="$(grep -nE '^[[:space:]]*codespace-linux-sandbox[[:space:]]*=' "$file" || true)"
if [[ -n "$out" ]]; then
{
echo "forbidden (codespace-linux-sandbox library path dep) in $file:"
echo "$FORBIDDEN (codespace-linux-sandbox library path dep) in $file:"
echo "$out"
} >"$outfile"
fi
Expand All @@ -327,7 +333,7 @@ scan_no_helper_lib() {
scan_helper_binary_only() {
local outfile="$1"
if [[ -f crates/linux-sandbox/src/lib.rs ]]; then
echo "forbidden: crates/linux-sandbox must be binary-only (src/lib.rs present)" >"$outfile"
echo "$FORBIDDEN: crates/linux-sandbox must be binary-only (src/lib.rs present)" >"$outfile"
fi
}

Expand Down Expand Up @@ -420,4 +426,4 @@ if [[ "$hits" -ne 0 ]]; then
exit 1
fi

echo "no-model-deps ok"
echo "no-model-deps $OK"
18 changes: 13 additions & 5 deletions scripts/check-upstream-pin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@
# patch adapter tests fail. Never treat a red parity run as success.
set -euo pipefail

# Outcomes in color on a terminal, unless NO_COLOR is set or TERM is dumb, as scripts/console.py
# chooses; redirected, every line is as before.
colors() { [[ -t "$1" && -z "${NO_COLOR:-}" && "${TERM:-}" != dumb ]]; }
if colors 1; then OK=$'\e[1;32mok\e[0m'; else OK=ok; fi
fail() {
if colors 2; then printf '\e[1;31m%s\e[0m\n' "$1" >&2; else printf '%s\n' "$1" >&2; fi
}

ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT"
LOCK_FILE="docs/upstream-lock.md"

if [[ ! -f "$LOCK_FILE" ]]; then
echo "missing $LOCK_FILE" >&2
fail "missing $LOCK_FILE"
exit 1
fi

Expand All @@ -23,26 +31,26 @@ expected="$(awk -F'|' '
' "$LOCK_FILE")"

if [[ ! "$expected" =~ ^[0-9a-f]{40}$ ]]; then
echo "could not parse 40-char commit from $LOCK_FILE (got: ${expected:-empty})" >&2
fail "could not parse 40-char commit from $LOCK_FILE (got: ${expected:-empty})"
exit 1
fi

if [[ ! -e third_party/codex ]]; then
echo "third_party/codex is missing; init the submodule" >&2
fail "third_party/codex is missing; init the submodule"
exit 1
fi

actual="$(git -C third_party/codex rev-parse HEAD)"
if [[ "$actual" != "$expected" ]]; then
echo "Codex submodule $actual does not match lock $expected" >&2
fail "Codex submodule $actual does not match lock $expected"
echo "Do not git submodule update --remote onto Codex main. See docs/upstream-update.md." >&2
exit 1
fi

# The recorded downstream patches must be applied, and nothing else changed.
python3 scripts/codex_patch.py check

echo "upstream pin ok $actual"
echo "upstream pin $OK $actual"

if [[ "${PIN_ONLY:-}" == "1" ]]; then
exit 0
Expand Down
31 changes: 20 additions & 11 deletions scripts/check_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@
import sys
from pathlib import Path

# Loaded by its path too (docs-site's tests), so its own directory is made importable.
sys.path.insert(0, str(Path(__file__).resolve().parent))
import console # noqa: E402

ROOT = Path(__file__).resolve().parents[1]
REGISTRY = ROOT / 'docs' / 'translations.json'
SCHEMA = 'codespace-documentation/v1'
KEYS = {
'id', 'section', 'order', 'route', 'source', 'translation',
'anchors', 'source_sha256', 'translation_sha256',
}
# The outcome in color on a terminal (scripts/console.py); redirected, the text as before.
CONSOLE = console.Console('check_docs')


def fail(message):
print('Documentation check failed: ' + message, file=sys.stderr)
CONSOLE.line('Documentation check ' + CONSOLE.err_style.verdict('failed') + ': ' + message, err=True)
return 1


Expand Down Expand Up @@ -89,7 +95,8 @@ def check():
missing_anchors = [item for item in document['anchors'] if item not in available]
if missing_anchors:
return fail('Missing anchors for ' + ident + ': ' + ', '.join(missing_anchors))
print('Documentation registry verified: ' + str(len(registry['documents'])) + ' paired documents')
CONSOLE.line('Documentation registry ' + CONSOLE.style.verdict('verified') + ': '
+ str(len(registry['documents'])) + ' paired documents')
return 0


Expand All @@ -106,23 +113,25 @@ def record(ident=None):
document['anchors'] = sorted(set(headings(source.read_text(encoding='utf-8')))
| set(headings(translation.read_text(encoding='utf-8'))))
REGISTRY.write_text(json.dumps(registry, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
print('Recorded ' + ', '.join(document['id'] for document in selected))
CONSOLE.line(CONSOLE.style('Recorded', 'bold', 'green') + ' ' + ', '.join(document['id'] for document in selected))
return 0


def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('command', nargs='?', default='check', choices=['check', 'record'])
parser.add_argument('--id')
console.add_option(parser)
args = parser.parse_args()
try:
if args.command == 'record':
return record(args.id)
if args.id:
return fail('check does not take --id')
return check()
except (OSError, ValueError, KeyError, json.JSONDecodeError) as error:
return fail(str(error))
with CONSOLE.session(args.color, live=False):
try:
if args.command == 'record':
return record(args.id)
if args.id:
return fail('check does not take --id')
return check()
except (OSError, ValueError, KeyError, json.JSONDecodeError) as error:
return fail(str(error))


if __name__ == '__main__':
Expand Down
Loading
Loading