192 lines
9.5 KiB
Markdown
192 lines
9.5 KiB
Markdown
# Pod Factory: Profile resolution and prompt assets
|
|
|
|
`PodFactory` は、選択された Profile または明示的な one-file Manifest から、検証済みの `PodManifest` と `PromptLoader` を生成する境界である。通常の fresh spawn は profile discovery/default selection を使い、user/project `manifest.toml` の ambient cascade は使わない。
|
|
|
|
`PodManifest` は Pod 起動に必要な完全な runtime recipe で、Pod 名、具体的な scope、解決済みパス、model/provider 設定、worker 設定などを含む。Profile はその前段の再利用可能な recipe template であり、Pod 名や concrete scope authority など runtime-bound な値は resolver が起動入力から埋める。
|
|
|
|
---
|
|
|
|
## 起動モード
|
|
|
|
| モード | 入力 | 用途 | ambient manifest cascade |
|
|
|---|---|---|---|
|
|
| Profile | `--profile <selector>` または省略時 default | 通常の fresh spawn。Lua profile を解決して runtime Manifest を作る | 使わない |
|
|
| One-file Manifest | `--manifest <path>` | デバッグ/互換用の完全 Manifest escape hatch | 使わない |
|
|
| Restore/attach | `--pod` / `--session` 等 | 保存済み Pod state / session snapshot から再開 | profile を再評価しない |
|
|
| SpawnPod internal | hidden `--spawn-config-json` | 親 Pod が子用 config を解決して渡す | 使わない |
|
|
|
|
`profiles.toml` は profile registry/default selection のための UX 設定であり、Pod Manifest 層ではない。Profile の中身へ merge されない。
|
|
|
|
## Profile resolution
|
|
|
|
Profile は Lua で書かれる。Rust resolver は selected profile を restricted Lua VM 内で評価し、返り値が Profile-shaped であることを検証してから `PodManifest` に変換する。
|
|
|
|
```lua
|
|
local profile = require("yoi.profile")
|
|
local models = require("yoi.models")
|
|
local scope = require("yoi.scope")
|
|
local compact = require("yoi.compact")
|
|
|
|
local model = models.catalog("codex-oauth/gpt-5.5")
|
|
|
|
return profile {
|
|
slug = "coder",
|
|
description = "Project coding profile",
|
|
|
|
model = model,
|
|
worker = {
|
|
reasoning = "high",
|
|
},
|
|
compaction = compact.ratio {
|
|
threshold = 0.8,
|
|
request = 0.9,
|
|
},
|
|
scope = scope.workspace_write(),
|
|
}
|
|
```
|
|
|
|
Profile が表現してよいのは reusable な recipe fields である。
|
|
|
|
- model selection / provider policy
|
|
- worker settings / reasoning
|
|
- compaction policy
|
|
- memory / web policy
|
|
- permissions / tool policy
|
|
- session diagnostics policy
|
|
- scope intent such as `scope.workspace_write()`
|
|
|
|
Profile に入れてはいけないもの:
|
|
|
|
- `pod.name`
|
|
- concrete `scope.allow` / `scope.deny`
|
|
- runtime directories, sockets, callback addresses
|
|
- active/pending/session/pod-store runtime state
|
|
- resolved absolute paths that bind the profile to one machine/workspace
|
|
- raw resolved secret material
|
|
|
|
これらが必要な場合は、resolver の runtime input または `--manifest` の explicit Manifest path で扱う。
|
|
|
|
## Profile selector semantics
|
|
|
|
`--profile` は以下を受け付ける。
|
|
|
|
| 形 | 意味 |
|
|
|---|---|
|
|
| 省略 / `default` | registry default を使う。通常は bundled `builtin:default` |
|
|
| `<name>` | unqualified profile name。ambiguous なら fail closed |
|
|
| `builtin:<name>` / `user:<name>` / `project:<name>` | source-qualified selector |
|
|
| `path:<path>` / `./profile.lua` / `/abs/profile.lua` | 明示 path profile |
|
|
|
|
`.nix` profile files are no longer supported. Reusable profiles are Lua; complete low-level recipes belong behind `--manifest`.
|
|
|
|
Discovery は bundled builtin profiles、user registry (`<config_dir>/profiles.toml`)、project registry (`<project>/.yoi/profiles.toml`) を読む。後段の default が前段の default を上書きするため、project default は user/default builtin より優先される。unqualified ambiguous names は source-qualified suggestion を出して失敗する。
|
|
|
|
Example `.yoi/profiles.toml`:
|
|
|
|
```toml
|
|
default = "coder"
|
|
|
|
[profile]
|
|
coder = "profiles/coder.lua"
|
|
reviewer = "profiles/reviewer.lua"
|
|
|
|
[profile.orchestrator]
|
|
path = "profiles/orchestrator.lua"
|
|
description = "Project orchestrator"
|
|
```
|
|
|
|
Relative registry paths are resolved against the `profiles.toml` file that declares them.
|
|
|
|
## Controlled Lua environment
|
|
|
|
Profile evaluation runs with controlled host-provided `require`.
|
|
|
|
Host virtual modules:
|
|
|
|
- `require("yoi")`
|
|
- `require("yoi.profile")`
|
|
- `require("yoi.models")`
|
|
- `require("yoi.compact")`
|
|
- `require("yoi.scope")`
|
|
|
|
Profile-local modules can be reused with dotted names such as `require("shared")` or `require("shared.models")`; they resolve only under the selected profile file's directory. Unsafe/unrestricted Lua facilities such as `os`, `io`, `debug`, unrestricted `package`, `dofile`, `loadfile`, `load`, and `collectgarbage` are unavailable by default.
|
|
|
|
## One-file Manifest mode
|
|
|
|
`--manifest <path>` reads exactly one TOML file, resolves relative paths against that file's parent directory, applies builtin defaults, and validates through `PodManifestConfig -> PodManifest`. It does not load user/project `manifest.toml` files and conflicts with `--profile`.
|
|
|
|
Use `--manifest` only when you need the complete low-level Manifest escape hatch or a focused debug fixture.
|
|
|
|
## Builtin defaults
|
|
|
|
Base defaults that are independent of profile choice live in Rust constants under `crates/manifest/src/defaults.rs` and in `PodManifestConfig::builtin_defaults()`. The default role profile is embedded from `resources/profiles/default.lua` at build time and is discovered as `builtin:default`.
|
|
|
|
デフォルト値を変更するときは、次のどちらを変更するのかを明確にする。
|
|
|
|
- all manifests/profiles の baseline default: Rust defaults
|
|
- ordinary dogfooding/default role: embedded `builtin:default` profile sourced from `resources/profiles/default.lua`
|
|
|
|
## Path resolution
|
|
|
|
Profile-local Lua module paths are resolved relative to the selected profile file's directory and are constrained to that directory tree.
|
|
|
|
Manifest paths in one-file Manifest mode are resolved relative to the manifest file's parent directory before validation. `scope.*.target` and auth file paths must be absolute by the time `PodManifest` is constructed; a remaining relative path indicates a resolver bug and returns `ResolveError::RelativePath`.
|
|
|
|
Pod cwd is not part of the Profile. It is the process `current_dir()` for manual startup, or the parent-selected `Command::current_dir` for spawned Pods.
|
|
|
|
## Unknown fields and type errors
|
|
|
|
Profile validation is stricter than old ambient manifest cascade semantics at the top-level boundary. Complete-Manifest/runtime-authority fields such as `manifest`, `config`, `pod`, and concrete `scope.allow`/`scope.deny` are diagnosed rather than silently treated as reusable profile data.
|
|
|
|
One-file Manifest deserialization keeps the Manifest compatibility behavior: unknown fields may warn/ignore where the Manifest schema allows it, while type mismatches are hard errors with file/path context.
|
|
|
|
## Prompt assets
|
|
|
|
`worker.instruction` refers to a prompt asset used as the main system prompt body. Import-map prefixes:
|
|
|
|
| Prefix | Resolution |
|
|
|---|---|
|
|
| `$yoi` | bundled `resources/prompts/` (`include_dir!`) |
|
|
| `$user` | `<config_dir>/prompts/` |
|
|
| `$workspace` | `<project>/.yoi/prompts/` |
|
|
|
|
`.md` extension can be omitted, e.g. `$yoi/default` resolves to `resources/prompts/default.md`. Missing files are hard errors; prefixes do not fall through.
|
|
|
|
Profile and one-file Manifest CLI paths currently use builtin prompt assets only for initial loader construction. `$yoi/...` works; `$user/...` and `$workspace/...` prompt refs need a future explicit prompt-loader source design instead of reviving ambient manifest discovery.
|
|
|
|
The rendered instruction body is followed by fixed Rust-provided sections for working boundaries and, when present, `AGENTS.md`. User templates cannot remove the scope section.
|
|
|
|
## `yoi pod` CLI
|
|
|
|
Normal fresh startup uses profile discovery/default selection:
|
|
|
|
```text
|
|
yoi pod [--profile <selector>] [--profile-pod-name <name>] [-s/--store <path>]
|
|
```
|
|
|
|
| Flag | Description |
|
|
|---|---|
|
|
| `--profile <selector>` | Select a Lua profile. Omitted means registry default, normally `builtin:default` |
|
|
| `--profile-pod-name <name>` | Fresh-spawn runtime `pod.name` override for profile resolution |
|
|
| `-s, --store <path>` | Session persistence directory, default `<data_dir>/sessions/` |
|
|
|
|
Restore/attach uses Pod/session state and does not re-evaluate profile sources.
|
|
|
|
```text
|
|
yoi pod --pod <name>
|
|
yoi pod --session <uuid>
|
|
```
|
|
|
|
Spawn children use hidden `--spawn-config-json`, `--adopt`, and `--callback <path>` flags. These are internal handoff details used by `SpawnPod` after the parent has allocated scope and prepared the child config.
|
|
|
|
`SpawnPod.profile` accepts registry/default selectors and the special `inherit` selector. Typical orchestration calls are `SpawnPod(profile = "project:coder")` for implementation work, `SpawnPod(profile = "project:reviewer")` for independent review, optionally `SpawnPod(profile = "project:orchestrator")` for lower-level coordination, or `SpawnPod(profile = "inherit")` when the child should reuse the parent role configuration. The explicit `SpawnPod.scope` remains the only delegated filesystem capability; selected or inherited profile scope is replaced by the tool argument.
|
|
|
|
## Programmatic boundary
|
|
|
|
New code should resolve profiles through the profile resolver and then construct Pods from the resulting `PodManifest` and `PromptLoader`. One-file Manifest helpers remain for tests/debugging. Avoid reintroducing user/project manifest cascade APIs as normal startup behavior.
|
|
|
|
```rust
|
|
let (manifest, loader) = resolve_profile_or_manifest(cli_inputs)?;
|
|
let pod = Pod::from_manifest(manifest, store, loader).await?;
|
|
```
|