mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-19 02:02:00 +00:00
Describe parent-equivalent helper reads and source-bound copies
This commit is contained in:
parent
5905bbbec2
commit
4a05222373
7 changed files with 26 additions and 16 deletions
|
|
@ -217,7 +217,7 @@ Used by `commit_reviewed` for all changes to the Ouroboros repository.
|
|||
| 15 | cross_platform | Does the diff use platform-specific APIs (`os.kill`, `os.setsid`, `os.killpg`, `os.getpgid`, `fcntl`, `msvcrt`, `signal.SIGKILL`, `signal.SIGTERM`, `subprocess` with `start_new_session`/`creationflags`, hardcoded `/` or `\\` in filesystem paths) outside of `ouroboros/platform_layer.py`? Does it import Unix-only or Windows-only modules (`fcntl`, `msvcrt`, `winreg`, `resource`) at any level without a platform guard (`sys.platform`/`IS_WINDOWS` check)? | critical |
|
||||
| 16 | changelog_accuracy | Do the exact wording, test counts, and minor description details in the README Version History row match what the diff actually does? Wording drift, off-by-one test counts, minor inaccuracies in descriptive prose — these belong here, NOT in `self_consistency` or `changelog_and_badge`. This item exists so reviewers have a dedicated advisory bucket for prose-level changelog imprecision that does not affect release metadata, runtime behavior, or safety contracts. | advisory |
|
||||
| 17 | gateway_parity | If the diff changes any browser-facing endpoint, WebSocket message, or frontend API call, are `ouroboros/gateway/contracts.py`, `ouroboros/gateway/router.py`, `web/modules/api_client.js`, `web/modules/api_types.js`, and `tests/test_gateway_parity.py` still aligned? Missing alignment is advisory unless it also breaks a frozen contract, safety guard, release metadata, or runtime behavior. | advisory |
|
||||
| 18 | subagent_isolation | If the diff changes `schedule_subagent`, child-task queueing, task constraints, tool discovery/execution, data reads, or memory handoff, does it preserve the accepted live-subagent contract: strict `subagent_id` + `objective` + `expected_output` schema, inferred lineage/workspace/contract/deadline/resource inheritance, `local_readonly_subagent` schema and execute-time allowlist, subagent-scoped secret/control-file denial for data tools, nested readonly delegation only within configured depth/cap limits (depth bounds how deep delegation NESTS, never actor strength; every new call names `subagent_id`, the scheduler snapshots the exact normalized `ConfiguredSubagent` route at task start, and an `agent_session` snapshot executes on the harness by construction — the host starts that exact leaf before the child's first model round without waiting on it, a definite typed start refusal ends the child unrun and typed at $0, and ambiguous start evidence wakes the model rather than terminaling; the model-visible schema must not expose `model_lane`/`executor`, while hidden legacy selectors map deterministically to one migrated row or return `subagent_selection_required`), for an explicitly read-only child, no arbitrary local writes/commits/review/runtime/tool-expansion/skills-lifecycle/shell (bounded media projection such as `extract_video_frames` may write derived outputs only under `artifact_store/video_frames` through a host-owned command shape), ordinary external tools follow owner policy and inherited resources; Cyber acting tools come from the actual registered catalog without inherited name exclusions or default-empty grants, while explicit read-only assignments retain their contract; the restricted subagent browser boundary (external HTTP(S) + `file://` scoped to `workspace_root` + loopback except actual Ouroboros control-service endpoints; concrete private origins require host-established `resource_policy.allowed_origins` with exact scheme/host/port and inherited/subset authority; unavailable identity for a matching recorded endpoint must not become foreign-service permission; apply the same target checks to direct navigation, actions and intercepted subresources, and validate every available redirect hop before returning page content; native browser redirects may send a request before post-navigation validation, so this is not a pre-request isolation or DNS-rebinding guarantee; metadata/link-local and reserved targets remain refused by the existing URL policy; `evaluate` JS unavailable to `local_readonly_subagent`, available to a valid `acting_subagent` on its current page; `vlm_query`/`analyze_screenshot` available), full task-result handoff, new/changed wait/timeout paths for cognitive work using progress-aware/re-decidable waiting rather than a fixed cutoff that discards in-flight work (P5), and tests for both allowed and blocked paths? | critical |
|
||||
| 18 | subagent_isolation | If the diff changes `schedule_subagent`, child-task queueing, task constraints, tool discovery/execution, data reads, or memory handoff, does it preserve the accepted live-subagent contract: strict `subagent_id` + `objective` + `expected_output` schema, inferred lineage/workspace/contract/deadline/resource inheritance, `local_readonly_subagent` schema and execute-time allowlist, parent-equivalent reads across native file, query, media and local-browser surfaces, with unchanged readonly mutation/command ceilings, nested readonly delegation only within configured depth/cap limits (depth bounds how deep delegation NESTS, never actor strength; every new call names `subagent_id`, the scheduler snapshots the exact normalized `ConfiguredSubagent` route at task start, and an `agent_session` snapshot executes on the harness by construction — the host starts that exact leaf before the child's first model round without waiting on it, a definite typed start refusal ends the child unrun and typed at $0, and ambiguous start evidence wakes the model rather than terminaling; the model-visible schema must not expose `model_lane`/`executor`, while hidden legacy selectors map deterministically to one migrated row or return `subagent_selection_required`), for an explicitly read-only child, no arbitrary local writes/commits/review/runtime/tool-expansion/skills-lifecycle/shell (bounded media projection such as `extract_video_frames` may write derived outputs only under `artifact_store/video_frames` through a host-owned command shape), ordinary external tools follow owner policy and inherited resources; Cyber acting tools come from the actual registered catalog without inherited name exclusions or default-empty grants, while explicit read-only assignments retain their contract; the restricted subagent browser boundary (external HTTP(S) + parent-equivalent local `file://` reads + loopback except actual Ouroboros control-service endpoints; concrete private origins require host-established `resource_policy.allowed_origins` with exact scheme/host/port and inherited/subset authority; unavailable identity for a matching recorded endpoint must not become foreign-service permission; apply the same target checks to direct navigation, actions and intercepted subresources, and validate every available redirect hop before returning page content; native browser redirects may send a request before post-navigation validation, so this is not a pre-request isolation or DNS-rebinding guarantee; metadata/link-local and reserved targets remain refused by the existing URL policy; `evaluate` JS unavailable to `local_readonly_subagent`, available to a valid `acting_subagent` on its current page; `vlm_query`/`analyze_screenshot` available), full task-result handoff, new/changed wait/timeout paths for cognitive work using progress-aware/re-decidable waiting rather than a fixed cutoff that discards in-flight work (P5), and tests for both allowed and blocked paths? | critical |
|
||||
| 19 | evolution_durability | If the diff touches `supervisor/git_ops.py`, `launcher.py`, `server.py`, `ouroboros/preflight_runner.py`, `ouroboros/tools/review_helpers.py`, `ouroboros/tools/git.py`, tests, review gates, or evolution code, does it preserve hermetic preflight, live repo/data mutation fuses, remote-optional local commit success, and transaction/rescue evidence for interrupted self-modification? | critical |
|
||||
| 20 | context_budget_ssot | If the diff changes context-size budgets/constants (`ouroboros/context_budget.py`), the context layout/manifest, a section's tier/policy, or the typed ContextFit deficit/reclaim contract: does it keep the low/max context split coherent (single SSOT + both profiles + docs + drift-guard tests in sync), preserve the tier-0 always-full core (BIBLE/SYSTEM/identity/scratchpad/knowledge-index/recent-dialogue) in EVERY mode, use a visible on-demand pointer instead of silent truncation (P1), and keep scope review independent of the context mode (scope review applies in every mode; the mode governs Ouroboros's own working window, never whether its changes are reviewed — a change that couples the two is an immune-system change under P3, not an incidental budget tweak)? Outside Cyber Pro the owner selects the context mode; Cyber may choose its own context and review settings through the existing writer. (PASS with "Not applicable" if no context-budget/layout change.) | critical |
|
||||
| 21 | capability_regression | Does the diff REMOVE or NARROW a previously-supported user-facing behavior or capability — a tool/flag/mode/path that worked before now errors or is gated tighter (e.g. a new `is_dir`/existence guard that blocks a legitimate create, a tightened allowlist that drops a real path, a removed fallback)? If so, is it INTENTIONAL and disclosed as a breaking/capability change in the commit message + changelog? Accidental capability removal is the failure class this item names. Ask whether a golden "from zero" test would have caught it. **Guard-change trigger (executable requirements, not an essay):** ADDING or TIGHTENING a guard, filter, allowlist, or deny rule IS a capability change and fires this item. For such a diff the reviewer must verify two things: (a) the diff STAGES A POSITIVE TEST that exercises a legitimate flow THROUGH the new guard and proves it still succeeds — a negative "it blocks X" test alone is insufficient (a gate can pass its own probe while breaking every real run); (b) the diff or its disclosure NAMES THE SURVIVING POSITIVE PATH — the concrete actor and flow that still work after the change. A guard change that stages no surviving-path test is a capability-regression finding, not a safety improvement. **Owner acceptance:** a narrowing counts as OWNER-ACCEPTED only when a GREEN plan review explicitly names that narrowing; owner acceptance makes the finding advisory (disclosed, non-blocking). Intent wording, a commit-message disclosure, or a changelog row alone is disclosure, NOT acceptance. Severity follows the `Critical surface whitelist` below — silently removing a documented capability or a safety/release contract is critical; an owner-accepted narrowing or an internal-only refactor is advisory. **Standing disclosures for this item live in `docs/CHECKLISTS_ARCHIVE.md`** (owner-accepted removals/narrowings and standing notes); they remain binding on every reviewer — consult that file before raising a removal/narrowing finding on a surface it covers, and do not re-raise anything recorded there. | advisory |
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@ integration is safe. Excluded nested repositories remain outside the snapshot
|
|||
inventory and are disclosed as an untracked residual.
|
||||
|
||||
A read-only child requests `mode: ask`, `access: readonly` under Claudexor's
|
||||
ordinary envelope. The host reads effective access back for both shapes;
|
||||
ordinary envelope. Its parent-selected starting folder is the session project
|
||||
root, not a read boundary; omitting it preserves inherited/folderless behavior.
|
||||
The host reads effective access back for both shapes;
|
||||
the delegated HOME/boundary checks below apply only to marker-carrying runs.
|
||||
|
||||
## 2a. Stable project identity and persistent registration
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ server.py (Starlette+uvicorn) ← HTTP + WebSocket on configurable host:port (de
|
|||
├── version.py ← Version string from the VERSION file with importlib.metadata fallback
|
||||
├── secret_masking.py ← Exact Settings/MCP wire-placeholder emitters/recognizers + top-level secret repair before env overlay and persistence (§7)
|
||||
├── settings_integrity.py ← Task-local in-memory settings read view and strict settings-snapshot integrity pin; `OUROBOROS_SETTINGS_SHA256` enables the trust root (§7)
|
||||
├── credential_shapes.py ← Credential leaf names plus physical owner credential locations (§6 Credential fence and byte masking)
|
||||
├── credential_shapes.py ← Credential leaf names plus physical owner credential locations (§6 Credential mutation and diagnostic redaction)
|
||||
├── update_channels.py ← Closed Stable/QA/Development channel mapping and update-network defaults (§8)
|
||||
├── update_letter.py ← The update letter: `base..target` commit material plus README history rows (only bodies and the oldest row texts are bounded, and disclosed), one accounted LIGHT-slot call, `state/update_letter.json`, one projection shared by the Updates payload and the Runtime-context `official_update` fact (§7; §3 Updates)
|
||||
├── colab_bootstrap.py ← Google Colab source-mode bootstrap: official update source, stable local `ouroboros` branch, Drive-backed settings/data, personal origin, no-UI server command, native Telegram setup
|
||||
|
|
@ -429,7 +429,7 @@ server.py (Starlette+uvicorn) ← HTTP + WebSocket on configurable host:port (de
|
|||
├── tools/ ← Auto-discovered tool plugins (registry.py owns discovery; frozen module list for packaged builds)
|
||||
│ ├── registry.py ← Tool registry SSOT: loads tool modules, exposes schemas, executes safely; owns the shell-guard/process-tool membership sets
|
||||
│ ├── core.py ← File/data tools (read_file, write_file, list_files) + code search and digest helpers
|
||||
│ ├── core_file_tools.py, core_secret_paths.py, core_artifacts.py ← Core-tool leaves: the read/list file tools with the shared resource-access helpers; the restricted-subagent physical read-denial policy (owner secrets/control state, data roots, repository credential locations, listing redaction); the verbs that put something in front of a human (§6 Credential fence and byte masking)
|
||||
│ ├── core_file_tools.py, core_secret_paths.py, core_artifacts.py ← Core-tool leaves: the read/list file tools with the shared resource-access helpers; the retained delegated action-profile and runtime-control helpers; the verbs that put something in front of a human (§6 Credential mutation and diagnostic redaction)
|
||||
│ ├── shell.py ← Process tools `run_command`/`run_script` (in-process `_active_subprocesses` tracking; §9)
|
||||
│ ├── shell_guards.py ← Shared process-path inspection helpers and retained target extractors; process admission is owned by registry_guard_process (§6)
|
||||
│ ├── registry_core.py, registry_guards.py, registry_guard_process.py, tool_context.py ← The registry's leaves: the execution authority (load, schemas, safe dispatch); the host-owned pre-dispatch guards (capability/resource, managed-update and skill-payload constraints); process admission over the prepared target with post-execution observations (§6 Safety and runtime mode); and `ouroboros/tools/tool_context.py`, the concrete `ToolContext` + `BrowserState` (its protocol is `contracts/tool_context.py`)
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ A routing/promote decision turn receives host-built ground truth (each project's
|
|||
|
||||
### Tool capability and execution
|
||||
|
||||
`tool_capabilities.py` is the SSOT for the tool classes (core, meta, cognitive-memory, parallel-safe, stateful-browser, untruncated, capped-result, reviewed-mutative) and the child allowlists, which remain deliberately narrower principals. `tool_policy.py` chooses the initial capability set; `ToolRegistry` remains the execution authority; `loop_tool_execution.py` owns timeouts, concurrency, live evidence, result handling and mutative ceilings. Ordinary top-level presets share one built-in name surface: project focus changes the default target, while root policy, runtime mode, task-contract disables, credentials, resources, selected-resource bindings and delegated-child profiles narrow independently — a registered or discoverable tool is not thereby callable for a particular target. Nothing disappears silently: lazy discovery returns an explicit capability omission or `CAPABILITY_UNAVAILABLE`; a policy-filtered REGISTERED tool (a contract-disabled extension/MCP name included) is "hidden by policy: <reason>" (`ToolRegistry.policy_hidden_reason`), never "Not found". A name dispatch cannot run gets one answer (`ToolRegistry._name_miss_result`): its typed reason plus the callable names of the ONE namespace it addresses (`tool_policy.tool_namespace`), or above 12 their count and `list_available_tools(namespace=…)`, never another namespace's or a hidden name. `list_available_tools` (`tools/tool_discovery.py`, bound by the loop) reads `schemas()` in every mode; `loaded` is residency, not availability.
|
||||
`tool_capabilities.py` is the SSOT for the tool classes (core, meta, cognitive-memory, parallel-safe, stateful-browser, untruncated, capped-result, reviewed-mutative) and the child allowlists, which retain independent mutation and command ceilings. `tool_policy.py` chooses the initial capability set; `ToolRegistry` remains the execution authority; `loop_tool_execution.py` owns timeouts, concurrency, live evidence, result handling and mutative ceilings. Ordinary top-level presets share one built-in name surface: project focus changes the default target, while root policy, runtime mode, task-contract disables, credentials, resources, selected-resource bindings and delegated-child profiles narrow independently — a registered or discoverable tool is not thereby callable for a particular target. Nothing disappears silently: lazy discovery returns an explicit capability omission or `CAPABILITY_UNAVAILABLE`; a policy-filtered REGISTERED tool (a contract-disabled extension/MCP name included) is "hidden by policy: <reason>" (`ToolRegistry.policy_hidden_reason`), never "Not found". A name dispatch cannot run gets one answer (`ToolRegistry._name_miss_result`): its typed reason plus the callable names of the ONE namespace it addresses (`tool_policy.tool_namespace`), or above 12 their count and `list_available_tools(namespace=…)`, never another namespace's or a hidden name. `list_available_tools` (`tools/tool_discovery.py`, bound by the loop) reads `schemas()` in every mode; `loaded` is residency, not availability.
|
||||
|
||||
Outcome classification keeps policy refusal separate from execution failure, so an expected authority boundary never becomes the task's headline failure: `user_files_path_blocked`, `cwd_blocked` and `artifact_output_undeclared` are typed non-failure surfaces, while a declared output that cannot be registered is the genuine `artifact_output_error`. The identifier register (`tool_result._EXACT_IDENTIFIER_CODES`) applies the same rule: a typed refusal is recorded as a refusal, never as `ok`. It covers the whole routing family (`STEER_REJECTED`/`STEER_UNCONFIRMED`, `PROMOTE_REJECTED`/`PROMOTE_UNCONFIRMED`, `ROUTE_REJECTED`/`ROUTE_UNCONFIRMED`, `ROUTING_UNCONFIRMED`, `NEEDS_MANUAL_TARGET`), so a promote or route that scheduled nothing is never read as a SUCCESSFUL call; a `_REJECTED` result carries its `(reason: detail)`, and the refusal stays visible to the agent (`is_error`) without degrading the execution axis — the host refused, the agent did not fail. File discovery distinguishes a confinement refusal (an error), a failed listing (a failure) and a path holding nothing — a MISS (`LIST_FILES_NOT_FOUND`, a warning beside `DATA_NOT_YET_CREATED`) that leaves the rest of the outcome uncoloured, because the recovery scan credits only a later success on the same target. The delegate tools (`delegate_start`, `delegate_wait`, `delegate_cancel`, `delegate_answer`) reach the same register from the producer side: `delegate_shared._fail` writes the `ok: false` + `host_code` envelope BESIDE the domain payload (whose `reason` keeps its own vocabulary), so every substrate refusal (daemon, ownership, containment, cancel, subscription window) is RECORDED as a refusal, never counted as a successful call — mapped to `TOOL_REPORTED_FAILURE` (never degrading), a malformed call to `TOOL_ARG_ERROR` (degrades, feeds reflection), never to a timeout or a generic tool error — and a terminal run in state `failed` or `cancelled` is a SUCCESSFUL observation of an unsuccessful leaf.
|
||||
|
||||
|
|
@ -120,11 +120,11 @@ Tool API v2 exposes neutral canonical names directly (`read_file`, `list_files`,
|
|||
|
||||
Filesystem results identify their canonical `root:path`; `run_command`/`run_script` echo the resolved `cwd`. A direct Project room selects one folder for file reads/edits, process cwd, VCS and delegation; governance stays at `system_repo`. A missing folder retains its address and availability note, never falling back to Ouroboros source; a folderless task uses its own `task_drive` scratch without workspace or write authority. Plain folders support file/process work and directory delegation (`delegate_directory.py`); Git-only snapshot/integration paths refuse non-Git targets. Resolution preserves physical identity: absolute paths inside a selected base normalize to it; rootless absolute paths bind to their permitted base; outside paths refuse before `safe_relpath` can turn them into similarly named files. Repo basename-prefix, lineage and delegated-artifact redirects use the same resolver as guards and handlers (`ToolContext.repo_path`/`drive_path`). Docker backend addresses (`workspace_executor.map_backend_path`) are accepted only inside `active_workspace`; other roots and unmapped paths remain confined.
|
||||
|
||||
`user_files` is the first-class root for user-visible files under the owner's home (the Ouroboros repo and runtime control plane are refused); `task_drive` is task-scoped scratch; `artifact_store` is task-scoped under `data/task_results/artifacts/<task_id>/`, created lazily on a write or output registration, where deliverables written through `user_files` or declared process `outputs` are copied for audit, and a rewritten user-visible file keeps its prior copy under `task_results/artifact_versions/<task_id>/` (§1 tree). `subagent_projects` and `deliverables` grant `read`/`list`/`search` only; the operation matrix (`tool_access_types._POLICY`) is closed under read⇒list,search and write⇒edit — a root a profile may read it also lists and searches through the same per-file guards, a root it may write it also edits — which adds no write-like operation to any read-only row; a read-only child reads `deliverables` and its lineage's (parent/root, each also on its own non-symlinked headless drive) `task_drive`/`artifact_store`, never a sibling's; a continuation also reads the `task_drive`/`artifact_store` of the one predecessor its contract names (`predecessor_authority.source.task_id`, one hop, read-only; a child carrying the envelope reads it too) (issue #1232); a top-level task writes the Deliverables container through `user_files`. Process admission keeps the original argv and the prepared physical target, and that one binding serves every other authorized destination; command words, script examples and unknown interpreter effects establish no write intent (the Supervisor's source: §6 Safety and runtime mode), and the post-execution shell audit is observational: no replay, rollback, interpreter or attribution proof. Invalid path-like prose yields no finding. A failed audit adds a bounded diagnostic (typed results record its exception class in `output_audit_unavailable`) while keeping stdout/stderr and completed exit, signal, timeout and runtime facts; it cannot invent a spawn failure or undeclared output. Computed targets stay a disclosed parser limit, not grounds for a new semantic scanner.
|
||||
`user_files` is the first-class root for user-visible files under the owner's home (the Ouroboros repo and runtime control plane are refused); `task_drive` is task-scoped scratch; `artifact_store` is task-scoped under `data/task_results/artifacts/<task_id>/`, created lazily on a write or output registration, where deliverables written through `user_files` or declared process `outputs` are copied for audit, and a rewritten user-visible file keeps its prior copy under `task_results/artifact_versions/<task_id>/` (§1 tree). `subagent_projects` and `deliverables` grant `read`/`list`/`search` only; the operation matrix (`tool_access_types._POLICY`) is closed under read⇒list,search and write⇒edit — a root a profile may read it also lists and searches through the same per-file guards, a root it may write it also edits — which adds no write-like operation to any read-only row; helpers inherit their parent's physical read reach in every runtime mode, including canonical runtime data and recorded ancestor workspaces; their own relative roots stay execution-local and their write/command ceilings stay independent; a continuation also reads the `task_drive`/`artifact_store` of the one predecessor its contract names (`predecessor_authority.source.task_id`, one hop, read-only; a child carrying the envelope reads it too) (issue #1232); a top-level task writes the Deliverables container through `user_files`. Process admission keeps the original argv and the prepared physical target, and that one binding serves every other authorized destination; command words, script examples and unknown interpreter effects establish no write intent (the Supervisor's source: §6 Safety and runtime mode), and the post-execution shell audit is observational: no replay, rollback, interpreter or attribution proof. Invalid path-like prose yields no finding. A failed audit adds a bounded diagnostic (typed results record its exception class in `output_audit_unavailable`) while keeping stdout/stderr and completed exit, signal, timeout and runtime facts; it cannot invent a spawn failure or undeclared output. Computed targets stay a disclosed parser limit, not grounds for a new semantic scanner.
|
||||
|
||||
#### Credential fence and byte masking
|
||||
#### Credential mutation and diagnostic redaction
|
||||
|
||||
Ordinary-mode protection of credentials, Bible and identity keys on PHYSICAL owner locations, source operands and actual VCS metadata, never on names: ordinary project names such as `tokens.json`, `credentials.json`, `.nojekyll` or `.well-known` do not identify host credential stores, and an owner's input or output is never refused on a SUFFIX or a WORD in a file name — on attachment ingest, on export/Deliverables, on a `user_files` mutation or in the git lanes — beside the exact-name and retained dotenv-tail rules. The enumerated physical owner stores (`credential_shapes.owner_credential_locations`) are the mutation-fence authority; path-selected attachment ingest checks exact credential leaves and credential/control directory components; ordinary `.config`, `Library`, `settings.json` and exact `~/.ssh/config` mutations need no special authority; ordinary git capture adds content evidence (`workspace_patch_capture.pem_private_key_reason`, shared by the patch, the coop checkpoint and the attached-folder snapshot). This is not blanket protection for all credential stores: unlisted locations such as `.cargo/credentials.toml`, `.terraform.d/credentials.tfrc.json` and `.kaggle/kaggle.json` keep ordinary root configuration access, and children keep source visibility for ordinary `auth/`/`tokens/` paths and public PEM certificates. `core_secret_paths.restricted_data_roots` anchors owner/control checks on the child, canonical parent and configured admission roots, shared with vision/media; runtime secret/control entries keep their predicate even inside the project. Reads and searches share ONE byte masker in every scope: restricted repository views mask known token formats and complete PEM private-key blocks while ordinary long identifiers, hashes and source bodies pass, so search and read deliver the same bytes; owner-home output does not mask opaque runs either, and known credential bytes stay masked on delivered views.
|
||||
Ordinary-mode protection of credentials, Bible and identity keys on PHYSICAL owner locations, source operands and actual VCS metadata, never on names: ordinary project names such as `tokens.json`, `credentials.json`, `.nojekyll` or `.well-known` do not identify host credential stores, and an owner's input or output is never refused on a SUFFIX or a WORD in a file name — on attachment ingest, on export/Deliverables, on a `user_files` mutation or in the git lanes — beside the exact-name and retained dotenv-tail rules. The enumerated physical owner stores (`credential_shapes.owner_credential_locations`) are the mutation-fence authority; path-selected attachment ingest checks exact credential leaves and credential/control directory components; ordinary `.config`, `Library`, `settings.json` and exact `~/.ssh/config` mutations need no special authority; ordinary git capture adds content evidence (`workspace_patch_capture.pem_private_key_reason`, shared by the patch, the coop checkpoint and the attached-folder snapshot). This is not blanket protection for all credential stores: unlisted locations such as `.cargo/credentials.toml`, `.terraform.d/credentials.tfrc.json` and `.kaggle/kaggle.json` keep ordinary root configuration access, and children keep source visibility for ordinary `auth/`/`tokens/` paths and public PEM certificates. File reads, searches, code queries, extracted media text and staged read-only inputs preserve source content without token/PEM replacement in every mode and actor profile. Read-view facts retain `source_masked=false` so exact-source review receipts remain verifiable. Configured-value command-output redaction, stored observability/log redaction, Settings placeholders, capture exclusions and skill-publication scanning have separate owners and remain unchanged. A helper has no additional `.git`, credential-name or listing filter; parent per-file rules still apply at the resolved physical base.
|
||||
|
||||
#### Web access mechanisms (three distinct paths — do not conflate)
|
||||
|
||||
|
|
@ -310,7 +310,7 @@ Children coordinate through `tree_note` and `tree_read`; read-only children can
|
|||
|
||||
**Registry.** `configured_subagents.py` owns `OUROBOROS_SUBAGENTS`: strict `{enabled, items}`, at most ten rows with hidden `subagent_id`, route-handle name (§3 Available subagents), any-language `recommended_use`, normalized API/session route, optional effort, account pin, access and row `enabled`. Session access defaults to `full` or the owner's `workspace_write`; snapshots lacking access retain workspace_write. Legacy env inputs fail closed. Row `enabled` defaults true, rejects nonbooleans and serializes only false, preserving fingerprints/receipts. Off rows retain configuration but leave catalog, alternatives, legacy matching, local autostart and reviewer resolution. Explicit selection returns `subagent_disabled`, distinct from global `subagents_disabled` and live unavailability. Tools take handle or stored id; roster edits refuse twins; records name their own engine. Captured snapshots never consult live settings. Descriptions guide the LLM, never host ranking/keyword routing; exact selection starts that route or refuses without substitution.
|
||||
|
||||
**Scheduling.** `schedule_subagent` requires `subagent_id`, a focused `objective` and `expected_output`; the remaining public fields describe child-local context, constraints, capability needs, write surface, narrower deadline, delegation budget and acceptance claims. `access=inherit` (default or omitted) preserves the configured session access; `readonly|workspace_write` can only lower it. API-model rows ignore this field with a result disclosure; `write_surface` stays the read/write authority. There is no model-visible lane/executor axis and no public `effort` override: the selected row is the complete execution choice, lineage/bounds/route/budget are host-derived, and omission never inherits the parent's acceptance claims. `subagent_runtime.select_subagent_snapshot` copies an immutable snapshot of the exact enabled row into the child task: an `api_model` row becomes an ordinary recursive API child on that exact model/effort, an `agent_session` row an ordinary recursive Ouroboros nanny on that exact external route. Burst cash (each sibling launched before the first sibling's first response pays its own prefix write on cache-write-priced routes) is disclosed in the tool description as an affordance, never scheduled by the host; so is an exchange of addressed turns, stated in `objective`/`constraints` (never a contract field or host section; no stage, topology or participant count fixed). The old lane/executor resolver serves only old durable records: for historical lane envelopes `schedule_subagent` reports the requested lane only (effective facts remain on the dispatched child record), and a task carrying `configured_subagent` goes straight to `subagent_runtime`, so legacy policy cannot reinterpret an active selection.
|
||||
**Scheduling.** `schedule_subagent` requires `subagent_id`, a focused `objective` and `expected_output`; optional `workspace_root` names the starting folder (including a read-only Git subdirectory), and omission inherits the parent. That focus grants no writes and removes no inherited reads. The selected folder survives the child contract, admission, recovery and session project binding. `parent_workspace` records the parent's actual root/mode/source before selecting the child folder; the read projection follows those existing delegation edges and the canonical runtime root, without a path-grant list. Relative roots remain local, and an explicitly named wrong root is still refused. The remaining fields describe child-local context, constraints, capability needs, write surface, narrower deadline, delegation budget and acceptance claims. `access=inherit` (default or omitted) preserves the configured session access; `readonly|workspace_write` can only lower it. API-model rows ignore this field with a result disclosure; `write_surface` stays the read/write authority. There is no model-visible lane/executor axis and no public `effort` override: the selected row is the complete execution choice, lineage/bounds/route/budget are host-derived, and omission never inherits the parent's acceptance claims. `subagent_runtime.select_subagent_snapshot` copies an immutable snapshot of the exact enabled row into the child task: an `api_model` row becomes an ordinary recursive API child on that exact model/effort, an `agent_session` row an ordinary recursive Ouroboros nanny on that exact external route. Burst cash (each sibling launched before the first sibling's first response pays its own prefix write on cache-write-priced routes) is disclosed in the tool description as an affordance, never scheduled by the host; so is an exchange of addressed turns, stated in `objective`/`constraints` (never a contract field or host section; no stage, topology or participant count fixed). The old lane/executor resolver serves only old durable records: for historical lane envelopes `schedule_subagent` reports the requested lane only (effective facts remain on the dispatched child record), and a task carrying `configured_subagent` goes straight to `subagent_runtime`, so legacy policy cannot reinterpret an active selection.
|
||||
|
||||
**Selected first-input sources.** `schedule_subagent(input_sources="declared")` opts a scheduled API-model child out of automatic shared-memory and prior-case channels; omitted/`shared` preserves ordinary continuity, independently of disk `memory_mode`. `context_input_selection.py` owns validation and composition. Full SYSTEM/BIBLE, declared assignment/evidence, actual authority, tools and the child's own working observations remain; identity/WORLD, shared dialogue/knowledge, project workpad/journal, inherited context/attachments/review notes, rosters/tree summaries and installation diagnostics do not enter automatically. A named predecessor retains a source pointer/read grant, not its narrative. The selector is captured in task contracts and inherited by descendants through restore/rebinding. Unsupported root/session shapes and direct delegated start/retry refuse typed before dispatch, rather than advertise unproved isolation.
|
||||
|
||||
|
|
@ -347,7 +347,7 @@ Each intended start mints a fresh invocation UUID as wire `Idempotency-Key`; con
|
|||
|
||||
`delegate_interactions.py` sends fresh live messages only when `/v2/operations` lists `POST /v2/runs/:id/messages` AND the route's `agent-capabilities.liveInput` is `mid_turn` or `next_tool_boundary`. Missing/none/unreadable capability is `unsupported` without POST; settled is `not_active` without POST. Engine `LiveMessageOutcome` and `reason` pass through: `delivered` proves correlated native consumption, not obedience; `accepted` proves the harness acceptance boundary, not consumption; `rejected`, `not_active`, `unsupported`, `delivery_unknown` retain their meanings. After both positive reads, any 404 becomes host `not_found` without touching custody or consulting `daemon_says_absent`; 409 `idempotency_conflict` is rejected, other 409s/5xx/transport death/deadline exhaustion delivery_unknown, and 400/413/422 bodies payload-rejected. Host-minted uuid4 `message_id` is returned and used as `Idempotency-Key`: only retry after delivery_unknown reuses it, bypassing both pre-POST short-circuits; every other outcome needs a fresh id. Steering stays attempt-local, never re-injected on native retry/continuation. `delegate_message_outcome` retains digest/size, not text; `delegate_wait` projects timeline `message.*` and delivered harness-status receipts carrying `messageId`.
|
||||
|
||||
A definite refusal needs a typed cause, no custody handle and either producer `definitely_unrun` or `_DEFINITE_UNRUN_REASONS`; it ends UNRUN at $0. Ambiguity wakes the model: uncertain live work cannot become a zero-spend terminal. Unsupported readonly/payload geometry (`directory_execution_unavailable`), unregistered roots and a complete selector binding nothing (`payload_selector_unresolved`) refuse before start with durable start-blocked evidence; the nanny registers first and retires owned registrations at settlement (`delegate_registration_policy`). The payload selector (default `root="active_workspace"` = omission; only a complete one binds) names authority through fresh `ResolvedResourceBinding(skill_payload.write)`, never grants it. Actor replacement/zero-run fences exclude `review_owned` runs: those belong to review custody, while settlement, money, containment and registration still see all runs. New delegation readers join the consumer matrix in `tests/test_custody_owner_kinds.py`.
|
||||
A definite refusal needs a typed cause, no custody handle and either producer `definitely_unrun` or `_DEFINITE_UNRUN_REASONS`; it ends UNRUN at $0. Refusal text states the observed cause; definite non-execution does not imply that a retry can heal it. Impossible non-default directory options on a Git target are rejected before enqueue, with the direct delegation check retained. Ambiguity wakes the model: uncertain live work cannot become a zero-spend terminal. Unsupported readonly/payload geometry (`directory_execution_unavailable`), unregistered roots and a complete selector binding nothing (`payload_selector_unresolved`) refuse before start with durable start-blocked evidence; the nanny registers first and retires owned registrations at settlement (`delegate_registration_policy`). The payload selector (default `root="active_workspace"` = omission; only a complete one binds) names authority through fresh `ResolvedResourceBinding(skill_payload.write)`, never grants it. Actor replacement/zero-run fences exclude `review_owned` runs: those belong to review custody, while settlement, money, containment and registration still see all runs. New delegation readers join the consumer matrix in `tests/test_custody_owner_kinds.py`.
|
||||
|
||||
**Finite leaf continuation** (`delegate_continuation.py`): engine `maxSeconds` cancellation settles as `outcome_reason=wall_clock_exceeded`, replayed as `terminal_reason`. Explicit `continue_from=<run_id>` admits a NEW run/cap/key only for the caller's settled run with that confirmed cause, full output read to EOF, captured patch applied/rejected without ambiguity, and unchanged actor/route/access/mode/isolation/authority target. Stop/Panic, owner deadline, user cancel, failure and unknown endings refuse. `continuation_of` and the host block retain predecessor/cause/disposition and the prior run's recorded access, so a mutating run that captured no patch is described as having written in place (its effects already on the target), never as read-only; the model authors remaining work in `prompt`. No session state transfers; `delegate_recovery.NO_RESUME_CAUSES` stays unchanged.
|
||||
|
||||
|
|
@ -397,7 +397,11 @@ WHERE a mutating run's changes are destined is the second, separate record — t
|
|||
|
||||
A payload target gets a standalone private Git snapshot (`subagent_worktrees.provision_payload_snapshot`): the live payload is never initialized as Git, and capture trusts nothing under the child-writable snapshot's `.git`. Disposition (`integrate_payload_patch`) applies a live, index-free `git apply` in no-repository mode under a whole-payload content-hash CAS (drift = typed conflict; identical content = idempotent applied); `GIT_CEILING_DIRECTORIES` is pinned at the payload's resolved PARENT (git still searches the ceiling entry itself, and an ancestor Git worktree above the runtime data root could otherwise make git skip every hunk at rc=0), and reserved paths refuse the WHOLE apply as `blocked_reserved_paths` with the candidate preserved. The post-apply outcome set is complete: a live loader hash equal to the recorded RESULT hash is the success; a hash equal to the recorded BASELINE hash with a non-empty touched set is a provable non-mutation that RESOLVES the apply intent (typed `INTEGRATE_APPLY_NO_OP`, the `apply_no_op` arm: no success, no disposition, no reconcile queued, retry lane open); anything else is the ambiguous mismatch, whose intent stays PENDING and whose reconcile marker IS queued (the payload did mutate). A successful apply queues the extension reconcile (`request_extension_reconcile`) and the skill's review goes STALE pending fresh `skill_preflight`/`skill_review`; a run whose ONLY change is a mode flip is already refused at CAPTURE time as `unreviewable_metadata_change`, so a live hash still equal to the baseline means nothing was written.
|
||||
|
||||
**A mutating run normally executes in a PRIVATE EXECUTION SNAPSHOT** (metered children keep sharing the tree; their patches integrate through `integrate_subagent_patch`: sha256-bound, 3-way `--index`, protected-path gated, genesis refused, `coop_already_in_tree` a no-op). At `delegate_start` the host snapshots tracked, staged and eligible untracked state, deciding sensitive/credential vetoes BEFORE hashing (`git add -A` would put secrets such as `.env` in the shared object database). Git's binary verdict for the whole untracked inventory comes from one index-versus-worktree `git diff --numstat` over a scratch index (two when empty files need their attribute verdict; `workspace_patch_capture.untracked_binary_verdicts`, shared with patch capture; a failed batch falls back to the per-file verdict with a warning), never one process per file by design. The machine-wide worktree ops lock (`subagent_worktrees._ops_lock`) guards SHARED metadata only — the registry file, a target's `.git/worktrees` and its baseline pin — held twice, briefly (row FIRST, then the ref, then `worktree add --no-checkout`, so a crash after the row is GC-nameable); the acting `self_worktree` lane is split the same way (`worktree add --no-checkout -b` + branch under the lock, `reset --hard --no-recurse-submodules` and deletion outside it — the body's own `post-checkout` hook no longer fires at provision either), and only the boot-time `prune_orphans` sweep and the millisecond genesis `git init` still do their work under it; listing, classifying, hashing, populating (`reset --hard --no-recurse-submodules`: what `worktree add` runs internally, minus the target's `post-checkout` hook), copying the source's exact bytes over the checkout and one `update-index` re-recording their stat (a CRLF-converting checkout otherwise leaves every such file "modified" in the child's `git status`) run outside it (issue #1241). A held lock refuses typed (`cause: lock_busy` + the holder's pid/task/op), a SIGKILLed holder is evicted by the owner-aware stale check, every refused snapshot provision is `definitely_unrun` with a durable `START_FAILED` row, and the start receipt discloses `snapshot` size/time. Disclosed residuals: the registry read-modify-write under the lock is O(registered rows, their per-file baseline maps included — moving those maps out of the row is a follow-up), and every eligible untracked text file is hashed into the TARGET's own object database, where it stays unreachable after the snapshot is removed until that repository's own gc. The baseline is pinned by `refs/ouroboros/delegated/` and checked out as a detached worktree; `scope.root` stays the authority target and `execution.workspaceRoot` names the snapshot. The host appends a separate typed binding after the immutable work order: the snapshot is writable and the authority is read-only until integration. Directory-copy runs use the engine-created copy and never the source folder. Full native access has no filesystem sandbox; the binding names the writable root but does not enforce it. Terminal capture records authority drift as evidence: ready-no-changes stays no-change with unknown authorship, while ready-with-changes keeps its private artifact and the locked baseline proof decides integration. Nested Git directories are excluded and disclosed; skill payloads use content-hash CAS. The binding is durable before POST and retry replays it; a GC-collected snapshot is a typed `execution_snapshot_missing` refusal. Worktrees live in `state/subagent_worktrees.json`; removal is explicit or custody-cross-checked startup GC, fail-closed on unreadable custody. The run still uses `live` from the engine view, so the scoped-HOME/`delegated` marker below applies.
|
||||
**A mutating session executes in a private snapshot.** Native/API `external_workspace` children share the target; `self_worktree` children copy the selected Git source's current eligible files. `workspace_copy` records source/execution roots, own-body identity and baseline; unstamped legacy copies retain system-repository semantics. Native copies keep task-owned branches/registry rows, not delegated custody. `integrate_subagent_patch` applies only the child's delta (sha256-bound, 3-way `--index`, own-body protected-path checks; genesis refused, `coop_already_in_tree` a no-op). Parents remain unchanged until explicit integration.
|
||||
|
||||
`subagent_worktrees` shares current-tree baseline construction: temporary index, tracked/staged and eligible untracked files, credential vetoes BEFORE hashing (plain `git add -A` would leak excluded files into the shared object database), exact copied bytes and file baselines. `workspace_patch_capture.untracked_binary_verdicts` batches Git's binary classification through index/worktree `diff --numstat` (a second batch for empty-file attributes); failed batches fall back per file with a warning. Actual destination/source overlap is refused; siblings in one worktree container are valid. The machine-wide `_ops_lock` covers registry, Git worktree metadata and baseline pins only: row first, ref and `worktree add --no-checkout`, then final registration. Listing, classification, hashing, `reset --hard --no-recurse-submodules`, byte copying, stat refresh (`update-index`, needed after CRLF conversion) and deletion run outside it, without post-checkout hooks. Boot `prune_orphans` and genesis `git init` retain their existing lock scope. `lock_busy` carries holder pid/task/op; owner-aware stale checks evict dead holders. Refused session provisioning records `definitely_unrun`/`START_FAILED`; start receipts disclose size/time. Residuals: locked registry updates are O(rows including file baselines), and eligible untracked text remains unreachable in the target's object database until its Git GC.
|
||||
|
||||
Session baselines are detached worktrees pinned by `refs/ouroboros/delegated/`; `scope.root` is authority, `execution.workspaceRoot` is execution. The durable binding follows the immutable work order before POST and replays on retry: write only the snapshot until explicit integration. Directory-copy sessions use the engine's copy; nested Git directories are excluded and disclosed, skill payloads use content-hash CAS. Full access supplies no OS sandbox. Captured authority drift keeps authorship unknown: no-change stays no-change; changed results retain their private artifact for locked baseline validation at apply. `state/subagent_worktrees.json` owns registration; removal is explicit or custody-checked startup GC, refusing unreadable custody. A collected snapshot returns `execution_snapshot_missing`. Engine isolation remains `live` with the scoped-HOME/delegated marker below.
|
||||
|
||||
At terminal, `delegate_wait` captures the run's diff against the baseline durably into the task's artifact store; NOTHING reaches the target automatically: the nanny explicitly applies or rejects through `integrate_delegated_patch`. Git and skill captures are whole-result operations: omitted `paths` and an exact empty list select the same captured result, disclosed when explicit; nonempty selectors stay directory-only. Engine-directory empty/subset semantics are unchanged. The staging substrate differs: a GIT workspace target applies under the repo git lock after PROVING no touched path drifted from `baseline_sha` (a plain `git apply` relocates hunks by offset; the touched-path set is read from `git apply --numstat` in BOTH directions, each naming only the paths it writes), then applies and STAGES, never commits. A SKILL-PAYLOAD target captures through the payload adapter over a parent-owned trusted index and applies LIVE into the non-Git payload: nothing is staged into any active root and no `.git` or index is created in the payload. The protected-path gate applies only when the target IS the Ouroboros body; a conflict (proven drift) is owned by the still-running nanny, with snapshot and patch persisting until explicit resolution or discard. Mutation rides an apply-intent protocol: a durable `delegate_run_patch_apply_started` row lands before any tree mutation, so on replay a pending intent without a disposition answers typed `INTEGRATE_DELEGATED_APPLY_AMBIGUOUS`, resolved only by explicit `acknowledge_ambiguous=true`, while the provably non-mutating outcomes (a lock error, proven baseline drift, a failed apply, a verified revert, a baseline-equal payload hash) RESOLVE the intent. `patch_verdict.py` is the ONE verdict writer for both pipelines: subjects are minted `run_<rid>` by the writer, never prefix-matched by readers, and each decision lands twice (artifact plus typed `delegate_run_patch_verdict` custody row), a failed artifact write disclosed on the row. `artifacts.delegated_capture_read_target` rebinds only the owning task's `artifact_store` READS of `delegated_runs/` and `source_handles/delegated_activity/` to canonical custody; activity reads stay inside that subtree after resolution, and `delegate_shared.orphan_capture_read_target` extends the same read-only, one-directory READ to the terminal-owner ORPHAN the disposition rule authorizes (confirmed by `orphan_disposition_status`), so the actor that may dispose a patch can inspect it without wider write authority. A read-only child stays in Claudexor's default envelope: one transport with one derived difference, not a second pipeline.
|
||||
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ of core" step in `.github/workflows/ci.yml`); the rest is review-only.
|
|||
refuse owner input or output on a SUFFIX or WORD inside a file name; dotenv
|
||||
spellings are the one surviving tail rule. Owner locations are a physical list
|
||||
(`credential_shapes.owner_credential_locations`), so an unlisted store keeps
|
||||
ordinary access; `make_subagent_secret_target_check` is prepared once per call
|
||||
and never retained; `pem_capture_refusal` keeps effective Cyber's finding
|
||||
ordinary access. Helpers inherit the parent's reads without file-name filters
|
||||
or content masking; `pem_capture_refusal` keeps effective Cyber's finding
|
||||
advisory with the requested bytes while ordinary modes keep the exclusion; the
|
||||
SSH config exception permits no key writes under `.ssh`.
|
||||
- An unlaunchable sole cmd element gets an actionable argv/shell hint, never
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ Run roots are append-only outside `repo/` and live `data/`; the focused contract
|
|||
and declared process `outputs` register/copy the canonical artifact; a
|
||||
rewrite keeps the previous copy in non-manifest history with last-5
|
||||
retention — recovery, never a second deliverable list. `root=deliverables`
|
||||
stays read/list/search-only and is never granted to children.
|
||||
stays read/list/search-only for every actor, including children.
|
||||
- Large task files stream (`artifacts.stream_artifact_file`, atomic
|
||||
`copy_artifact_file`): never a whole dataset in a bytes object, and a read is
|
||||
rejected as soon as it exceeds the source's initial regular-file size rather
|
||||
|
|
@ -307,6 +307,10 @@ and 23 (`delegated_transport`), both critical. The imperatives:
|
|||
`tests/test_browser_isolation.py`, `tests/test_browser_redirect_chain.py`).
|
||||
- Acting children return `workspace.patch`; only the parent commits the live body,
|
||||
applying via `integrate_subagent_patch` then its own `commit_reviewed`.
|
||||
An isolated Git child copies the selected source's current eligible contents,
|
||||
including uncommitted inputs; isolation alone does not make a project part of
|
||||
the Ouroboros body. Preserve the source binding through capture, cleanup and
|
||||
integration, and apply only the child's delta back to its source.
|
||||
`external_workspace` verifies and records without re-applying. Edit/capture text preserves external Git authority and patch-only `self_worktree`. Capture bases prove no authorship; compare via explicit `vcs_diff`. A genesis project's directory is its durable deliverable
|
||||
(until it declares a `.gitignore`, small
|
||||
text build output rides `workspace.patch`, bounded by the
|
||||
|
|
|
|||
|
|
@ -165,8 +165,8 @@ canonical deliverables, `skill_payload` for reviewed skill payloads, and
|
|||
`user_files` for user-visible files under the owner's home (a bare filename
|
||||
lands in the visible Deliverables folder, not the home root).
|
||||
`subagent_projects` and `deliverables` are read-only (never written or a
|
||||
shell cwd); a read-only subagent reads `deliverables`, every subagent its
|
||||
parent's and root's task files.
|
||||
shell cwd). A helper reads what its parent reads; its starting folder is a
|
||||
focus, not a read boundary. Read-only helpers do not write or run commands.
|
||||
|
||||
My cognitive memory has first-class tools — `update_identity`,
|
||||
`update_scratchpad`, `knowledge_write` — and I never reach for
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue