fix: tuiからのPod作成の挙動を修正・開発時にcargo runでpodを起動する経路を実装
This commit is contained in:
parent
288e2239d4
commit
d8d802d120
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
||||||
.direnv
|
.direnv
|
||||||
*.local*
|
*.local*
|
||||||
.env
|
.env
|
||||||
|
.worktree
|
||||||
|
|
|
||||||
1
TODO.md
1
TODO.md
|
|
@ -2,6 +2,7 @@
|
||||||
- [ ] 内部 Worker / 内部 Pod の Workflow 化 → [tickets/internal-worker-workflow.md](tickets/internal-worker-workflow.md)
|
- [ ] 内部 Worker / 内部 Pod の Workflow 化 → [tickets/internal-worker-workflow.md](tickets/internal-worker-workflow.md)
|
||||||
- [ ] Agent Skills を Workflow として ingest → [tickets/agent-skills.md](tickets/agent-skills.md)
|
- [ ] Agent Skills を Workflow として ingest → [tickets/agent-skills.md](tickets/agent-skills.md)
|
||||||
- [ ] パーミッション: パターンベースのツール実行制御 → [tickets/permission-extension-point.md](tickets/permission-extension-point.md)
|
- [ ] パーミッション: パターンベースのツール実行制御 → [tickets/permission-extension-point.md](tickets/permission-extension-point.md)
|
||||||
|
- [ ] Resume 時の Scope claim の改善 → [tickets/resume-scope-claim.md](tickets/resume-scope-claim.md)
|
||||||
- [ ] Pod CLI: マニフェスト関連フラグの整理 → [tickets/pod-cli-manifest-flags.md](tickets/pod-cli-manifest-flags.md)
|
- [ ] Pod CLI: マニフェスト関連フラグの整理 → [tickets/pod-cli-manifest-flags.md](tickets/pod-cli-manifest-flags.md)
|
||||||
- [ ] OpenAI Responses: sampling パラメータの取り扱い → [tickets/responses-sampling-params.md](tickets/responses-sampling-params.md)
|
- [ ] OpenAI Responses: sampling パラメータの取り扱い → [tickets/responses-sampling-params.md](tickets/responses-sampling-params.md)
|
||||||
- [ ] llm-worker のエラー耐性
|
- [ ] llm-worker のエラー耐性
|
||||||
|
|
|
||||||
|
|
@ -268,12 +268,11 @@ async fn wait_for_ready(
|
||||||
form: &mut Form,
|
form: &mut Form,
|
||||||
overlay_toml: &str,
|
overlay_toml: &str,
|
||||||
) -> Result<SpawnReady, SpawnError> {
|
) -> Result<SpawnReady, SpawnError> {
|
||||||
let (pod_bin, pod_args) = resolve_pod_command();
|
let pod_bin = resolve_pod_command();
|
||||||
let cwd = std::env::current_dir().map_err(SpawnError::Io)?;
|
let cwd = std::env::current_dir().map_err(SpawnError::Io)?;
|
||||||
|
|
||||||
let mut command = Command::new(&pod_bin);
|
let mut command = Command::new(&pod_bin);
|
||||||
command
|
command
|
||||||
.args(&pod_args)
|
|
||||||
.arg("--overlay")
|
.arg("--overlay")
|
||||||
.arg(overlay_toml)
|
.arg(overlay_toml)
|
||||||
.current_dir(&cwd)
|
.current_dir(&cwd)
|
||||||
|
|
@ -375,28 +374,21 @@ fn build_overlay_toml(form: &Form) -> String {
|
||||||
toml::to_string(&toml::Value::Table(root)).expect("overlay serialisation cannot fail")
|
toml::to_string(&toml::Value::Table(root)).expect("overlay serialisation cannot fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the program (and any leading args) used to launch a child Pod.
|
/// Resolves the binary used to launch a child Pod. Must point at a
|
||||||
|
/// `pod`-compatible executable — the parent reads the child's stderr
|
||||||
|
/// directly looking for `INSOMNIA-READY`, so any wrapper that emits
|
||||||
|
/// extra lines on stderr will pollute that handshake.
|
||||||
///
|
///
|
||||||
/// `INSOMNIA_POD_COMMAND` is split on whitespace so devshells can point it
|
/// `INSOMNIA_POD_COMMAND` overrides the lookup (used by tests to inject
|
||||||
/// at e.g. `cargo run -p pod --quiet --`; the first token is the program
|
/// a mock binary). Otherwise we defer to `PATH` — missing binary
|
||||||
/// and the rest are prepended before `--overlay` and friends.
|
/// surfaces as the spawn `io::Error`.
|
||||||
fn resolve_pod_command() -> (PathBuf, Vec<String>) {
|
fn resolve_pod_command() -> PathBuf {
|
||||||
if let Ok(cmd) = std::env::var("INSOMNIA_POD_COMMAND") {
|
if let Ok(cmd) = std::env::var("INSOMNIA_POD_COMMAND") {
|
||||||
let mut tokens = cmd.split_whitespace();
|
if !cmd.is_empty() {
|
||||||
if let Some(program) = tokens.next() {
|
return PathBuf::from(cmd);
|
||||||
let args = tokens.map(str::to_owned).collect();
|
|
||||||
return (PathBuf::from(program), args);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Ok(exe) = std::env::current_exe() {
|
PathBuf::from("pod")
|
||||||
if let Some(dir) = exe.parent() {
|
|
||||||
let candidate = dir.join("pod");
|
|
||||||
if candidate.is_file() {
|
|
||||||
return (candidate, Vec::new());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(PathBuf::from("pod"), Vec::new())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StderrTail {
|
struct StderrTail {
|
||||||
|
|
|
||||||
21
devshell.nix
21
devshell.nix
|
|
@ -1,4 +1,23 @@
|
||||||
{ pkgs }:
|
{ pkgs }:
|
||||||
|
let
|
||||||
|
# Dev-only wrapper. tui の spawn 経路は `pod` バイナリを直に exec し、
|
||||||
|
# stderr の `INSOMNIA-READY` 行で握手するので、cargo の進捗や rustc の
|
||||||
|
# warning が混ざると tail に余計な行が積もり本当のエラーが押し出される。
|
||||||
|
# ここで一度ビルドを切り離し、成功時はビルド出力を一切捨てて素のバイナリ
|
||||||
|
# を exec、失敗時のみ build log を stderr に流して exit する。
|
||||||
|
pod-dev = pkgs.writeShellScriptBin "pod" ''
|
||||||
|
set -u
|
||||||
|
buildlog=$(mktemp)
|
||||||
|
trap 'rm -f "$buildlog"' EXIT
|
||||||
|
if ! cargo build --quiet -p pod 2>"$buildlog"; then
|
||||||
|
cat "$buildlog" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
manifest=$(cargo locate-project --workspace --message-format plain 2>/dev/null)
|
||||||
|
target_dir=''${CARGO_TARGET_DIR:-$(dirname "$manifest")/target}
|
||||||
|
exec "$target_dir/debug/pod" "$@"
|
||||||
|
'';
|
||||||
|
in
|
||||||
pkgs.mkShell {
|
pkgs.mkShell {
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
nixfmt
|
nixfmt
|
||||||
|
|
@ -6,12 +25,12 @@ pkgs.mkShell {
|
||||||
git
|
git
|
||||||
rustc
|
rustc
|
||||||
cargo
|
cargo
|
||||||
|
pod-dev
|
||||||
];
|
];
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
pkg-config
|
pkg-config
|
||||||
openssl
|
openssl
|
||||||
];
|
];
|
||||||
INSOMNIA_POD_COMMAND = "cargo run -p pod --quiet --";
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
echo "dev-shell-loaded"
|
echo "dev-shell-loaded"
|
||||||
'';
|
'';
|
||||||
|
|
|
||||||
40
tickets/resume-scope-claim.md
Normal file
40
tickets/resume-scope-claim.md
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Resume 時の Scope Claim の改善
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
`tickets/dynamic-scope.md` で in-process Scope の縮小(SpawnPod による委譲時の Write revoke)と pod-registry 上の delegation 記録が揃った。これにより「セッション中に scope が縮む」状態を Pod / registry の双方が一貫して表現できる。
|
||||||
|
|
||||||
|
一方で `tui -r` 経由の resume は、`crates/tui/src/spawn.rs` の `build_overlay_toml` を通じて fresh spawn と同じロジックで overlay を合成する。manifest cascade に scope 宣言が無い場合、cwd 直下に `write` 再帰の rule を毎回付ける挙動。
|
||||||
|
|
||||||
|
このため次のような衝突が起きる:
|
||||||
|
|
||||||
|
- セッション S が稼働中に SpawnPod で子 C を作り、cwd 配下のサブパスを委譲した
|
||||||
|
- 親が exit、子 C は registry 上にエントリが残存(あるいはまだ稼働中)
|
||||||
|
- ユーザーが S を resume しようとすると、新しい Pod が cwd 全体に `write` を claim → 委譲された部分と overlap して registry が拒否
|
||||||
|
|
||||||
|
resume の意図は「過去のセッションの続きを取る」であって「過去の effective scope より広い範囲を新たに掴み直す」ではない。現状は後者になっており、過去に手放した scope を resume が勝手に取り戻そうとする形になっている。
|
||||||
|
|
||||||
|
## ゴール
|
||||||
|
|
||||||
|
セッション resume 時に claim する scope が、当該セッションが最後に持っていた effective scope に揃う。委譲済み・他 Pod が保持中の部分は claim 対象から外れ、resume された Pod は当時と同じ範囲だけで動作する。
|
||||||
|
|
||||||
|
## 要件
|
||||||
|
|
||||||
|
- resume 時の overlay 合成は cwd 盲信ではなく、当該セッションが過去に持っていた scope を反映する。情報源は session log / registry / その他のいずれでも良いが、何らかの永続情報から復元できること
|
||||||
|
- 過去の scope 情報が取得できないセッション(旧形式 / 破損)は、明示的なエラーで止めるか、ユーザーに確認させてから fresh claim にフォールバックする(黙って広げない)
|
||||||
|
- claim 試行が registry の既存 allocation と衝突した場合、エラーメッセージで衝突相手の Pod 名 と target rule の双方が伝わる(現状は Pod 名のみ)
|
||||||
|
- 委譲済みエントリ(`delegated_from` を持つ allocation)が同じセッションの委譲チェーンに属する場合、resume はその範囲を claim せずに進行する
|
||||||
|
|
||||||
|
## 完了条件
|
||||||
|
|
||||||
|
- 「親 Pod がセッション中に SpawnPod を実行 → 子に委譲 → 親 exit → 親セッションを resume」のフローが、既存子 allocation を残したまま衝突なしで成功する
|
||||||
|
- 既存の無関係な Pod と衝突するケースは、衝突 rule と相手 Pod 名を含む明確なエラーで失敗する
|
||||||
|
- 単体テスト or 統合テストで上記 2 ケースが検証される
|
||||||
|
- 既存の fresh spawn (resume なし) の挙動には変化なし
|
||||||
|
|
||||||
|
## 範囲外
|
||||||
|
|
||||||
|
- 過去スコープの永続化スキーマを新規導入するかの判断は実装時に決める(session log の既存フィールドで足りるなら追加しない)
|
||||||
|
- 自動的に既存 Pod を kill / reclaim して claim を通す挙動
|
||||||
|
- protocol 経由の外部からの GrantScope / RevokeScope(`tickets/dynamic-scope.md` の範囲外宣言を継承)
|
||||||
|
- registry 側のエラー型の全面再設計(rule 情報を含めるための最小限の拡張のみで足りる想定)
|
||||||
Loading…
Reference in New Issue
Block a user