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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ xgen model check --compatibility
# 상세 durable progress 로그가 필요할 때
xgen --debug

# 요청 하나가 쓸 수 있는 model turn 수를 바꿀 때 (기본 24)
# 요청 하나가 쓸 수 있는 model turn 수를 바꿀 때 (기본 24, --trust면 96)
xgen --max-model-turns 48
```

Expand Down
26 changes: 22 additions & 4 deletions crates/xgen-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ struct Cli {
/// Show durable progress events instead of the interactive Thinking display.
#[arg(long)]
debug: bool,
/// Model turns each interactive goal may use before it stops without an answer.
/// Model turns each interactive goal may use before it stops without an answer
/// [default: 24, or 96 with --trust].
#[arg(
long,
value_name = "TURNS",
default_value_t = REPL_MODEL_TURNS,
value_parser = clap::value_parser!(u32).range(1..=i64::from(xgen_cli::MAX_HOST_MODEL_TURNS))
)]
max_model_turns: u32,
max_model_turns: Option<u32>,
/// Start the interactive session with model/read/write/execute allowed, asking nothing.
#[arg(long)]
trust: bool,
Expand Down Expand Up @@ -387,7 +387,12 @@ fn main() -> ExitCode {
}
match cli.command {
Some(Command::Tools { command }) => tools::run(command),
None => interactive_command(cli.debug, cli.max_model_turns, cli.trust),
None => interactive_command(
cli.debug,
cli.max_model_turns
.unwrap_or_else(|| default_repl_model_turns(cli.trust)),
cli.trust,
),
Some(Command::Licenses) => print_licenses(),
Some(Command::Protocol {
command: ProtocolCommand::Check,
Expand Down Expand Up @@ -508,10 +513,23 @@ fn executable_catalog(named: Vec<String>, include_path: bool) -> Vec<String> {
// The bare workspace budget (8 turns) ends a read-edit-test task before it can answer.
// 24 turns is the budget the project evals already use for Node.js and Python work.
const REPL_MODEL_TURNS: u32 = 24;
// Trust mode (ADR-0062) is the personal mode where nobody approves each step, so a goal like a
// rename across 17 files should not stop at 24 turns. 96 turns keep a foreground invocation
// inside MAX_TICKS (1_024) at ten ticks per turn.
const TRUSTED_REPL_MODEL_TURNS: u32 = 96;
const _: () = assert!(TRUSTED_REPL_MODEL_TURNS * REPL_TICKS_PER_MODEL_TURN <= xgen_cli::MAX_TICKS);
// Ticks only bound one foreground invocation; the manifest budget bounds the Run. Ten
// per turn covers plan, admission, effect and verification with room for recovery.
const REPL_TICKS_PER_MODEL_TURN: u32 = 10;

const fn default_repl_model_turns(trust: bool) -> u32 {
if trust {
TRUSTED_REPL_MODEL_TURNS
} else {
REPL_MODEL_TURNS
}
}

const fn repl_max_ticks(max_model_turns: u32) -> u32 {
let ticks = max_model_turns.saturating_mul(REPL_TICKS_PER_MODEL_TURN);
if ticks > xgen_cli::MAX_TICKS {
Expand Down
3 changes: 2 additions & 1 deletion docs/adr/0059-trust-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ session에서 승인은 각각 3회였다. 매번 `/permissions KIND allow`를
바뀌지 않는 것:

- journal, Receipt 검증, 끝난 뒤 `Changes` 표시(ADR-0057), Ctrl+C, `/resume`.
- 실행 파일 catalog. 이 옵션은 이미 catalog에 있는 것의 승인만 생략한다.
- 실행 파일 catalog. 이 옵션은 이미 catalog에 있는 것의 승인만 생략한다. (이후 ADR-0060이 shell을,
ADR-0062가 더 큰 기본 turn 예산을 신뢰 모드에 더했다.)
- Core의 action 단위 authorization. 각 effect는 실행 직전에 기존 검증을 거친다.
- 비대화형 `xgen run`/`resume`의 승인 플래그.

Expand Down
48 changes: 48 additions & 0 deletions docs/adr/0062-trusted-turn-budget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# ADR-0062: 신뢰 모드의 기본 turn 예산

- 상태: Accepted
- 기준일: 2026-10-26
- 변경 분류: 범용 REPL 기본값. 특정 작업·도구·업무 규칙 없음.
- 기존 경계: ADR-0059(신뢰 모드), ADR-0061(거절된 응답 재시도)
- protocol·SQLite schema·journal 변경: 없음. 예산은 지금처럼 Run manifest에 기록된다.

## 문제

REPL goal 하나는 기본 24 model turn을 쓴다. 승인을 묻는 기본 모드에서는 사람이 단계마다 보고 있으므로
짧은 예산이 비용과 폭주를 막는다. 신뢰 모드(ADR-0059)는 사람이 단계마다 보지 않는 개인용 모드라 같은
예산이 일을 끝내기 전에 멈추는 원인이 됐다.

Claude Code 비교 실험(devhub-social 사본, deepseek flash, `--trust`)에서:

- rename 작업(참조 137개, 17개 파일): 거절 재시도(ADR-0061)를 넣은 뒤 2회 모두 정상 제안 24개를
처리하고 `model_turn_budget_exhausted`로 멈췄다. 참조 86개가 남았다.
- 저장소 상태 조회 작업(`~/projects/app` 아래 저장소 24개): 2회 모두 예산을 다 쓰고 답하지 못했다.

두 작업은 서로 다른 종류(편집, 조회)인데 모두 24 turn 안에 끝나지 않았다. 반면 버그 수정, 기능 추가,
후속 대화, 새 스크립트 작업은 24 turn 안에 끝났다.

## 결정

1. `xgen --trust`의 goal당 기본 model turn을 96으로 한다. 기본 모드는 24 그대로다.
2. `--max-model-turns N`을 주면 모드와 관계없이 그 값이 우선한다.
3. 96은 foreground 호출의 tick 상한(`MAX_TICKS` 1,024, turn당 10 tick) 안에 들어가는 값이다. 이 관계는
컴파일 시점에 검사한다.
4. 나머지 예산(model call, planned step, tool call)은 지금처럼 turn 수에서 2:1:1로 정해진다. context
상한(512 KiB)은 바꾸지 않는다.

## 대안

- **기본 예산을 모든 모드에서 올리기:** 승인 모드의 비용 상한까지 바뀐다. 필요성을 보인 것은 신뢰 모드뿐이다.
- **예산이 끝난 Run을 `/resume`으로 연장:** manifest의 예산을 바꾸는 계약 변경이라 이번 범위에 넣지 않았다.
- **예산 제한 없음:** 같은 실수를 반복하는 모델이 끝없이 call을 쓴다. 상한은 남긴다.

## 검증

- `xgen --help`가 `[default: 24, or 96 with --trust]`를 보인다.
- rename 작업을 새 binary로 2회 실행:
- 1회는 164초에 완료했다. 테스트 47개가 통과했고 참조 137개 중 3개가 남았다(call 61개, 거절 7개
재시도 후 진행).
- 1회는 215초 동안 정상 제안 70개를 처리한 뒤 존재하지 않는 capability를 3번 연속 요청해
`proposal_rejected.capability_unavailable`로 멈췄다. 예산이 아닌 다른 문제다. 거절 이유를 다음
context에 넣지 않아 같은 요청이 반복된 것으로 보이며 후속 과제로 남긴다.
- 저장소 상태 조회 작업은 workspace 밖 경로 안내 변경과 함께 다시 잰다.
2 changes: 2 additions & 0 deletions docs/development/interactive-repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Session 허용은 파일에 저장하지 않고 process가 끝나면 사라진
신뢰 모드는 사용자의 shell(`SHELL`이 bash·zsh·sh면 그것, 아니면 PATH의 bash→zsh→sh, Windows는
pwsh→powershell)도 catalog에 넣는다([ADR-0060](../adr/0060-trusted-shell-executable.md)). 모델은 이것으로
파이프와 catalog에 없는 명령을 실행한다.
신뢰 모드의 goal당 model turn 기본값은 24가 아니라 96이다([ADR-0062](../adr/0062-trusted-turn-budget.md)).
`--max-model-turns`를 주면 그 값이 우선한다.

```text
xgen> 프로젝트 구조를 보고 테스트 실패를 수정해줘.
Expand Down