Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ the existing context-delivery owner. C1 must converge the remaining composition
and consumer assumptions, rather than introduce another capture service or
rewrite already shared delta/native-resume logic.

A separate native-presentation slice reuses the committed full recovery as its
reader target, avoiding an unused second wire publication. Delta and unwrapped
inputs retain their own immutable files and the same reader/lifecycle owners.
On a matched 107,289-byte synthetic source, capture writes fall from 216,754 to
107,289 bytes; full reader bytes and the 3,937-byte warm delta are unchanged.
This qualifies only duplicate capture IO removal, not model-input reduction,
complete cost, accepted outcomes or C1 writer/consumer cutover.
For a small explicit output ceiling, native presentation can also omit repeated
directory/selection commands while retaining the complete recovery command:
append `--list` or `--select` to discover/read current facts without a baseline.
The original 4,000-byte cold/warm/compaction cases retain their requirements,
Stop, original identity and full recovery; this is transport qualification.

| Current source / obligation | Present envelope treatment | Required C1 disposition |
| --- | --- | --- |
| `interaction_contract.agent_channel.work_context` and pending required reads | `work_context` plus `required_reads`; identical inline material can be referenced | One complete requirement/evidence source with resolvable references; preserve pending reads and their completion conditions |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ Quota capture 与 Turn journal capture 都调用
context-delivery owner。C1 应收敛剩余的组合逻辑与消费者假设,不另建 capture service,
也不重写已经共享的差分与 native-resume 逻辑。

独立的原生展示切片复用已提交的完整恢复原件作为 reader 目标,避免再次发布未使用的
wire 副本。Delta 与未包装输入仍保留各自的不可变文件,reader/lifecycle owner 不变。
同一 107,289 字节合成来源的 capture 写入从 216,754 降为 107,289 字节;完整 reader
字节及 3,937 字节的 warm delta 均不变。这只验证重复 capture IO 的消除,不证明模型
输入减少、完整成本收益、成果验收或 C1 writer/consumer 切换。
显式小预算下,原生展示还可省略重复的目录/选读快捷命令,保留完整恢复命令;
在其后追加 `--list` 或 `--select`,无须基线即可发现或读取当前事实。
原有 4,000 字节冷态、温态及压缩恢复用例保留完整要求、Stop、原身份和完整恢复;
这属于传输验证。

| 当前来源 / 义务 | 当前 envelope 处理 | C1 必须保留的归属与语义 |
| --- | --- | --- |
| `interaction_contract.agent_channel.work_context` 与未完成必读 | `work_context` 加 `required_reads`;相同内联材料可改用引用 | 一份完整要求/证据来源与可解析引用;保留未完成必读及其完成条件 |
Expand Down
7 changes: 6 additions & 1 deletion docs/reference/required-work-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,9 @@ capture and return a small file reference, not an eager first page. The response
names the delta/full `path`, digest and reader `command`; `recovery` names the
current complete file. A file delta additionally carries `baseline_reference`.
`index_command` lists current fields and `select_command` batches exact pointers.
Under a smaller explicit ceiling, these shortcut commands may be omitted;
append `--list` or `--select <pointer>` to `recovery.command` instead. Both
operations address the current complete capture, including during delta delivery.
The reference stays below 7900 serialized bytes (or the explicit smaller output
ceiling); if metadata cannot fit, recovery retains the current capture. Reader
pages remain independently bounded. A complete raw tool log is not proof that
Expand Down Expand Up @@ -724,7 +727,9 @@ SSH Goal 使用 `--runtime-profile codex_app_ssh_goal` 生成 context-config 和
不内联第一页。`path`、摘要与 `command` 指向当前差分或全量;`recovery` 指向当前
完整文件;文件差分另带 `baseline_reference`。`index_command` 展示当前字段目录,
`select_command` 批量选读精确路径。引用输出不超过 7900 序列化字节或调用者显式更小
的上限;元数据放不下时保留当前捕获恢复入口。读取页另受输出预算约束。
的上限;较小预算下可省略这两个快捷命令,改在 `recovery.command` 后追加
`--list` 或 `--select <pointer>`,差分投递时也直接读取当前完整捕获。
元数据仍放不下时保留当前捕获恢复入口。读取页另受输出预算约束。
原始工具日志完整,不证明宿主保留给模型的字节也完整。

原生 `captured_file` 基线来自校验后的前一完整文件,可差分复用所有未变字段,包括
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
INIT_PATH = REPO_ROOT / "loopx" / "cli_commands" / "__init__.py"
TODO_MODULE = REPO_ROOT / "loopx" / "cli_commands" / "todo.py"
QUOTA_MODULE = REPO_ROOT / "loopx" / "cli_commands" / "quota.py"
QUOTA_PRESENTATION_MODULE = REPO_ROOT / "loopx" / "cli_commands" / "quota_presentation.py"


def run_cli(*args: str) -> subprocess.CompletedProcess[str]:
Expand All @@ -41,6 +42,7 @@ def assert_source_shape() -> None:
init_text = INIT_PATH.read_text(encoding="utf-8")
todo_text = TODO_MODULE.read_text(encoding="utf-8")
quota_text = QUOTA_MODULE.read_text(encoding="utf-8")
quota_presentation_text = QUOTA_PRESENTATION_MODULE.read_text(encoding="utf-8")

assert_contains(init_text, "register_todo_command", "__init__")
assert_contains(init_text, "handle_todo_command", "__init__")
Expand All @@ -61,7 +63,8 @@ def assert_source_shape() -> None:
assert_contains(runtime_text, "handle_quota_command(", "cli_runtime.py")
assert_contains(todo_text, "render_todo_markdown", "todo module")
assert_contains(todo_text, "append_cli_rollout_event", "todo module")
assert_contains(quota_text, "render_quota_should_run_markdown", "quota module")
assert_contains(quota_text, "from .quota_presentation import", "quota module")
assert_contains(quota_presentation_text, "render_quota_should_run_markdown", "quota presentation")
assert_contains(quota_text, "append_cli_rollout_event", "quota module")


Expand Down
19 changes: 13 additions & 6 deletions loopx/cli_commands/quota_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,21 +209,25 @@ def native_context_capture_view(projected: dict[str, Any], directory: Path, *,
max_page_output_bytes: int | None = None) -> dict[str, Any]:
"""Publish file references for an already-decided native capture.

Persist the complete wire and expose navigation without eagerly importing
content into model history. Reading never collects or admits a decision.
Reuse full recovery; persist delta or unwrapped wire before exposing it.
Reading never collects or admits a decision.
"""
from ..control_plane.turn_driver.decision_capture import write_decision_capture

if max_page_output_bytes is not None and max_page_output_bytes <= 0:
raise QuotaDecisionTransportError("Native context output limit must be positive.")
wire = directory / "delivery.json"
write_decision_capture(wire, projected)
reference = {"path": str(wire), "sha256": hashlib.sha256(wire.read_bytes()).hexdigest()}
reference = None
mode, target = "full", None
if projected.get("schema_version") == "context_delivery_v1":
mode, target = projected["mode"], projected["target"]
if mode == "full":
reference = {key: projected["recovery"][key] for key in ("path", "sha256")}
if reference is None:
# Full delivery already reads its committed recovery. Delta and
# unwrapped inputs still need their own immutable reader target.
wire = directory / "delivery.json"
write_decision_capture(wire, projected)
reference = {"path": str(wire), "sha256": hashlib.sha256(wire.read_bytes()).hexdigest()}
page_bytes, output_bytes = native_context_page_budget()
if max_page_output_bytes is not None:
output_bytes = min(output_bytes, max_page_output_bytes)
Expand Down Expand Up @@ -265,7 +269,7 @@ def native_context_capture_view(projected: dict[str, Any], directory: Path, *,
result["instruction"] = CONTEXT_CAPTURE_NAVIGATION_GUIDANCE
if mode != "full":
result["instruction"] += (" command reads the delta; baseline_reference identifies its exact saved base when file-backed. "
"index_command/select_command read current facts directly without that baseline.")
"recovery.command reads current facts directly without that baseline.")
# The reference must fit even under an explicit small caller ceiling.
def fits():
return len(json.dumps(result, ensure_ascii=False, separators=(",", ":")).encode()) + 1 <= min(output_bytes, 7900)
Expand All @@ -275,6 +279,9 @@ def fits():
result.pop("select_command") # --select also works on the current recovery command.
if not fits() and "baseline_reference" in result:
result["baseline_reference"].pop("command", None) # Exact path/hash remain usable.
if not fits():
result.pop("index_command")
result["instruction"] += " Append --list or --select to recovery.command to discover or read current fields."
if not fits():
raise QuotaDecisionTransportError("Native context reference exceeds the output limit; "
"read the saved original decision and receipt without rerunning admission.")
Expand Down
2 changes: 1 addition & 1 deletion loopx/semantics/inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def _assigned_name(node: ast.AST) -> tuple[str, ast.AST] | None:
return None


def parse_python(source: SourceFile) -> ast.AST:
def parse_python(source: SourceFile) -> ast.Module:
"""Parse one tracked Python source, rejecting an unparseable one by path.

Deliberately not cached. Caching the trees would hold roughly two million
Expand Down
70 changes: 43 additions & 27 deletions loopx/semantics/python_production.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
import ast
from collections import Counter
from dataclasses import dataclass
from functools import lru_cache
from types import SimpleNamespace
from typing import Mapping, TypeVar
from typing import Mapping, TypeVar, cast

from .inventory import SourceFile

Expand Down Expand Up @@ -137,20 +138,29 @@ def enum_members(source: SourceFile, symbol: str, *, strict: bool = False) -> di
return result


@lru_cache(maxsize=4096)
def _field_mentions(source: SourceFile) -> frozenset[str]:
"""Reuse parsed names, not another retained AST or a source-text filter."""
names: set[str] = set()
for node in ast.walk(_parsed(source)):
if isinstance(node, ast.Name):
names.add(node.id)
elif isinstance(node, ast.Attribute):
names.add(node.attr)
elif isinstance(node, ast.Constant) and isinstance(node.value, str):
names.add(node.value)
elif isinstance(node, ast.keyword) and node.arg is not None:
names.add(node.arg)
return frozenset(names)


def python_literal_uses(source: SourceFile, field: str) -> set[str]:
"""Literal writes and direct dispatch operands; not alias/data-flow proof."""
tree = _parsed(source)
nodes = list(ast.walk(tree))
# Inspect parsed names/keys, including escaped strings and normalized
# identifiers. Parse errors must still fail even when the field is absent.
if not any(
isinstance(node, ast.Name) and node.id == field
or isinstance(node, ast.Attribute) and node.attr == field
or isinstance(node, ast.Constant) and node.value == field
or isinstance(node, ast.keyword) and node.arg == field
for node in nodes
):
# Parsing precedes absence: escaped keys and normalized identifiers count,
# and invalid syntax must fail even without a textual field mention.
if field not in _field_mentions(source):
return set()
tree = _parsed(source)

def reads(node: ast.AST) -> bool:
if isinstance(node, ast.Name):
Expand Down Expand Up @@ -188,7 +198,7 @@ def literals(node: ast.AST) -> set[str]:
# conditions and does not inspect strings containing sample source text.
rows = scan_python_production(source, field=field, enums={})
result = set().union(*(row.values for row in rows))
for node in nodes:
for node in ast.walk(tree):
if isinstance(node, ast.Compare):
for left, operator, right in zip(
[node.left, *node.comparators[:-1]], node.ops, node.comparators, strict=True,
Expand Down Expand Up @@ -222,9 +232,9 @@ def _qualified_bindings(source: SourceFile, tree: ast.Module, owners: Mapping[st
if value is None and modules is not None and alias.name in symbols:
# One unrenamed re-export hop through a tracked module binds the
# same owner; only names that are owner symbols are followed.
target = _tracked_module(module, modules)
if target is not None and target.path != source.path:
value = _reexported(target, alias.name, imports)
reexport_source = _tracked_module(module, modules)
if reexport_source is not None and reexport_source.path != source.path:
value = _reexported(reexport_source, alias.name, imports)
if value is not None:
bindings[name] = value
else:
Expand Down Expand Up @@ -323,11 +333,11 @@ def _module_functions(source: SourceFile, tree: ast.Module) -> dict[str, ast.Fun
# name some scope declares ``global`` and then stores is no longer reliably
# the ``def`` above; binding a call to that ``def`` would report the original
# body's returns for a function the module can swap at runtime.
for node in ast.walk(tree):
if not isinstance(node, ast.Global):
for global_node in ast.walk(tree):
if not isinstance(global_node, ast.Global):
continue
scope = _enclosing_scope(tree, node)
for name in node.names:
scope = _enclosing_scope(tree, global_node)
for name in global_node.names:
if name in defined and _stores_name(scope, name):
bound[name] += 1
functions = {name: node for name, node in defined.items()
Expand All @@ -338,14 +348,14 @@ def _module_functions(source: SourceFile, tree: ast.Module) -> dict[str, ast.Fun

def _index_value(node: ast.AST) -> str | int | None:
if isinstance(node, ast.Constant) and type(node.value) in (str, int):
return node.value
return cast(str | int, node.value)
if (isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub)
and isinstance(node.operand, ast.Constant) and type(node.operand.value) is int):
return -node.operand.value
return None


def _union(values: list[ast.AST]) -> ast.AST:
def _union(values: list[ast.expr]) -> ast.expr:
"""Fold several definitions of one local into a finite selection node.

The scan reports syntactic result possibilities, so a name written more than
Expand Down Expand Up @@ -407,6 +417,12 @@ def scan_python_production(
call_arguments = call_arguments or {}
calls = _qualified_bindings(source, tree, call_arguments, modules)
return_paths = return_paths or {}
# Every output form needs a field, registered return or qualified owner.
# Keep enum results and builder arguments even when the field is absent.
if not bindings and not calls and not return_functions and (
not field or field not in _field_mentions(source)
):
return []
module_functions = _module_functions(source, tree)
call_memo: dict[tuple[str, str], tuple[frozenset[str], tuple[str, ...]]] = {}
environments: dict[tuple[int, str], SimpleNamespace] = {}
Expand Down Expand Up @@ -481,7 +497,7 @@ def collect(node: ast.AST, loops: frozenset[int] = frozenset()) -> None:
if scope == '<module>':
nested_names -= local_owner_names | {owner.split('::')[1] for owner in call_arguments
if owner.split('::')[0] == source.path}
import_bound = set()
import_bound: set[str] = set()
for node in nodes:
if isinstance(node, (ast.Import, ast.ImportFrom)):
import_bound.update(alias.asname or alias.name.split('.')[0] for alias in node.names)
Expand Down Expand Up @@ -510,7 +526,7 @@ def collect(node: ast.AST, loops: frozenset[int] = frozenset()) -> None:
local_bindings = {k: v for k, v in bindings.items() if k not in shadows}
local_calls = {k: v for k, v in calls.items() if k not in shadows}

plain: dict[str, list[ast.AST]] = {}
plain: dict[str, list[ast.expr]] = {}
for node in nodes:
target = value = None
if (isinstance(node, ast.Assign) and len(node.targets) == 1
Expand All @@ -520,7 +536,7 @@ def collect(node: ast.AST, loops: frozenset[int] = frozenset()) -> None:
and node.value is not None):
target, value = node.target.id, node.value
if target is not None:
plain.setdefault(target, []).append(value)
plain.setdefault(target, []).append(cast(ast.expr, value))
declared = {name for node in nodes if isinstance(node, (ast.Global, ast.Nonlocal))
for name in node.names}
# Every store of the name must be one of those plain writes, so a value
Expand Down Expand Up @@ -772,7 +788,7 @@ def same_module_call(node: ast.Call, mode: str) -> tuple[set[str], bool] | None:
blocked(reason)
return set(values), bool(reasons)

def enum_object_value(node: ast.AST, seen: frozenset[str]) -> tuple[set[str], bool]:
def enum_object_value(node: ast.AST | None, seen: frozenset[str]) -> tuple[set[str], bool]:
node, seen = bound(node, seen)
if isinstance(node, ast.IfExp):
parts = [enum_object_value(value, seen) for value in (node.body, node.orelse)]
Expand Down Expand Up @@ -855,7 +871,7 @@ def scan_scope(body: list[ast.stmt], scope: str, parameters: set[str],
env = env if env is not None else environment(body, scope, parameters, call_shadows, own)
blockers = env.blockers

def record(node: ast.AST | None, form: str, location: ast.AST) -> None:
def record(node: ast.AST | None, form: str, location: ast.expr | ast.stmt) -> None:
# Reasons are read back by position: one scope's environment is
# shared with same-module call resolution, which may nest inside.
start = len(blockers)
Expand Down
Loading
Loading