- Status: Implemented; focused quality gate failed
- Date: 2026-08-12
- Scope:
/rlm <directory> -- <question>、file-indexed REPL、shared limits、real workload benchmark - Implementation root:
poc/pi-rlm/
残っている問題は検索機能不足だけではない。
systemic root causeは、file-indexed contextがtyped evidence authorityではなく、raw string helperとfree-form stdoutとして実装されていることにある。そのためruntimeは、modelが見たsource範囲、subcallが参照したsource、root transcriptへ流れるデータ量、次のprovider callが残budgetへ収まることを保証できない。
修正方針は次の4点とする。
- source accessをboundedなtyped evidenceとして表現する
- subcallをevidence IDへbindし、無関係なreadではunlockできなくする
- free-form stdoutをbounded observationへ置き換える
- root、child、one-shotを含む全provider callをdispatch前に予約する
answer contractは維持し、runtimeが正解を推測してcandidateを自動補正することは禁止する。
原因分析と設計判断には次を使う。
- current implementation:
poc/pi-rlm/src/ - deterministic tests:
poc/pi-rlm/test/ - benchmark artifacts:
poc/pi-rlm/platform-indexed-*-results.json - run traces:
poc/pi-rlm/platform-indexed-*-runs.jsonl - workload source identity: artifactに記録されたPlatform commit
次はcurrent failure mechanismの根拠として扱わない。
- 現在の
/workspace/platformworking tree - benchmark後に変更されたcurrent Platform HEAD
- synthetic benchmarkだけから推測したreal workload quality
- reportに転記された集計値だけで、対応artifactとharness sourceが一致しないもの
real workload artifactが使ったPlatform sourceは47a97fa4a389e68adc48a4bc07403b222cef38caであり、working treeを使用していない。
現行PoCは次を満たしている。
- 長大なsourceをroot model chatへ直接入れない
- file contentをparent-side
FileIndexedContextへ保持する - subprocessまたはDockerでisolated REPLを実行する
- parent、child、one-shot model callでshared limitsを使う
- subcall inputのtoken/costをdispatch前に予約する
answer.contentのundefined、empty、exact validator rejectionを検出する- rejected candidateとcorpus call traceをboundedに保存する
関連実装:
poc/pi-rlm/src/file-context.tspoc/pi-rlm/src/repl-worker.tspoc/pi-rlm/src/repl-client.tspoc/pi-rlm/src/runner.tspoc/pi-rlm/src/shared-limits.ts
file-indexed REPLは次を公開する。
files
list_files(prefix?)
read_file(path)
grep_files(literal, options?)
find_symbol(name, options?)
current grep_filesはglobal literal searchで、結果はpath、line、1行のtextだけを持つ。current read_fileはファイル全文を返す。find_symbolはdefinition/referenceのline heuristicであり、symbol bodyを返さない。
file-indexed modeでは、同じdepthで一度でもread_fileが成功するとsubcallを許可する。
このgateは「sourceを一度readした」ことしか証明しない。subcall promptへ関連sourceが含まれることや、readしたfileとsubcall questionが対応することは証明しない。
Nexus Vault 50-projects/rlm/reports/real-workload-poc.mdのlocal-first resultは16/24だが、最新のplatform-indexed-final-results.jsonを集計すると10/24である。
latest final artifactのfailure内訳:
token-limit: 11
answer-contract: 2
provider: 1
hard cells:
CLI error classification: 0/3
SDK supply-chain cross-file: 0/3
API timeout boundary: 0/3
したがって16/24をstable resultとして扱わない。
latest hard 9 runsの集計:
total tokens: 241,754
cache read + cache write: 229,067
cache share: 94.8%
modelの新規input/outputではなく、root AgentSessionに蓄積したtool resultと会話履歴の再利用が20K ceilingを消費している。
latest SDK repeat 3では次が観測された。
sdk/client_test.goとsdk/projects.goを繰り返しread- required generated clientの該当builderをevidenceとして確定できていない
llmSubcalls=10preflightRejectedSubcalls=3- rejected candidateはinsufficient evidenceまたはempty answer
つまり、無関係または不十分なlocal readでも後続subcallが許可されている。
path filterとcontext linesを追加した実験でも、hard cellsは0/9だった。
したがって次は採用しない。
grep_filesへpathを追加するだけcontextLinesを追加するだけ- promptでlocal-firstを強く要求するだけ
path/range指定は必要だが、それだけではevidence provenance、transcript size、provider reservationを保証できない。
| Loop | Problem observed | Why it exists | Evidence | Confidence |
|---|---|---|---|---|
| 1 | latest finalは10/24、hard cellsは0/9 | token limitとanswer contract failureが継続 | platform-indexed-final-results.json |
High |
| 2 | hard runsの94.8%がcache read/write | root AgentSessionが複数turnのtool resultを保持して再送する | run usage集計、runner.ts |
High |
| 3 | transcriptへ大きな観測が入る | helperがglobal 1-line grepかwhole-file readで、rlm_exec stdoutを最大20,000 characters返せる |
file-context.ts、repl-worker.ts |
High |
| 4 | corpusを継承しないsubcallへ不十分なevidenceを渡せる | runtime gateがsame-depthの任意のread_fileだけを検査する |
runner.ts、SDK failure trace |
High |
| 5 | growing root contextをproviderへ送ってからlimit failureになる | root provider callはpreflightせず、usage受信後にlimitを判定する | shared-limits.ts |
High |
- 20K token limit
- answer contract failure
- p95 latency tail
- run間の大きなquality variance
- cross-file control-flow
- generated sourceの深いsymbol trace
- exact source spellingを要求する複数file contract
複数のroot model turnが大きなtool outputを履歴へ蓄積し、cache read/writeを含むtotal tokensが20Kを超える。
- whole-file readとglobal searchしかない
- free-form stdoutが大きい
- any-read gateでsubcallがunlockされる
- root callのinput reservationがない
source evidence、subcall authority、model-visible observation、provider budgetがtyped runtime contractとして統合されていない。
実装後は次を常に満たす。
- raw corpusはmodel chatへ入れない
- full file contentはREPL local computationにだけ使う
- model-visible sourceはbounded
SourceSliceだけにする - subcallはcurrent corpus/revisionのevidence IDだけを参照する
- unrelated readはsubcall authorityを付与しない
- 同じevidence本文をroot transcriptへ繰り返し追加しない
- 全provider callはdispatch前にtoken/costを予約する
- answer correction用のfinalization budgetを先に確保する
- 予約不能なcallはproviderへ送らずfail closedする
- runtimeはcandidateから正解を推測・補正しない
- benchmark artifactをexact harness sourceとworkload sourceへbindする
type EvidenceId = string;
type MatchId = string;
interface SearchHit {
id: MatchId;
path: string;
line: number;
preview: string;
}
interface SourceSlice {
id: EvidenceId;
revision: string;
path: string;
startLine: number;
endLine: number;
text: string;
sha256: string;
truncated: boolean;
}
interface SearchRequest {
literal: string;
pathPrefix?: string;
maxResults?: number;
}
interface ReadLinesRequest {
path: string;
startLine: number;
endLine: number;
}
interface OpenMatchRequest {
matchId: MatchId;
before?: number;
after?: number;
}files
list_files(prefix?)
search_files(request)
read_file(path)
read_lines(path, startLine, endLine)
open_match(matchId, options?)
observe(evidenceIds)
search bounded metadata
-> choose one or more matches
-> open bounded source slices
-> runtime automatically observes opened slices
-> compute locally or invoke evidence-bound subcall
-> submit answer
search_files()のbounded metadataはtool resultへ自動掲載する。read_lines()とopen_match()は内部でobserve()を呼び、modelが明示的なobserve callを忘れてもsource sliceを次turnで確認できる。重複Evidence IDの本文は再送しない。
Initial defaults:
search max results: 100
source slice max lines: 200
source slice max characters: 16,384
observation max per turn: 4,096
provider output max per call: 512
unique observed source per run: 12,288
console diagnostic max: 1,024
exact valuesはfocused benchmarkで調整するが、全上限はruntimeで強制する。
read_file(path)は残す。aggregation、regex extraction、local symbolic executionにはファイル全文が必要なためである。
ただし次を禁止する。
- full stringを
observe()する - full stringを
console.log()する - full stringをraw subcall promptへ渡す
modelがsource本文を見る場合は、read_lines()またはopen_match()でSourceSliceを作る。runtimeはそのsliceをbounded observationとして自動掲載する。
Evidence IDは少なくとも次へbindする。
corpus ID
source revision
canonical path
start/end line
slice sha256
別run、別revision、別directoryのEvidence IDはrejectする。
current:
llm_query(prompt: string): Promise<string>new:
interface EvidenceQuery {
question: string;
evidenceIds: EvidenceId[];
notes?: string;
}
llm_query(query: EvidenceQuery): Promise<string>
rlm_query(query: EvidenceQuery): Promise<string>batched variantも同じrequest shapeを使う。
parent runtimeはdispatch前に次を行う。
- Evidence IDをcurrent evidence registryで解決する
- corpus IDとrevisionを検証する
- evidenceが空ならrejectする
- duplicate sliceを除去する
- runtime自身がsource manifest付きpromptを組み立てる
- estimated input token/costを予約する
- providerまたはchild RLMへdispatchする
modelはsource textをfree-form promptへ貼り直さない。
child RLMはparent directory全体を継承しない。選択されたevidenceだけをexternal child contextとして受け取る。
これによりrecursive semanticsを維持しつつ、child scopeとtoken usageを明示できる。
次のgateは削除する。
same depthでread_fileが一度でも成功したか
代わりに各subcall requestのevidenceIdsを検証する。
source evidenceをroot modelへ返す経路はobserve()だけに限定する。read_lines()とopen_match()はworker内部で次を呼ぶ。
observe([slice.id]): ObservationResultresultには次を含める。
interface ObservationResult {
evidence: SourceSlice[];
omittedDuplicateIds: EvidenceId[];
remainingObservationCharacters: number;
truncated: boolean;
}一度本文を返したEvidence IDを再度observeした場合、本文を再送しない。path、range、IDだけを返す。
console.logはscalarや短いdebug情報だけに使う。
- output max 1,024 characters
- limitを超えるoutputはsource prefixを返さずomitted markerへ置換する
- omissionとoriginal character countを明示する
- full valueはworker
stateへ残す
rlm_exec tool resultは次だけを返す。
- bounded observation
- compact execution status
- budget snapshot
- answer rejection
- structured replan
worker内のarbitrary stdout全体をtool resultへ流さない。
各rlm_exec tool callはterminate: trueでそのPi AgentSessionを終了する。次のcontroller turnはfresh in-memory AgentSessionで開始し、同じpersistent REPL workerと直近2件のbounded resultsだけを受け取る。
fresh Pi AgentSession
-> one provider response
-> one rlm_exec
-> terminate session
-> retain latest two bounded results
-> next fresh Pi AgentSession
complete variables -> persistent REPL state
older results -> controller historyから除去
これによりPiのmodel/tool lifecycleとusage accountingを維持したまま、全tool transcriptの再送とcache tailをなくす。
current shared limitsはsubcall promptをpreflightするが、root AgentSessionのprovider callは送信前に予約しない。actual usageを受信した後に20K超過を検出するため、strict ceilingになっていない。
全provider callを次のAPIへ通す。
interface ProviderCallReservationRequest {
model: Model<Api>;
serializedContext: Context;
maxOutputTokens: number;
callKind: "root" | "child-root" | "llm-subcall" | "rlm-subcall";
}
reserveProviderCall(request): ProviderCallReservation対象:
- root initial call
- root continuation after
rlm_exec - answer rejection continuation
- child RLM root call
- one-shot
llm_query - batched subcalls
優先順位:
- provider/model tokenizerが利用可能なら正確なestimate
- 利用不可ならserialized UTF-8 bytesからconservative estimate
- fixed protocol overheadを追加
- configured max output tokensを追加
cache hitを仮定してreservationを小さくしない。strict total-token ceilingでは、input、cache read、cache writeのいずれとして報告されても収まる値を予約する。
run開始時にanswer completion用budgetを予約する。
初期値:
finalization reserve: 2,000 tokens
answer correction turns: 1
observationやsubcallがfinalization reserveを消費する場合はdispatchしない。
予約不能ならprovider requestを送らない。
failure class:
budget-preflight
traceには次を残す。
estimated input
reserved output
remaining tokens/cost
call kind
rejection reason
providerDispatched=false
post-hoc overshootはprovider usage estimateの誤差として別classにし、zeroをacceptance criterionにする。
REPLへread-onlyで公開する。
budget.remainingTokens
budget.remainingCostUsd
budget.remainingRootTurns
budget.maxObservationCharacters
budget.finalizationReserveTokens
runtimeは引き続き次を検出する。
- undefined
- empty
- validator rejection
- retry exhaustion
runtimeは禁止する。
- expected answerのmodelへの公開
- candidateの自動書き換え
- source evidenceなしの推測補正
11.2 Public contract and hidden oracle
benchmark caseは次を分離する。
interface PublicAnswerContract {
description: string;
pattern?: string;
}
interface BenchmarkOracle {
expected: string;
validator: PiRlmAnswerValidator;
}PublicAnswerContractはmodelへ見せる。BenchmarkOracleはharness内だけに置く。
current questionのwrapped=<behavior>は表現が曖昧で、意味的に正しい説明もexact validatorでrejectされる。
questionは正解値を公開せず、表現単位を明示する。
wrapped=<exact method call used to obtain the wrapped ExitCoder code>
question変更後はDirect PiとPi-RLMを両方再実行し、旧結果と混ぜない。
通常の/rlmはexpected answerを持たないため、default contractはdefined/nonemptyだけとする。
将来schema validationが必要になった場合は、明示的なuser-provided contractとして追加する。benchmark固有のoracleをproduct runtimeへ埋め込まない。
current artifactはPlatform source commitを記録するが、Pi-RLM harness sourceへbindされていない。
さらにPoC sourceがuntrackedのため、次を確定できない。
- artifact生成時のhelper contract
- scoped grep実装が有効だったか
- benchmark後にどのsourceがrevertされたか
- exact dependency state
全benchmark artifactへ追加する。
{
"harnessSource": {
"gitCommit": "...",
"dirty": false,
"manifestSha256": "...",
"packageLockSha256": "..."
},
"workloadSource": {
"repo": "/workspace/platform",
"commit": "...",
"workingTreeUsed": false
}
}正式benchmarkはclean committed harnessを既定とする。
開発中にdirty sourceで実行する場合:
src/、test/、package.json、lockfileのdeterministic manifestを作る- exact source snapshotをartifactと一緒に保存する
dirty=trueを記録する- snapshotのないdirty artifactをMVP判定へ使わない
hashだけでは再現不能なので、dirty resultにはsource snapshotが必要である。
| File | Change |
|---|---|
poc/pi-rlm/src/worker-protocol.ts |
SourceSlice、Evidence ID、typed subcall、budget snapshot |
poc/pi-rlm/src/file-context.ts |
path-filtered search、line range、match handle、deterministic slice ID |
poc/pi-rlm/src/repl-worker.ts |
observe()、bounded console、evidence-bound query surface |
poc/pi-rlm/src/repl-client.ts |
structured observation result、per-execution trace |
poc/pi-rlm/src/runner.ts |
any-read gate削除、evidence registry、all-call reservation |
poc/pi-rlm/src/shared-limits.ts |
unified provider reservation、finalization reserve |
poc/pi-rlm/src/platform-pilot.ts |
harness source provenance、per-turn usage、provider dispatch trace |
poc/pi-rlm/src/platform-pilot-cases.ts |
public answer contract、CLI case wording |
poc/pi-rlm/test/indexed-context.test.ts |
any-read testをevidence-bound regressionへ置換 |
poc/pi-rlm/test/recursive-limits.test.ts |
root/child provider preflight tests |
behaviorを変えずに次を追加する。
- harness source identity
- root turn number
- model callごとのusage
- executionごとのobservation characters
- observed Evidence ID
- provider dispatch有無
- budget before/after
- answer candidateとrejection reason
目的は、どのexecution outputがcache tailを作ったかをartifactだけで追跡可能にすること。
実装:
search_filesread_linesopen_match- Evidence ID registry
observe- bounded console
- evidence-bound
llm_query/rlm_query - any-read gate削除
このphaseではAST/LSP dependencyを追加しない。
実装:
- root call preflight
- child root call preflight
- finalization reserve
- per-call output ceiling
budget-preflightfailure class- no-dispatch trace
対象:
CLI error classification
SDK supply-chain cross-file
API timeout boundary
条件:
model: openai/gpt-5.6-luna
repeats: 5
max tokens: 20,000
max cost: $0.05
thinking: off
same exact Platform commit
- 8 cases × 5 repeats
- Pi-RLMとDirect Piを同じexact source commitで比較
- Direct Piにはbundleではなくmaterialized exact file treeを渡す
- model、question、budget、thinking、case orderを揃える
- path filterがselection外を返さない
- canonical pathだけを許可する
- line rangeがinclusive/exclusive contractどおり
- max lines/charactersを超えるsliceをtruncateする
- match orderingがdeterministic
- same sourceからsame Evidence IDを生成する
- stale/foreign Evidence IDをrejectする
- tracked symlinkをfollowしない
- duplicate Evidence IDの本文を再送しない
- per-turn character ceilingを守る
- run-wide unique evidence ceilingを守る
- consoleへfull sourceを出せない
- full fileはworker stateに残る
- unrelated
read_fileでsubcallをunlockできない - evidenceなしsubcallをproviderへ送らない
- foreign revision evidenceをrejectする
- parentがevidence manifest付きpromptを構築する
- raw arbitrary source prompt APIを残さない
- root call予約不能時にprovider call countが増えない
- child rootも同じlimitを使う
- parallel reservationを合算する
- non-finalization provider callがfinalization reserveを消費できない
- finalization provider callだけがreserveを一度消費できる
- strict 20K ceilingを超えてdispatchしない
- timeout/cancellation時にreservationをreleaseする
7MB以上のsynthetic generated fileを作り、末尾近くのtarget symbolについて次を確認する。
- searchがtarget lineを返す
open_matchが16KiB以下のsliceを返す- model-visible transcriptへfull fileが入らない
- subcallはselected sliceだけを受け取る
hard 3 cases × 5 repeats = 15 runs。
合格条件:
correct: >= 14/15
all runs: <= 20,000 tokens
corpus-blind subcalls: 0
evidence-less subcalls: 0
post-hoc token-limit failures: 0
provider calls after preflight rejection: 0
provider/auth failureは隠さず別classとして記録する。正解率の分母から黙って除外しない。
8 cases × 5 repeats = 40 runs。
合格条件:
overall correct: >= 36/40
hard case families: each >= 4/5
strict token overshoot: 0
answer auto-corrections: 0
artifact provenance complete: true
36/40は90%のpoint gateであり、真の成功率が90%以上であることの統計的証明ではない。安定性を主張する段階ではcase数、repository、source revisionを増やす。
不採用。promptはmodel behaviorのhintであり、authority、provenance、budgetを強制できない。
不採用。既存実験でhard cells 0/9。必要だが十分ではない。
不採用。context isolationとsingle-call budgetを壊し、RLMの外部context設計を後退させる。
現時点では保留。language parser dependencyとsnapshot整合性が増える。まずbounded line evidenceでcurrent hard casesを解けるか確認する。
必要になった場合は、read_symbol()をEvidence IDを返す追加adapterとして実装し、raw AST objectをmodelへ流さない。
禁止。validatorはaccept/rejectだけを行い、expected answerや推測値でcandidateを書き換えない。
provider tokenizerがPi runtimeから利用できるか確認が必要。利用できない場合はconservative byte estimateを使う。過小estimateはstrict ceilingを壊すため、効率よりfail-closedを優先する。
4KiB per turn、16KiB per sliceは初期値。小さすぎるとsemantic contextを失い、大きすぎるとcache tailが再発する。focused benchmarkで調整する。
free-form questionからruntimeがexact schemaを安全に推論することはできない。MVPではnonemptyをproduct contractとし、exact correctnessはbenchmark oracleまたは明示的user contractで検証する。
current tracesにはexecution code、exact stdout size、harness source identityが不足している。そのため、cache tailを作った個々のturnまでは確定していない。Phase 0 instrumentationで閉じる。
この設計では次を行わない。
- source editingまたはmutation
- arbitrary untracked file indexing
- modelによるanswer自動補正
- all-language AST index
- subcallへのfull repository access
- prompt-only quality fix
- benchmark結果からDirect Piの全面置換を宣言
Phase 0からPhase 2、clean cutover、focused 15-run pilotまで実装した。
実装済み:
- harness/workload source provenanceとdirty source snapshot
- turn-level evidence、candidate rejection、provider reservation trace
search_files、read_lines、open_match、observe- revision-bound Evidence IDとevidence-bound subcall
- per-turn 4KiB、run-wide 12KiB、slice 16KiB、console 1KiB ceiling
- root、child、one-shotを含むprovider preflight
- typed
EvidenceQueryへのclean cutover。raw string subcallとany-read gateは削除 - finalization provider callだけが2,000-token reserveを消費するsingle-use scope
deterministic verification:
TypeScript typecheck: PASS
tests: 53
pass: 53
fail: 0
focused pilot:
model: openai/gpt-5.6-luna
Platform commit: 47a97fa4a389e68adc48a4bc07403b222cef38ca
harness manifest: 6c6fbd26492aef35b2897d8a20e9e2abb4066fcf2d891e90328c23983fc0addc
correct: 0/15
answer-contract: 10
budget-preflight: 5
max observed tokens: 16,024
post-hoc limit violations: 0
subcalls without evidence manifest: 0
provider dispatches after preflight rejection: 0
artifact:
poc/pi-rlm/platform-evidence-focused-finalization-results.json
poc/pi-rlm/platform-evidence-focused-finalization-runs.jsonl
poc/pi-rlm/platform-evidence-focused-finalization-harness-source.json
14/15 quality gateは不合格。source authority、observation ceiling、dispatch budgetはruntime invariantとして閉じたが、Lunaはhard casesで6 turns内に必要なsource extractionを完了・永続化・提出できなかった。
evidence runtime自体は採用する。巨大sourceのread-only queryを安全に実行する基盤として有効で、Docker-isolated /rlm /workspace/platform smokeは11,546 files / 202,708,693 bytesをindexし、NewRootCmdのAddCommand count 14を3 model calls / 2,326 tokensで返した。
一方、汎用real-workload MVPとautomatic routingはNO-GO。focused hard gateが0/15のため、Phase 4 full benchmarkへ進む根拠がない。
次の設計対象はbudget緩和やprompt追加ではない。read_symbol()相当のbounded structural evidence、またはtask-specific local plannerでCLI control-flow、generated SDK trace、API timeout boundaryを決定的に収束させ、その後に同じ15-run gateを再実行する。