Skip to content
Draft
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
1 change: 1 addition & 0 deletions .github/shard-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"rest-1": [
"tests/test_agent_efficiency.py",
"tests/test_agent_run.py",
"tests/test_task_ir.py",
"tests/test_arch_error_details.py",
"tests/test_arch_plumbing.py",
"tests/test_arch_private_names.py",
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ files = [
"tools/learning_os/app/agent_plans.py",
"tools/learning_os/adapters/agent_runner.py",
"tools/learning_os/app/agent_presentation.py",
"tools/learning_os/app/task_ir.py",
]

# Linting (engineering review 2026-08-18, item 12; added 2026-08-21).
Expand Down
12 changes: 12 additions & 0 deletions system/developer/components/operation-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Admit, lock, dispatch and settle one operation through the shared application bo
- `tools/learning_os/app/agent_plans.py`
- `tools/learning_os/app/agent_presentation.py`
- `tools/learning_os/app/agent_reporting.py`
- `tools/learning_os/app/task_ir.py`
- `tools/learning_os/app/projection_readiness.py`
- `tools/learning_os/app/read_session.py`

Expand Down Expand Up @@ -58,6 +59,17 @@ Admission lives in `system/contracts/capabilities.yaml`; `app/admission.py` cons
`GESTURE_ALLOWLIST` and `UI_REVIEWED_ALLOWLIST` enforce it. The UI mirrors
its permitted gesture set and checks parity in its own contract check.

### Experimental Task IR P1

`task_ir.py` compiles four closed operations into the existing runner/gateway.
Read results retain bounded content, snapshot pins and expansion references; route binding requires complete discovery.
Private evidence retains original check/envelope bytes with digests and root/session/request bindings through the shared atomic writer.
Apply submits copied original bytes; interrupted outcomes use gateway replay-only and the strict committed-evidence verifier. Missing proof stays uncertain;
preview never replaces in-flight evidence, and interrupted requests are never
resealed. Legacy previews lacking original bytes require a new reviewed request ID.
Operator assertions do not authenticate human review. There is no new journal, session entity, approval protocol or general graph composition.
Pinning: `tests/test_task_ir.py`.

## Pinning tests

- `tests/test_ops_pipeline.py`
Expand Down
2 changes: 1 addition & 1 deletion system/procedures/add-or-evaluate-a-source.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ any destination outside the transcript `TMPDIR`.
```scrut
$ mkdir -p "$TMPDIR/inv-pair/repo" && export INV_PAIR="$(realpath "$TMPDIR/inv-pair")" INV_REPO="$(realpath "$TMPDIR/inv-pair/repo")" && $H/los-anchored-copy materials_manifest "$INV_REPO"
sha256:a0583ab05765120a52f688d88ed6cd03e56c1567d7183328d0c640424297b575 Makefile
sha256:6b5296af66bf7bfb1a03b3dbb6c8ee6778fa4e7ba121fac957d73d8502f14161 pyproject.toml
sha256:3ceabba96c45022e7f726ab432e918da7769554aac8b2f757f0db9033955cef0 pyproject.toml
sha256:0c84a5cae52dc8aa786e0009eaaa38e9e2f202b5f140e8cb07d1b188bc338ba8 tools/learning_os/__init__.py
sha256:9f4511b38e35ff56efa15bc8b486a589d9dc42eed38ab331da85c7e7f6503c41 tools/learning_os/domain/material_inventory.py
sha256:8bdcc529afb1448f8856ba13e615b894989c70786024d702e839f62b52e96e3b tools/materials_manifest.py
Expand Down
1 change: 1 addition & 0 deletions tests/group_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
"test_agent_efficiency.py",
"test_agent_reporting.py",
"test_agent_run.py",
"test_task_ir.py",
"test_projection_readiness.py",
"test_read_session.py",
"test_review_consolidation.py",
Expand Down
805 changes: 805 additions & 0 deletions tests/test_task_ir.py

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions tools/agent_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,47 @@ def build_parser() -> argparse.ArgumentParser:
parser.add_argument("--report-dir", default=None,
help="directory for the full report artifact (bounded stdout)")
parser.add_argument("--keep-report", action="store_true", help="exclude this run from report retention")
parser.add_argument("--task", default=None,
help="Task IR P1 spec JSON file (experimental; alternative to a plan)")
parser.add_argument("--task-phase", choices=("preview", "apply"), default="preview",
help="Task IR phase: preview retains the checked envelope; apply submits or reconciles it")
parser.add_argument("--evidence-dir", default=None,
help="Task IR run-evidence directory (outside the data root)")
return parser


def _run_task(args: argparse.Namespace, root: Path) -> int:
from learning_os.app import task_ir

if args.plan or args.expand_report or args.step:
raise PlanStepError("choose a plan, --expand-report, or --task")
if not args.evidence_dir:
raise PlanStepError("--task requires --evidence-dir outside the data root")
if Path(args.task).stat().st_size > 262144:
raise PlanStepError("task spec exceeds 256 KiB")
spec = task_ir.parse_spec(json.loads(
Path(args.task).read_text(encoding="utf-8"),
parse_float=_finite_number, parse_constant=_finite_number))
evidence_dir = Path(args.evidence_dir)
if args.task_phase == "preview":
report = task_ir.preview_task(
root, spec, evidence_dir,
run_reads=lambda plan, data_root: agent_runner.run_plan(
plan, data_root, report_dir=args.report_dir,
keep_report=args.keep_report))
else:
report = task_ir.apply_task(root, spec, evidence_dir)
print(encode_json(report))
return {"ok": 0, "needs_review": 0, "committed": 0, "unchanged": 0,
"refused": 2, "partial": 3, "uncertain": 3}.get(str(report["status"]), 3)


def main() -> int:
args = build_parser().parse_args()
root = Path(args.root).resolve() if args.root else TOOLS.parent
try:
if args.task:
return _run_task(args, root)
if args.expand_report:
from learning_os.app.agent_reporting import expand_report
if args.plan:
Expand Down
Loading
Loading