From f794d99bdcf21717771047db14753a30f51b0a96 Mon Sep 17 00:00:00 2001 From: Darian Ngo Date: Fri, 24 Jul 2026 16:10:34 -0500 Subject: [PATCH 1/7] The worker answers questions about a corpus; it does not transport one Collaborator doctrine, July 24 2026, owner-approved for a framing pass: the worker never works with gigabytes at a time. It answers questions ABOUT gigabyte contexts, reading slices intelligently -- explicitly not everything -- and building a derived RESPONSE ARTIFACT over several turns and multiple slices, without extracting more than needed. It never emits text to the orchestrator and calls itself done. When bulk must be quoted it composes the record programmatically and hands it over wholesale. For code editing the repository is the response artifact. The occasion was my own error. Ratifying the slice bound one commit earlier I wrote, into config.py and CONFORMANCE section 2.3, that "a corpus arrives in ceil(bytes / max_result_bytes) round trips, so 2 MiB is ~500 calls per GB" -- presenting the bound as the lever for bulk transfer. That describes an operation the architecture does not have. DATA_MODEL section 6 is titled "The bounded materialisation exception" and says so in its first paragraph: prefer by-reference sinks; materialize is only for when the model itself must compute over the bytes. Repaired here, and the tell recorded with them: any sentence computing how much of a corpus fits through the model is treating the model as the transport. Three more of my own, found by a clean-room sweep. The CONFORMANCE layering table asked "how large a corpus may the model work over?" and answered with the guest's RLIMIT_AS -- locating the corpus inside the namespace, the one place the handle model says it never is, and contradicting its own next sentence since "potentially gigabytes" does not fit in 1 GiB. The table now separates the corpus (host-side, bounded by nothing here) from the working set. SPEC section 4.4 still carried the superseded July-22 frame rule as settled -- a breadcrumb I failed to follow when I ratified its replacement. And a DATA_MODEL section 6 quote was cited as section 7. docs/architecture/RESPONSE_ARTIFACT.md is new: the doctrine, the mechanisms that already hold it, and what four read-only sweeps found pulling the other way. The mechanisms are real -- submit refuses a bare literal structurally, handles keep the corpus out of the guest, locate/lines slice files, workspace read() returns an index and never contents, MCP capture is harness-guaranteed. What was missing is a frame tying them together, which is why every rule could hold while the system got narrated as a bulk reader. The findings are recorded and NOT acted on. The retrieval budget meters calls rather than bytes, so a model optimising against it maximises bytes per fetch. ITERATION BUDGET tells the worker it has very few turns and should combine loading and computing into one block, against a max-iterations default of 5. The corpus half of the runtime has no locate and no slice while the editing half has both. fetch_texts is a public unmetered twin returning a live dict, fewer keystrokes than the audited path. The word "artifact" reaches no model. The default module opens with "Load the full question catalog in one query", the module stating the discipline is active but out of DEFAULT_SELECTION, and the module teaching the model to stop is retired. Section 4 carries the design question I cannot settle: the doctrine's wholesale hand-off is not reachable under the security model, because answer.submit sits on the exfil ledger at 64 KiB, and nothing distinguishes a large legitimate deliverable from bulk exfil -- submit("'\n'.join(b['text'] for b in blocks)) satisfies the pillar exactly while regurgitating. Section 5 states what this authorizes: no prompt bytes (rule 16; the collaborator's standing is that this gets prompted and enforced later), no engine change, and no re-adjudication of TEST_TIME_TRAINING section 12.2, which holds the opposite view as a dated owner correction and only the owner retires. Reachability stated plainly: this record has no enforcing surface. Verified: every quotation re-read from the tree it cites, including the four module statuses and DEFAULT_SELECTION; pytest 1042; check:repo-surface PASS. --- docs/architecture/RESPONSE_ARTIFACT.md | 167 ++++++++++++++++++ .../repl-sandbox/REPL_SANDBOX_CONFORMANCE.md | 31 +++- .../product/repl-sandbox/REPL_SANDBOX_SPEC.md | 16 +- src/repl_sandbox/config.py | 19 +- 4 files changed, 217 insertions(+), 16 deletions(-) create mode 100644 docs/architecture/RESPONSE_ARTIFACT.md diff --git a/docs/architecture/RESPONSE_ARTIFACT.md b/docs/architecture/RESPONSE_ARTIFACT.md new file mode 100644 index 0000000..282b5a1 --- /dev/null +++ b/docs/architecture/RESPONSE_ARTIFACT.md @@ -0,0 +1,167 @@ +# The response artifact — what a query produces + +**Status: DOCTRINE stated by the collaborator (Matt) July 24, 2026; framing repairs AUTHORIZED by +the owner (Cnid) the same day. The findings in §3 are AUDITED AND UNRESOLVED — nothing in them is +authorized, and §5 says so explicitly.** + +This record exists because the mechanisms were shipped and the frame was not. Every individual rule +below is already written down somewhere in this repository, and a reader can honour all of them and +still narrate Trellis as a system that reads a corpus. That happened, in this repository, on the day +this record was written — which is the evidence that the scattered form was insufficient. + +--- + +## 1. The doctrine + +**The worker never works with gigabytes at a time. It answers questions *about* gigabyte contexts.** + +- **It reads slices intelligently — explicitly not everything.** The collaborator named the reason: + *"Your current training goal is to read everything. This will explicitly not read everything."* + The disposition being corrected for is exhaustive reading, and it is the model's default, not an + occasional lapse. +- **Its job is not to regurgitate.** It never emits text to the orchestrator and calls itself done. +- **Every query builds a response artifact:** a derived deliverable — a spreadsheet, a PDF with a + chart, a slide deck, text with an illustration, most often plain text. **Not one output.** + Composed over several turns and multiple slices, *"without extracting more than needed."* +- **Bulk quotation is composed, never read.** If gigabytes of text must appear in the deliverable, + the model composes the record **programmatically** and hands it to the artifact wholesale. It never + reads five hundred slices to do it. +- **For code editing, the repository is the response artifact.** The write is the deliverable; the + submitted string is a receipt. + +This is [CODE_MEDIATED_TEXT.md](CODE_MEDIATED_TEXT.md) applied one level up. That record fixes how +bytes move — the engine computes positions, the engine splices, the model supplies addresses. This +one fixes **what a turn is for**, and the two share a consequence: *the model is never the +transport.* + +**The tell.** Any sentence that computes how much of a corpus fits through the model — "N round +trips per GB", "the model loads the corpus", "reads the whole X" — is treating the model as the +transport. That arithmetic describes an operation this architecture does not have. One such sentence +was written into `src/repl_sandbox/config.py` and `REPL_SANDBOX_CONFORMANCE.md` §2.3 on July 24, 2026 +and repaired the same day; it is quoted here because the author had just finished ratifying the +layering it contradicted. + +## 2. What already holds it + +The mechanisms ship. This is not an aspiration record. + +| mechanism | what it holds | where | +|---|---|---| +| `trellis_answer.submit` | Refuses a **bare literal** by `ast.parse` — an expression referencing no REPL state is a retyped literal. Structural, not advisory. | `src/rlm/trellis_answer.py` | +| Handles, not payloads | The corpus never enters the guest; the algebra is handle-in / handle-out and crosses no content. | [REPL_SANDBOX_DATA_MODEL.md](../product/repl-sandbox/REPL_SANDBOX_DATA_MODEL.md) §4, §5 | +| By-reference sinks | `llm_query(context=[H])` and `answer.submit(H)` resolve host-side. *"Prefer by-reference sinks; `materialize` is only for when the model itself must compute over the bytes."* | same, §6 — titled **The bounded materialisation exception** | +| `locate` / `lines` | Query for an address; read a bounded slice. *"the model queries for a location, it never counts lines."* | `src/rlm/trellis_textedit.py` | +| Workspace index/segment | `read()` returns the index and **never** contents; a segment is a deliberate pull. | `src/rlm/trellis_workspace.py` | +| MCP capture | External results are captured mechanically; the model receives a stub with a preview. Harness-guaranteed, not model discipline. | `src/rlm/trellis_mcp.py` | +| `concat_files` | Builds sub-LLM buffers as one string *"instead of printing file contents through the REPL output cap"* — compose-and-hand-off, shipped. | `src/rlm/trellis_scaffold.py` | +| `trellis_upsum.commit` | Measures running state engine-side and refuses over budget with a per-key breakdown, so the model compresses against engine-computed numbers. | `src/rlm/trellis_scaffold.py` | +| Orchestrator routing | *"Route working state by reference, never by paraphrase."* | `src/core/agent/orchestrator_prompt.ts` | + +## 3. Where the system pulls the other way + +Audited July 24, 2026 by four read-only sweeps over `docs/architecture/`, the prompt surface, the +REPL-sandbox record set, and the runtime. **Findings only.** Each names a surface; none is a change +this record makes. + +### 3.1 The incentives point at one big read + +- **The retrieval budget meters calls, not bytes.** `_held["fetches"] += 1` fires once per successful + call at all three surfaces (`src/rlm/trellis_tools.py`). Sixty-four fetches buys sixty-four whole + documents exactly as readily as sixty-four paragraphs, so a model optimising against the budget + **maximises bytes per fetch**. The per-root dedup on `get_ast_blocks` sharpens it: one read of a + document, ever — under-fetching is unrecoverable, over-fetching is free. +- **`ITERATION BUDGET` instructs the opposite of composition.** The kernel tells the worker it has + *"very few REPL turns"*, to combine loading and computing into **one** block, and not to *"spend a + turn on tiny exploratory prints"* — against a `--max-iterations` default of **5**. The doctrine + wants an artifact composed over several turns and multiple slices. +- **No retrieval surface over the corpus has a `locate`.** `get_ast_blocks(root)` returns every block + of a document with full text and no way to ask for a range. The editing half of the runtime has + `load` (shape only), `locate` (addresses plus previews, capped) and a 200-line slice cap; **the + corpus half has neither.** The slice-reading affordance exists, over files, and was never built + over the substrate. +- **The friction gradient runs backwards.** Disciplined, budgeted, audited reads return JSON strings + needing `json.loads`; the undisciplined twins return live Python values. `fetch_texts` is public, + returns a real dict, and is *"NOT counted as a tool call and NOT audited"* — fewer keystrokes than + the metered path, reachable by `dir()`, and the natural next move for a model that has just hit the + dedup refusal. `trellis_postgres.conn` is the same gap with no ceiling. + +### 3.2 The bytes that reach the model do not carry the frame + +- **The word "artifact" reaches no model.** Nor "deliverable", "spreadsheet", "chart", "slide". With + none of it present, *produce an answer* defaults to what the workflow rules literally say: a string. +- **The default module opens with an exhaustive load.** A default run's only protocol module is + `spatial-flywheel`, whose first procedural instruction is *"Load the full question catalog in one + query."* The module that states the discipline cleanly — `workspace-discipline`: *"Index first, + then pull only the bounded material needed for the current step"* — is `active` but **not in the + default selection**. The module that teaches stopping — `estimation-discipline`: *"If every required + operand is bound and the answer is determined, stop searching immediately"* — is `retired` and + cannot load. +- **Nothing tells an editing run that the repository is the deliverable.** After `write_back`, the + worker is still told its final answer is a `FINAL_ANSWER` string, with no relation drawn between + them. It will predictably restate the edit in prose. +- **Two bounds are invisible until they fire.** The 64-per-run retrieval budget and the 64 KiB answer + cap are unstated in any addendum. The cap's refusal — *"Submit the result, not the corpus."* — is + the clearest anti-regurgitation sentence in the codebase and the model sees it only after building + the oversized answer. + +### 3.3 Records that state the opposite + +- **[TEST_TIME_TRAINING.md](TEST_TIME_TRAINING.md) §12.2** holds that *"large REPL dumps ARE + long-context modeling in practice"*, re-reading the pillar as a rule against *retyping* only while + explicitly conceding reading, and concluding the effective-context thesis *"survives only in narrow + form."* It is a dated owner correction from July 13, 2026, and the July 24 doctrine post-dates it. + **This needs re-adjudication by the owner, not a silent edit** — it is load-bearing for that + record's argument that long-context results apply to Trellis directly. +- **[REASONING_TEMPLATES.md](REASONING_TEMPLATES.md) §17** gives all eight reasoning modes the same + out-port, `answer`, and the locked port vocabulary offers no artifact type. `construction` — *"Build + or modify a file artifact in the edit root"* — routes `write_back` as a mid-flow node and terminates + at a submitted string. A mode space built from it cannot express a query that produces an artifact. + The apparatus is catalog text with no kernel behind it yet, which makes this the cheap moment. +- **`REPL_SANDBOX_SPEC.md` §4.2** writes `run_query(sql, params) -> rows`, putting the result set in + the namespace on the first call. `LEARNINGS` §10c already flagged this exact line on July 23 and the + correction never reached the record it names. +- **`REPL_SANDBOX_ARCHITECTURE.md` §3** says *"The root worker splits context"* — to split it you must + hold it. The shipped fan-out names handles and the host splices referents. + +## 4. The open design question + +**The doctrine's escape hatch is currently closed by the security model, and nothing distinguishes a +large legitimate deliverable from bulk exfil.** + +The doctrine says: when gigabytes must be quoted, compose the record programmatically and hand it to +the artifact wholesale. But `answer.submit` sits on the outbound ledger under a cumulative byte cap, +`THREAT_MODEL` prices the channel at `ANSWER_CONTENT_MAX_CHARS` (64 KiB), and +[DATA_MODEL](../product/repl-sandbox/REPL_SANDBOX_DATA_MODEL.md) §6 makes that same ceiling the +quantified exfil-rate residual. Under that reading a wholesale hand-off is not reachable at all. + +Worse, the two are presently indistinguishable by construction: `submit("'\n'.join(b['text'] for b in +blocks))` references REPL state, passes the literal check, and delivers 64 KiB of corpus text +engine-rendered and unretyped — **satisfying the pillar exactly while doing what the doctrine calls +regurgitation.** `submit` closes transcription error. It does not close volume. + +This is an owner decision, not a wording fix. It probably wants a distinct artifact sink — a +deliverable that is *written* by reference rather than *submitted* as a value, charged against a +different ledger from the exfil residual, with the provenance of its parts intact. + +## 5. What this record does not authorize + +- **No prompt bytes.** The collaborator's standing is that the doctrine *"will be prompted later and + enforced"*; authoring those bytes runs under `AGENTS.md` rule 16 and is not opened by this record. +- **No engine change.** Every item in §3 is a finding. The budget's metering, the missing corpus + `locate`, `fetch_texts`, the iteration budget, and the module selection are each a separate change + with its own gate. +- **No re-adjudication of TEST_TIME_TRAINING §12.2.** That is a dated owner correction and only the + owner retires it. + +**Reachability, stated rather than implied:** this record has no enforcing surface. Nothing refuses a +wholesale print, no guard couples a decisive step to a bounded read, and the term "response artifact" +reaches no model. It is a frame for work that has not been done, and it should be read as one until a +row in §2 says otherwise. + +--- + +*Siblings: [CODE_MEDIATED_TEXT.md](CODE_MEDIATED_TEXT.md) (how bytes move — this record is its +upper storey) · [RETRIEVAL_DISCIPLINE.md](RETRIEVAL_DISCIPLINE.md) (held state and the per-run budget) +· [WORKSPACE_AND_MODULES.md](WORKSPACE_AND_MODULES.md) §4.3 (thin control channel, fat heap) · +[REPL_SANDBOX_DATA_MODEL.md](../product/repl-sandbox/REPL_SANDBOX_DATA_MODEL.md) §6 (the bounded +materialisation exception).* diff --git a/docs/product/repl-sandbox/REPL_SANDBOX_CONFORMANCE.md b/docs/product/repl-sandbox/REPL_SANDBOX_CONFORMANCE.md index 1387463..fa1c1ca 100644 --- a/docs/product/repl-sandbox/REPL_SANDBOX_CONFORMANCE.md +++ b/docs/product/repl-sandbox/REPL_SANDBOX_CONFORMANCE.md @@ -144,19 +144,38 @@ wrong layer, and it had two consequences: | what is bounded | question it answers | constant | |---|---|---| -| REPL namespace | how large a corpus may the model work over? | `Tier0Limits.address_space_bytes` (1 GiB) | -| one slice | how much moves host store → guest namespace per call? | `BrokerCaps.max_result_bytes` (2 MiB) | +| the corpus | how large a body may the model reason over? | **nothing here** — it is host-side, behind handles | +| the working set | how much may the model hold in the namespace at once? | `Tier0Limits.address_space_bytes` (1 GiB) | +| one materialisation | how much crosses host store → guest namespace per call? | `BrokerCaps.max_result_bytes` (2 MiB) | | model attention | how much reaches the transcript? | `MarshalCaps` (20 KiB stdout, 64 KiB answer) | | one wire message | how large may a single frame be? | `DEFAULT_MAX_FRAME_LEN` (4 MiB) | -**The REPL is meant to be large — potentially gigabytes — and read in slices.** No bound in this -table constrains that; the ceiling on the corpus is address space. The token derivation survives as +**The corpus is unbounded by anything in this table, because it never enters the guest.** An earlier +edition of this row asked "how large a corpus may the model work over?" and answered with the guest's +own `RLIMIT_AS` — which locates the corpus *inside* the namespace, the one place the handle model says +it never is ([DATA_MODEL §8](REPL_SANDBOX_DATA_MODEL.md): *no row of the user's knowledge is ever +placed in the guest as a payload*). It also contradicted its own next sentence, since "potentially +gigabytes" does not fit in 1 GiB of address space. Address space bounds **what the model itself +materialised and is computing over** — the working set — and nothing else. The token derivation +survives as the way `max_result_bytes` is *chosen* — "about what one model pass could consider" — and is recorded as a **sizing convention, not an enforcing bound**, because treating it as enforcing is exactly the error above. -**The cost, stated:** a 2 MiB slice means a corpus arrives in ~500 round trips per GB. If bulk -transfer needs to be cheaper, `max_result_bytes` is the single lever and `max_frame_len` follows it. +**What this bound is *not*, corrected 2026-07-24.** An earlier edition of this section costed it as +"~500 round trips per GB", as though `max_result_bytes` were the throughput lever for moving a corpus +into the worker. **There is no such operation.** The worker never works with gigabytes at a time — it +answers questions *about* gigabyte contexts by reading slices and composing a derived **response +artifact** over several turns, extracting no more than it needs. `materialize` is the exception path, +which [DATA_MODEL §6](REPL_SANDBOX_DATA_MODEL.md) — titled *The bounded materialisation exception* — already states: *prefer by-reference sinks; +`materialize` is only for when the model itself must compute over the bytes.* When bulk content must +reach an answer it goes `answer.submit(H)` — resolved host-side, out through the audited egress, +never through the guest namespace and so never through this bound at all. + +So `max_result_bytes` is sized against **one computation's working set**, and the arithmetic +"a corpus takes N calls at this size" is a tell that the model is being treated as the transport. It +never is: the model supplies addresses and the engine moves the bytes +([CODE_MEDIATED_TEXT.md](../../architecture/CODE_MEDIATED_TEXT.md)). **The DoS property is independent of all of this and always held:** `frame.read_frame` compares the declared length against the bound *before* reading a body, so a frame declaring 4 GiB is refused for diff --git a/docs/product/repl-sandbox/REPL_SANDBOX_SPEC.md b/docs/product/repl-sandbox/REPL_SANDBOX_SPEC.md index 7869c37..6514601 100644 --- a/docs/product/repl-sandbox/REPL_SANDBOX_SPEC.md +++ b/docs/product/repl-sandbox/REPL_SANDBOX_SPEC.md @@ -157,13 +157,15 @@ capability. spec the standing provenance-grounded injection-objection and the outbound defeater seats, composed from the -1 doubt tier ([DOUBTS_WORKSPACE §8–§9](../../architecture/DOUBTS_WORKSPACE.md)); Guardrail 15 (prompt-engineering + hypershot-protocol) applies when the seat prompts are authored. -- **`MAX_FRAME_LEN` — rule settled July 22, 2026, shipped value still owner-gated.** Size the cap - off the worker's context window rather than off the largest frame the plumbing might carry, so it - is a structural guarantee (no single frame can context-saturate a worker) and not only a DoS - bound. Derivation: 1,050,000-token window × 50% × ~4 bytes/token ≈ 2.1 MB → **2 MiB**, set as - `config.DEFAULT_MAX_FRAME_LEN` with the window recorded beside it as the input to re-derive from - when the model pin changes. The DoS property is unchanged: the reader still rejects an over-length - declaration before allocating. +- **`MAX_FRAME_LEN` — RATIFIED July 24, 2026.** `max_result_bytes` **2 MiB** (one materialisation), + `max_frame_len` **4 MiB**, the frame **derived from the slice** and never from the worker's context + window; `tests/test_config.py` asserts `max_frame_len >= 2 * max_result_bytes`. Full ruling: + [CONFORMANCE §2.3](REPL_SANDBOX_CONFORMANCE.md). The superseded July-22 rule sized the frame off + the context window, which protects nothing the marshal caps do not already hold and left the frame + *below* `max_result_bytes`, so a legal broker result could not cross the wire; the token derivation + now sizes the **slice**, as a sizing convention rather than an enforcing bound. The DoS property is + unchanged and was never what the number bought: the reader rejects an over-length declaration + before allocating. - The vsock bridge is unbuilt glue — security-critical; spec its frame parser + privilege drop. - Warm-pool clean-slate-reset policy (only if pooling is adopted). - Whether to relax `_SAFE_BUILTINS` once the VM boundary exists (redundant defense-in-depth). diff --git a/src/repl_sandbox/config.py b/src/repl_sandbox/config.py index 3b0b83b..7ac0c14 100644 --- a/src/repl_sandbox/config.py +++ b/src/repl_sandbox/config.py @@ -87,9 +87,22 @@ def version_at_least(observed: str, minimum: str) -> bool: #: as though it protected the context would repeat, one layer over, the error that #: produced the correction. #: -#: Raising this is the single lever for bulk transfer: a corpus arrives in -#: `ceil(bytes / max_result_bytes)` round trips, so 2 MiB is ~500 calls per GB. -#: `DEFAULT_MAX_FRAME_LEN` follows it and must be re-derived with it. +#: **This is not a bulk-transfer bound, because there is no bulk transfer.** The +#: worker does not move a corpus through itself; it answers questions *about* one +#: by reading slices and composing a derived response artifact over several turns. +#: `materialize` is the exception path — DATA_MODEL section 6, whose title is +#: *The bounded materialisation exception*: *prefer by-reference sinks; +#: `materialize` is only for when the model itself must compute over the bytes.* +#: A gigabyte reaching an answer goes `answer.submit(H)`, +#: resolved host-side and leaving by the audited egress, never through this +#: constant and never through the guest namespace. +#: +#: So the number to size against is **one computation's working set**, not a +#: corpus divided by anything. Arithmetic of the form "a corpus takes N calls at +#: this size" is the tell that the model is being treated as the transport, which +#: it never is (`docs/architecture/CODE_MEDIATED_TEXT.md` — the model never counts +#: and never copies, so bulk movement is the engine's job). +#: `DEFAULT_MAX_FRAME_LEN` follows this constant and must be re-derived with it. DEFAULT_MAX_RESULT_BYTES = 2 * 1024 * 1024 #: Hard cap on the declared length of a single wire frame. **Derived from the From ab126f6fa7c56c337660bd49422d1a480f257f4b Mon Sep 17 00:00:00 2001 From: Darian Ngo Date: Fri, 24 Jul 2026 16:58:25 -0500 Subject: [PATCH 2/7] Rule 24 states what is being built, and a feature list says what that needs The target was written in records a construction decision never had to open, and the build converged on the nearest familiar shape: a retrieval system with a fact store bolted on. Rule 20 already names this failure for measurement -- reach for a comparison because it is closer to hand than a target. Rule 24 is the same ordering, one level up, and it is numbered last and printed first because burying a target where construction cannot see it is the error itself. AMBIENT.md gains rule 24. Trellis is an agentic knowledge-work system over a large context of domain-specific user information -- a toolset the user works WITH, not a retrieval system with a fact store bolted on. Two sentences carry it and one act falsifies each: a query produces a deliverable rather than a reply, and the worker queries the corpus rather than transporting it. A schema that can only carry a string cannot carry a deliverable, so a terminal action requiring non-empty prose, an artifact envelope with one hardcoded text part, and an answer sink rendering a single value each make the first false. The collaborator wrote the charter sentence; the falsifiable clauses are what make it a rule rather than a description. Fitting it took compressing this file's least-decisive prose rather than growing past the bound (rule 22): 8070/8192. docs/product/FEATURE_LIST.md is new -- eight capability groups, each row marked shipped, built-unreachable, partial, or absent, written before security hardening on purpose. A hardening pass over an unspecified feature set produces a queue of follow-ups rather than a boundary. What four read-only sweeps established, and what changed my read of it. The substrate is healthy. The successor worker layer already HAS the corpus locate, the bounded window, text-free get_ast_blocks, byte metering both directions and a handle-typed llm_query context -- built, with no non-test caller, while production spawns the legacy path. Grounded authoring is the response artifact done correctly for one case: a run with zero retrieval affordance that writes a multi-file directory. So the diagnosis holds where it matters and is narrower than "rebuild": the doctrine is implemented one layer down and one layer over from where the model actually lives. The hole is the delivery contract, and it is text-only by schema at every layer. A2A already carries a first-class artifacts array with typed parts and renderArtifact hardcodes one text part; finish is invalid without non-empty prose; submit renders one value. The envelope the target needs is already there, empty. Two corrections to records I wrote earlier today. answer.submit(H) is cited in three places as the route for bulk content and DOES NOT EXIST -- submit takes an expression string and renders a value, with no handle argument. An unbuilt escape hatch reads exactly like a built one, which is why the doctrine's wholesale hand-off looked merely capped. And the layering block in config.py still asked what corpus size address space permits, locating the corpus inside the namespace, which is the one place the handle model says it never is. A defect worth its own line, found while assessing the turn budget: when a run exhausts its iterations, rlms re-prompts the model over the whole transcript and returns that as FINAL_ANSWER, bypassing the answer channel entirely -- no literal check, no cap, no telemetry, no protocol violation. More turns spent composing means more chance of landing on the guard-free path, so the iteration budget cannot be widened before that is closed. FEATURE_LIST 2.7. Verified: every quotation re-read from the tree it cites; pytest 1042; vitest 1416/119; check:repo-surface PASS (0 issues, AMBIENT.md 8070/8192); wiki:check --check-html PASS. --- AMBIENT.md | 58 +++++- docs/architecture/RESPONSE_ARTIFACT.md | 2 +- docs/product/FEATURE_LIST.md | 165 ++++++++++++++++++ .../repl-sandbox/REPL_SANDBOX_CONFORMANCE.md | 6 +- .../repl-sandbox/REPL_SANDBOX_DATA_MODEL.md | 6 +- src/repl_sandbox/config.py | 21 ++- 6 files changed, 238 insertions(+), 20 deletions(-) create mode 100644 docs/product/FEATURE_LIST.md diff --git a/AMBIENT.md b/AMBIENT.md index cbf6061..108967e 100644 --- a/AMBIENT.md +++ b/AMBIENT.md @@ -11,13 +11,57 @@ operating a persistent Python REPL over a knowledge store where every stored fact traces to immutable content-addressed source bytes. Most rules in this repository wait for an event — a file opened, a -command run, bytes written. These five wait for nothing. A session has -an objective, sits under whatever gates the collaborator has set, claims -things are delivered, rests those claims on records, and can ask. That -is the whole trigger. Numbers are append-only and cite exactly as they -did before the restructure. Rule 21 is split: 21(a) is here, and 21(b) — -asking before installing standing configuration — has a detectable -trigger and lives with the task-type files. +command run, bytes written. These six wait for nothing. A session knows +what is being built, has an objective, sits under whatever gates the +collaborator has set, claims things are delivered, rests those claims on +records, and can ask. That is the whole trigger. Numbers are append-only +and cite exactly as they did before the restructure. Rule 21 is split: +21(a) is here, and 21(b) — asking before installing standing +configuration — has a detectable trigger and lives with the task-type +files. + +**Rule 24 is numbered last and printed first, deliberately.** It exists +because what is being built was written only in records a construction +decision never had to open, and a target a session must go looking for +loses to the nearest familiar shape — rule 20's ordering failure, one +level up. + +## Rule 24 — what is being built + +Trellis is an **agentic knowledge-work system** over a large context of +domain-specific user information: a toolset the user works *with*, not a +retrieval system with a fact store bolted on. It holds the information, +reasons over it, acts on it, and forms beliefs and doubts from it. Two +sentences carry the rule, and one act falsifies each. + +**A query produces a deliverable, not a reply.** Every run leaves a +**response artifact** — a derived object composed over several turns and +multiple slices, which outlives the run and which the orchestrator can +parse, summarize, and link for the user. A run whose whole output is a +string in a transcript produced none. **A schema that can only carry a +string cannot carry a deliverable**, so a terminal action requiring +non-empty prose, an artifact envelope with one hardcoded text part, and +an answer sink that renders a single value each make this false. For +code editing the repository is the artifact and the submitted string is +a receipt. + +**The worker queries the corpus; it never transports it.** It answers +questions *about* a body it never holds, reading the slices the question +needs and no more. A surface returning a whole document where the +question asked about part of one, a bound pricing a paragraph and a +corpus alike, and an instruction to collapse several turns into one load +each make this false. + +The tell for both: a sentence computing how much of a corpus fits +through the model. That arithmetic describes an operation this system +does not have — bulk movement is the engine's job, which is rule 5's +code-mediated text applied to what a turn is *for* +(`docs/architecture/RESPONSE_ARTIFACT.md`). + +Two surfaces carry this outward, and neither is decoration. **A2A is +inbound** — peer agents query Trellis as a human would. **MCP is +outbound** — Trellis acts in the world as a human would. A text-only +contract on either bounds the whole system. ## Rule 1 — where the objective comes from diff --git a/docs/architecture/RESPONSE_ARTIFACT.md b/docs/architecture/RESPONSE_ARTIFACT.md index 282b5a1..2e4dd6d 100644 --- a/docs/architecture/RESPONSE_ARTIFACT.md +++ b/docs/architecture/RESPONSE_ARTIFACT.md @@ -49,7 +49,7 @@ The mechanisms ship. This is not an aspiration record. |---|---|---| | `trellis_answer.submit` | Refuses a **bare literal** by `ast.parse` — an expression referencing no REPL state is a retyped literal. Structural, not advisory. | `src/rlm/trellis_answer.py` | | Handles, not payloads | The corpus never enters the guest; the algebra is handle-in / handle-out and crosses no content. | [REPL_SANDBOX_DATA_MODEL.md](../product/repl-sandbox/REPL_SANDBOX_DATA_MODEL.md) §4, §5 | -| By-reference sinks | `llm_query(context=[H])` and `answer.submit(H)` resolve host-side. *"Prefer by-reference sinks; `materialize` is only for when the model itself must compute over the bytes."* | same, §6 — titled **The bounded materialisation exception** | +| By-reference sink (one of two) | `llm_query(context=[H])` resolves host-side; its `context` slot is typed `Handle` so a string does not fit. *"Prefer by-reference sinks; `materialize` is only for when the model itself must compute over the bytes."* **The answer-side twin, `answer.submit(H)`, is documented in two records and does not exist** — see §4. | same, §6 — titled **The bounded materialisation exception** | | `locate` / `lines` | Query for an address; read a bounded slice. *"the model queries for a location, it never counts lines."* | `src/rlm/trellis_textedit.py` | | Workspace index/segment | `read()` returns the index and **never** contents; a segment is a deliberate pull. | `src/rlm/trellis_workspace.py` | | MCP capture | External results are captured mechanically; the model receives a stub with a preview. Harness-guaranteed, not model discipline. | `src/rlm/trellis_mcp.py` | diff --git a/docs/product/FEATURE_LIST.md b/docs/product/FEATURE_LIST.md new file mode 100644 index 0000000..231f171 --- /dev/null +++ b/docs/product/FEATURE_LIST.md @@ -0,0 +1,165 @@ +# Trellis — the feature list + +> *Trellis: it's an expert at working with your data.* + +**Status: PROPOSED, July 24, 2026. A planning artifact, not a design record and not an +authorization.** Written before security hardening deliberately: a hardening pass over an +unspecified feature set produces a queue of follow-ups rather than a boundary, because every +control has to be re-litigated the moment a feature it did not anticipate arrives. + +**Governed by [AMBIENT.md rule 24](../../AMBIENT.md).** Every row below is a consequence of that +rule or a component that serves it. Where a row's status contradicts the rule, the rule wins and the +row is the work. + +**How to read the status column.** `shipped` — built with a non-test caller. `built, unreachable` — +built with no non-test caller, which is not delivered ([rule 15](../../AMBIENT.md)). `partial` — +serves one case of a general capability. `absent` — nothing stands in for it. + +--- + +## 1. Holds the information + +The substrate. This layer is the one the audit found healthiest, and nothing here is on the +critical path. + +| # | feature | what it means | status | +|---|---|---|---| +| 1.1 | Content-addressed store | Every stored fact traces to immutable source bytes; nodes final at write time | shipped | +| 1.2 | Verified ingest | One transaction every document crosses; parsers for markdown, code, PDF | shipped | +| 1.3 | Structural chunking | Syntax-aligned, size-budgeted blocks; byte-exact | shipped | +| 1.4 | Live-blocks-only retrieval | Superseded versions are archive, reachable only by explicit address | shipped | +| 1.5 | Repository snapshot ingest | Whole-repo scoped snapshots, carry-forward for out-of-scope paths | shipped | +| 1.6 | **Multi-tenant identity** | A principal the store can name, so "this user's corpus" is expressible | **absent** — `SessionTable` holds `CID → session id` and nothing else. Prerequisite for 3.5 and for any shared deployment | + +## 2. Reasons over it + +The worker. This is the layer that drifted toward retrieval. + +| # | feature | what it means | status | +|---|---|---|---| +| 2.1 | Persistent REPL per task | One process, one task; namespace survives turns | shipped | +| 2.2 | Flat sub-LLM fan-out | `llm_query` over slices at depth 1 | shipped | +| 2.3 | Code-mediated text | Engine computes locations; bytes move by splice or reference | shipped | +| 2.4 | **Corpus `locate` + bounded window** | Query the corpus for addresses; read a bounded range — what `trellis_textedit` already does for files | **built, unreachable** — `repl_sandbox/algebra.py` has `locate`, `narrow`, text-free `get_ast_blocks`, byte metering, handle-typed `llm_query` context. Nothing outside `src/repl_sandbox/` imports it; production spawns `src/rlm/trellis_agent.py` | +| 2.5 | Byte-metered extraction budget | "No more than needed" needs a unit; today the budget counts calls | **absent** in the live path, shipped in 2.4's layer | +| 2.6 | Turn budget affording composition | Several turns and multiple slices, per rule 24 | **wrong shape** — schema ceiling of 9, and the kernel instructs collapsing turns | +| 2.7 | **Iteration-exhaustion path that keeps its guards** | Running out of turns must not bypass the answer channel | **defect** — `rlms` re-prompts over the transcript and returns that as `FINAL_ANSWER`: no literal check, no cap, no telemetry, no protocol violation. See §6 | +| 2.8 | Durable artifact-under-construction state | A growing, engine-measured buffer the model deposits into across turns | **absent** — `upsum` is a 2,000-char shrinking summary; the workspace has no model-facing deposit path | + +## 3. Produces a deliverable + +**The hole, and the reason this list exists.** No layer of the system can express a deliverable that +is not a string. + +| # | feature | what it means | status | +|---|---|---|---| +| 3.1 | **Response artifact object** | A durable, addressable, Trellis-side object a run composes and the orchestrator links. Not a rendering — the thing itself | **absent** | +| 3.2 | **Artifact sink** | A by-reference write path: the model names parts, the engine assembles. Distinct from `answer.submit`, which renders one value | **absent** — `answer.submit(H)` is documented in two records and was never built | +| 3.3 | **Output location** | A run-scoped place to create files. `TRELLIS_EDIT_ROOT` is an *edit* root and `load` refuses a non-existent file | **absent** | +| 3.4 | **Non-text artifact types** | Spreadsheet, PDF with chart, slide deck, text with illustration | **absent** — no `openpyxl`/`matplotlib`/`reportlab`/`pptx`-class dependency exists | +| 3.5 | **Artifact provenance** | Which slices composed which deliverable, resolvable to source bytes | **absent** — the system's own value proposition, unapplied to its output | +| 3.6 | Artifact receipt | The submitted string names the artifact rather than restating it | partial — `submit` is the right shape for a receipt and is currently doing both jobs | +| 3.7 | Repository as artifact | For code editing, the write is the deliverable | **partial** — `trellis_textedit` + `stage2_selfedit_check.ts` are a working loop; missing file *creation*, a link from run outcome to write, and any byte telling the worker the write was the point | + +## 4. Acts in the world — MCP outbound + +Trellis takes human-like actions: query a service, book a thing, request work. Today this is framed +as a research intake. + +| # | feature | what it means | status | +|---|---|---|---| +| 4.1 | Allowlisted MCP client | stdio + streamable HTTP, per-server tool allowlist, bounded results | shipped | +| 4.2 | Harness-guaranteed capture | Results captured to workspace segments; model sees a stub | shipped | +| 4.3 | **MCP available to the orchestrator** | Rule 24 names MCP as the orchestrator's toolbox | **absent** — no MCP reference anywhere in `src/core/agent/`; it is injected into the RLM worker only | +| 4.4 | **Action semantics** | A tool call that *does* something, with a result that carries standing | **wrong shape** — `EXTERNAL CONTENT CONTRACT (HARD RULE): MCP results are research context ONLY` | +| 4.5 | **Non-text tool results** | Images and embedded resources the protocol already carries | **absent** — flattened to the literal string `[non-text content: ]` at the boundary | +| 4.6 | Action authorization | Which side effects need a human gate, and how that is asked | **absent** — the allowlist is the only control, and it is configuration, not consent | + +## 5. Serves peers — A2A inbound + +Peer agents query Trellis as a human would, without knowing its internals. + +| # | feature | what it means | status | +|---|---|---|---| +| 5.1 | JSON-RPC surface | `SendMessage`, `SendStreamingMessage`, `GetTask`, `CancelTask`; live-wired behind `config.a2a.enabled` | shipped | +| 5.2 | Spec-faithful refusal | Out-of-scope operations declined whole, with the spec's own error codes | shipped | +| 5.3 | Admission bounded ahead of allocation | `StreamGate` counts a stream in before resources exist | shipped | +| 5.4 | **Typed artifact parts** | A2A's `artifacts` array carries `oneof (text \| raw \| url \| data)` | **wrong shape** — `renderArtifact` hardcodes one `text` part from `finalAnswer`; the agent card declares `text/plain` as capability. **The envelope the target needs is already there, empty** | +| 5.5 | Peer identity and trust | Which peer asked, and what that entitles | **absent** — bearer key or open | + +## 6. Forms beliefs and doubts + +The epistemic layer. Built, and gated on decisions rather than on code. + +| # | feature | what it means | status | +|---|---|---|---| +| 6.1 | Provenance-bound beliefs | `sourceNodeIds` enforced by the write path, not by prompt | shipped | +| 6.2 | Invalidation sweep | Beliefs contest when their source bytes die | shipped | +| 6.3 | Support arithmetic | Graded, decaying (b,d,u) computed sweep-side, writer-blind | shipped | +| 6.4 | Composed judge ceremony | Judges composed per context from primitives | design-resolved, unbuilt | +| 6.5 | Doubts / objections / defeaters | The −1 tier; a doubt is *based on* its objection | ratified as principle, no build | +| 6.6 | Signed ternary + user gate | −1/0/+1 standing; the user ratifies; the panel never moves standing | ratified as principle, no build | + +## 7. Builds its own modules — the capability flywheel + +| # | feature | what it means | status | +|---|---|---|---| +| 7.1 | Grounded authoring | A run with **zero retrieval affordance** derives a protocol from a seeded corpus and writes `modules//{module.json,addendum.txt,RESEARCH.md}` | **shipped** — and it is the response artifact, done, for one case | +| 7.2 | Derivation gate | Assembly refuses below a derivation threshold | shipped | +| 7.3 | Module registry + byte-pinned composition | Manifest, acceptance criterion naming its own module, composed-prompt pins | shipped | +| 7.4 | Self-editing | Trellis may edit Trellis; the repo is the artifact | partial — see 3.7 | + +## 8. Knows the user + +Rule 24's "one mega-context" includes the user. This is the thinnest column in the list. + +| # | feature | what it means | status | +|---|---|---|---| +| 8.1 | **Stored user preferences** | Preferences live in the substrate and are pulled each run, not configured per deployment | **absent** — no preference, profile, or persona retrieval anywhere in `src/core/agent/` | +| 8.2 | **Orchestrator rules from the REPL** | The orchestrator's behaviour is data-driven from the user's own store | **absent** — `ORCHESTRATOR_SYSTEM_PROMPT` is a static string | +| 8.3 | User gate on ratification | The user, not a panel, moves standing | ratified as principle, no build | + +--- + +## What blocks what + +``` +rule 24 (landed) + │ + ├─ 3.1 artifact object ──┬─ 3.2 sink ─┬─ 3.3 location ─ 3.4 non-text types + │ │ └─ 3.5 provenance ← needs 1.6 principal + │ └─ 5.4 A2A typed parts (rendering, not home) + │ + ├─ 2.7 exhaustion defect ─── 2.6 turn budget (2.6 is unsafe to widen before 2.7) + │ + ├─ 2.4 reachability ─── 2.5 byte budget (mostly migration, not construction) + │ + └─ 4.3 orchestrator MCP ─── 4.4 action semantics ─── 4.6 authorization +``` + +**Three orderings I hold with some confidence.** The artifact object (3.1) precedes its renderings — +A2A parts and the UI are two views of one Trellis-side thing, not two places it lives. The +exhaustion defect (2.7) precedes widening the turn budget (2.6), because more turns currently means +more chance of landing on the guard-free path. And artifact provenance (3.5) needs a principal (1.6) +before "these are the requester's own bytes" is a sentence the system can form. + +**What this list deliberately does not do:** sequence the work, estimate it, or authorize any of it. +It exists so a security pass has a fixed surface to harden against. + +## Open questions for the owner + +1. **Is the artifact a Trellis-side object with A2A as one rendering?** This list assumes yes (3.1 + before 5.4). The alternative — the artifact *is* the A2A envelope — would make peer agents the + privileged consumer and the human UI the adapter, which seems backwards but is not absurd. +2. **Does an artifact need a principal before it can exist**, or can a single-tenant artifact ship + first and gain ownership later? 1.6 is a large prerequisite to put in front of 3.1. +3. **How much of §6 is in scope for the first artifact-capable release?** Beliefs and doubts are + ratified as principle with no build, and a deliverable that carries standing is a different object + from one that carries content. +4. **Does `TEST_TIME_TRAINING` §12.2 stand?** It holds that large REPL dumps are the expected + workload, which rule 24's second sentence now contradicts. Dated owner correction; only the owner + retires it. + +*Siblings: [AMBIENT.md rule 24](../../AMBIENT.md) (what is being built) · +[RESPONSE_ARTIFACT.md](../architecture/RESPONSE_ARTIFACT.md) (the doctrine and the audit) · +[CODE_MEDIATED_TEXT.md](../architecture/CODE_MEDIATED_TEXT.md) (how bytes move).* diff --git a/docs/product/repl-sandbox/REPL_SANDBOX_CONFORMANCE.md b/docs/product/repl-sandbox/REPL_SANDBOX_CONFORMANCE.md index fa1c1ca..2adc34c 100644 --- a/docs/product/repl-sandbox/REPL_SANDBOX_CONFORMANCE.md +++ b/docs/product/repl-sandbox/REPL_SANDBOX_CONFORMANCE.md @@ -169,8 +169,10 @@ answers questions *about* gigabyte contexts by reading slices and composing a de artifact** over several turns, extracting no more than it needs. `materialize` is the exception path, which [DATA_MODEL §6](REPL_SANDBOX_DATA_MODEL.md) — titled *The bounded materialisation exception* — already states: *prefer by-reference sinks; `materialize` is only for when the model itself must compute over the bytes.* When bulk content must -reach an answer it goes `answer.submit(H)` — resolved host-side, out through the audited egress, -never through the guest namespace and so never through this bound at all. +reach an answer it was meant to go `answer.submit(H)` — **an op that does not exist.** +`trellis_answer.submit` takes an expression string and renders a *value*; there is no handle argument +and no host-side resolution. Recorded here because this record is one of the two that route bulk +through it (RESPONSE_ARTIFACT.md section 4). So `max_result_bytes` is sized against **one computation's working set**, and the arithmetic "a corpus takes N calls at this size" is a tell that the model is being treated as the transport. It diff --git a/docs/product/repl-sandbox/REPL_SANDBOX_DATA_MODEL.md b/docs/product/repl-sandbox/REPL_SANDBOX_DATA_MODEL.md index 9206b7d..3308e8e 100644 --- a/docs/product/repl-sandbox/REPL_SANDBOX_DATA_MODEL.md +++ b/docs/product/repl-sandbox/REPL_SANDBOX_DATA_MODEL.md @@ -177,7 +177,11 @@ residual the boundary deliberately leaves; its caps are defense-in-depth, **neve them host-side, so the model can direct host-resident data to a sink **without ever holding it**: `llm_query(prompt="summarise", context=beliefs)` resolves the belief handle host-side, the sub-LLM summarises, and only the bounded summary returns (inbound-metered) — the model reasoned over the belief -base without a row of it entering the guest. Likewise `answer.submit(H)`: the referent leaves via the +base without a row of it entering the guest. Likewise `answer.submit(H)` — **DESIGNED, NOT BUILT +(noted 2026-07-24): `trellis_answer.submit` takes an expression string and renders a value; there is +no handle argument and no host-side resolution. The by-reference answer sink is the doctrine's +wholesale hand-off and it does not exist** ([FEATURE_LIST.md](../FEATURE_LIST.md) 3.2) — under which +the referent leaves via the audited answer egress, never through the guest namespace. **Prefer by-reference sinks; `materialize` is only for when the model itself must compute over the bytes.** diff --git a/src/repl_sandbox/config.py b/src/repl_sandbox/config.py index 7ac0c14..a1a3546 100644 --- a/src/repl_sandbox/config.py +++ b/src/repl_sandbox/config.py @@ -52,15 +52,16 @@ def version_at_least(observed: str, minimum: str) -> bool: # Four different things get bounded in this system and they are easy to collapse # into one number. Keeping them apart is the point of this block: # -# REPL namespace the corpus the model works over `Tier0Limits.address_space_bytes` -# one slice host store -> guest namespace `BrokerCaps.max_result_bytes` +# the corpus what the model reasons over *nothing here* — host-side, behind handles +# the working set what it holds in the namespace `Tier0Limits.address_space_bytes` +# one materialisation host store -> guest namespace `BrokerCaps.max_result_bytes` # model attention what reaches the transcript `MarshalCaps` (this file) # one wire message a single frame `DEFAULT_MAX_FRAME_LEN` # -# **The REPL is meant to be large — potentially gigabytes — and read in slices.** -# None of the bounds below constrain that; the namespace ceiling is the rlimit. -# A 12 MiB value in the namespace returns a ~4 KB exec reply, because the -# marshalling caps describe it rather than carrying it. +# **The corpus is bounded by nothing in this file, because it never enters the +# guest.** Address space bounds the working set — what the model materialised and +# is computing over — and nothing else. A 12 MiB value in the namespace returns a +# ~4 KB exec reply, because the marshalling caps describe it rather than carry it. #: The context window a slice is sized against, in tokens. Recorded so the #: derivation below can be re-run rather than re-guessed. @@ -93,9 +94,11 @@ def version_at_least(observed: str, minimum: str) -> bool: #: `materialize` is the exception path — DATA_MODEL section 6, whose title is #: *The bounded materialisation exception*: *prefer by-reference sinks; #: `materialize` is only for when the model itself must compute over the bytes.* -#: A gigabyte reaching an answer goes `answer.submit(H)`, -#: resolved host-side and leaving by the audited egress, never through this -#: constant and never through the guest namespace. +#: A gigabyte reaching an answer was meant to go `answer.submit(H)`, resolved +#: host-side and leaving by the audited egress — **an op that does not exist.** +#: `trellis_answer.submit` takes an expression string and renders a value; there +#: is no handle argument. Recorded because two records route bulk through it, and +#: an unbuilt escape hatch reads exactly like a built one. #: #: So the number to size against is **one computation's working set**, not a #: corpus divided by anything. Arithmetic of the form "a corpus takes N calls at From 9ba8d4b4200eadc1f1b207f75a4473d0627987f8 Mon Sep 17 00:00:00 2001 From: Darian Ngo Date: Fri, 24 Jul 2026 17:00:00 -0500 Subject: [PATCH 3/7] The density-trellis tracks rule 24 and the two corrections C12 and C13 went stale against ab126f6, so their sections move with the code they describe rather than being stamped later. C13 takes rule 24, because a rule stating what is being built is a self-description that construction has to be able to see -- which is the class's whole subject. It records the placement argument (numbered last, printed first; rule 20's ordering failure raised from measurement to construction), the byte discipline that fitting it obeyed (least-decisive prose compressed rather than appending past the bound, 8,070/8,192), and FEATURE_LIST.md as the consequence. C12 takes both corrections. The corpus is bounded by nothing in the config because it never enters the guest -- RLIMIT_AS bounds the working set, and the earlier layering table asked what corpus size address space permits, putting the corpus in the one place the handle model says it never is. And the sharper one: answer.submit(H), the by-reference sink two records route bulk through, does not exist, so the wholesale hand-off is unbuilt rather than capped. Also recorded that charge_outbound fires only for the llm_query prompt, so the answer is not on the exfil ledger, and THREAT_MODEL already calls the 64 KiB cap output-shaping rather than a confidentiality control -- the collapse is DATA_MODEL section 6 alone. Verified: wiki:check clears C12 and C13, leaving C11 (from #189, not this branch) and the seven fff40f4 left git-stale on purpose; --check-html PASS; check:repo-surface PASS (0 issues). --- docs/density-chain/DENSITY-CHAIN.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/density-chain/DENSITY-CHAIN.md b/docs/density-chain/DENSITY-CHAIN.md index 11c1f39..d504591 100644 --- a/docs/density-chain/DENSITY-CHAIN.md +++ b/docs/density-chain/DENSITY-CHAIN.md @@ -865,8 +865,17 @@ the RLM execution model, the doubts machinery it borrows, or the pillar it reali protects nothing (a 12 MiB namespace value returns a ~4 KB reply — `MarshalCaps` holds attention, independently) while leaving the frame **below** `max_result_bytes`, so a legal broker result could not cross the wire. The token rule now sizes the **slice**, as a sizing convention and never an - enforcing bound. **The REPL is meant to be gigabytes read in slices; the corpus ceiling is - `RLIMIT_AS`, not any wire number.** Still open: handle lifetime, the warm pool, depth-2. The + enforcing bound. **The corpus is bounded by nothing in the config, because it never enters the + guest** — `RLIMIT_AS` bounds the *working set*, and an earlier edition of that same layering table + asked what corpus size address space permits, which puts the corpus in the one place the handle + model says it never is. **A second correction the same day, and the sharper one: `answer.submit(H)` + — the by-reference answer sink two records route bulk content through — DOES NOT EXIST.** `submit` + takes an expression string and renders a value; there is no handle argument and no host-side + resolution. So the doctrine's wholesale hand-off is not capped, it is **unbuilt**, and an unbuilt + escape hatch reads exactly like a built one. Also established: `charge_outbound` is called only for + the `llm_query` prompt, so the answer is **not on the exfil ledger at all**, and THREAT_MODEL + already calls the 64 KiB cap output-shaping *"not a confidentiality or escape control"* — the + collapse lives in DATA_MODEL §6 alone. Still open: handle lifetime, the warm pool, depth-2. The remaining **[A]** halves (S6, GB, GA-eq) are ≤$5 and **unspent**; S3's and S4's are **banked**. *Status ledger:* **control plane shipped; a microVM boots, a frame crosses to it, a real database @@ -964,6 +973,17 @@ descriptor program. Not the guards it describes, only the accounts of them.* now BUILT. What remains unbuilt: `llm_help` itself, the eight further descriptors, the human-doc generator, and the advisory-marking convention that belongs with `llm_help`'s frame. +**AMBIENT.md gained rule 24 on 2026-07-24 — what is being built — and it is this class's business +because it is a self-description that construction has to be able to see.** The rule is numbered +last and printed first: the target existed only in records a construction decision never had to +open, and the build converged on the nearest familiar shape. That is rule 20's ordering failure +raised from measurement to construction, and the placement is the correction. Fitting it obeyed the +byte discipline rather than the ceiling — the file's least-decisive prose was compressed instead of +appending past the bound (**8,070 / 8,192**, 122 free). `docs/product/FEATURE_LIST.md` is the +consequence: eight capability groups, every row marked shipped, built-unreachable, partial or +absent, written **before** security hardening because a hardening pass over an unspecified feature +set yields follow-ups rather than a boundary. + *Status ledger:* root contract · machine twin · surface checker · its governed-headroom report · CI wiring — **shipped-pinned**; self-describing surfaces — **RATIFIED** (2026-07-23); harness self-model — **principle endorsed, From 23382719135a14c4625a0f975f1b39f084f705f3 Mon Sep 17 00:00:00 2001 From: Darian Ngo Date: Fri, 24 Jul 2026 18:50:47 -0500 Subject: [PATCH 4/7] Withdraw the TTT finding, answer the feature list, and name a conflation Collaborator answers, July 24 2026, and one thing I got wrong twice. TEST_TIME_TRAINING section 12.2 is WITHDRAWN from the audit. I read it as doctrine holding that large REPL dumps are the intended workload. Section 12 is a literature-applicability analysis; 12.2 argues that LaCT's long-context results DO transfer to Trellis. That is a claim about whether a research finding is relevant, not about desired behaviour, and reading the second as the first is the whole error. TTT is not a counterweight to rule 24 -- it is a mechanism FOR it: the harness composes a prompt from internal primitives, the composed prompt sets the mode, the model self-plays over REPL data, and properly filtered programmatic slicing is the REWARDED behaviour, scored by RLVCG (arXiv:2607.19044). It targets a local open-weights model that does not exist yet; TRELLIS_RLM_BACKEND is root-agent only and worker transport is explicitly not configurable, which is why it reads forward-looking. Nothing needed adjudicating. Question 1 answered: the artifact is a Trellis-side object and A2A is one rendering. 3.1 precedes 5.4. Question 2 answered better than it was asked. An artifact CARRIES STANDING -- filed as a fact, a belief, or a doubt depending on purpose and what it carries. So sections 3 and 6 are one subject, and artifact provenance rides the machinery that already exists. DATA_MODEL section 7 already designs the shape: doubts, beliefs and facts as three pre-allocated root handles at setup, kind = graph-view, sliced by the algebra and never materialised whole. My original question meant multi-tenant owner identity, which stays open as 1.6 and is smaller than it looked, since ownership is a property of a filed artifact rather than a prerequisite for having one. Question 3, and the finding worth keeping: THE GATE IS ON A REMOVAL AND THE WANT IS AN ADDITION. STANDING_MODEL.md was ratified July 20 2026 with a status line authorizing no build, and its section 3 says what the withheld authorization covers -- if the panel never moves standing, the promotion machinery reduces to a findings recorder plus a user gate, and "that reduction removes shipped engine surface", so deleting or rewriting shipped disposition code needs its own owner dated entry and drills. Surfacing doubts/beliefs/facts as REPL state spaces is not that. It is an addition, it is already designed, and no gate covers it. It was never scheduled, not never approved. Deferred capabilities are now marked PLACEHOLDER rather than absent, per the collaborator: rough in the seam so the shape exists, land the capability when the model connection does. 3.4 non-text artifact types and 4.5 non-text tool results are the two. Verified: pytest 1042; check:repo-surface PASS (0 issues). --- docs/architecture/RESPONSE_ARTIFACT.md | 27 +++++++++---- docs/product/FEATURE_LIST.md | 54 +++++++++++++++++++------- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/docs/architecture/RESPONSE_ARTIFACT.md b/docs/architecture/RESPONSE_ARTIFACT.md index 2e4dd6d..6203da4 100644 --- a/docs/architecture/RESPONSE_ARTIFACT.md +++ b/docs/architecture/RESPONSE_ARTIFACT.md @@ -106,12 +106,23 @@ this record makes. ### 3.3 Records that state the opposite -- **[TEST_TIME_TRAINING.md](TEST_TIME_TRAINING.md) §12.2** holds that *"large REPL dumps ARE - long-context modeling in practice"*, re-reading the pillar as a rule against *retyping* only while - explicitly conceding reading, and concluding the effective-context thesis *"survives only in narrow - form."* It is a dated owner correction from July 13, 2026, and the July 24 doctrine post-dates it. - **This needs re-adjudication by the owner, not a silent edit** — it is load-bearing for that - record's argument that long-context results apply to Trellis directly. +- **[TEST_TIME_TRAINING.md](TEST_TIME_TRAINING.md) §12.2 — LISTED HERE IN ERROR, withdrawn + 2026-07-24.** It was read as doctrine holding that large REPL dumps are the intended workload. It + is not: §12 is a **literature-applicability** analysis, and §12.2 asks whether LaCT's long-context + results transfer to Trellis. The first draft argued they do *not*, because the RLM keeps the corpus + out of attention; the owner's correction overturned that — per-run token flow is genuine + long-context load, **so those results apply here directly**. That is an argument about whether a + research finding is relevant, not a statement of desired behaviour, and reading the second as the + first is the error. + + **TTT is a mechanism for rule 24's second sentence, not a counterweight to it.** It targets an + open-weights model served locally (§7 R3), which does not exist yet — `TRELLIS_RLM_BACKEND` is + root-agent only and worker transport is explicitly not configurable. Its shape, from the + collaborator: the harness composes a prompt from internal primitives, the composed prompt **sets + the mode**, and the model self-plays over REPL data with **properly filtered programmatic slicing + as the rewarded behaviour**, scored by RLVCG (arXiv:2607.19044). Slice discipline is what TTT pays + for. Nothing here needs re-adjudication. + - **[REASONING_TEMPLATES.md](REASONING_TEMPLATES.md) §17** gives all eight reasoning modes the same out-port, `answer`, and the locked port vocabulary offers no artifact type. `construction` — *"Build or modify a file artifact in the edit root"* — routes `write_back` as a mid-flow node and terminates @@ -150,8 +161,8 @@ different ledger from the exfil residual, with the provenance of its parts intac - **No engine change.** Every item in §3 is a finding. The budget's metering, the missing corpus `locate`, `fetch_texts`, the iteration budget, and the module selection are each a separate change with its own gate. -- **No re-adjudication of TEST_TIME_TRAINING §12.2.** That is a dated owner correction and only the - owner retires it. +- **Nothing about TEST_TIME_TRAINING.** §12.2 was listed in §3.3 in error and is withdrawn there; + it needed no adjudication, because it never said what it was read as saying. **Reachability, stated rather than implied:** this record has no enforcing surface. Nothing refuses a wholesale print, no guard couples a decisive step to a bounded read, and the term "response artifact" diff --git a/docs/product/FEATURE_LIST.md b/docs/product/FEATURE_LIST.md index 231f171..c6bd3c7 100644 --- a/docs/product/FEATURE_LIST.md +++ b/docs/product/FEATURE_LIST.md @@ -13,7 +13,7 @@ row is the work. **How to read the status column.** `shipped` — built with a non-test caller. `built, unreachable` — built with no non-test caller, which is not delivered ([rule 15](../../AMBIENT.md)). `partial` — -serves one case of a general capability. `absent` — nothing stands in for it. +serves one case of a general capability. `absent` — nothing stands in for it. **`PLACEHOLDER`** — deferred by the owner and the collaborator pending a capability Trellis does not have (typically a model connection); the seam is roughed in so the shape exists, and the capability lands later. --- @@ -56,7 +56,7 @@ is not a string. | 3.1 | **Response artifact object** | A durable, addressable, Trellis-side object a run composes and the orchestrator links. Not a rendering — the thing itself | **absent** | | 3.2 | **Artifact sink** | A by-reference write path: the model names parts, the engine assembles. Distinct from `answer.submit`, which renders one value | **absent** — `answer.submit(H)` is documented in two records and was never built | | 3.3 | **Output location** | A run-scoped place to create files. `TRELLIS_EDIT_ROOT` is an *edit* root and `load` refuses a non-existent file | **absent** | -| 3.4 | **Non-text artifact types** | Spreadsheet, PDF with chart, slide deck, text with illustration | **absent** — no `openpyxl`/`matplotlib`/`reportlab`/`pptx`-class dependency exists | +| 3.4 | **Non-text artifact types** | Spreadsheet, PDF with chart, slide deck, text with illustration | **absent, PLACEHOLDER** — no `openpyxl`/`matplotlib`/`reportlab`/`pptx`-class dependency exists. Rough in the seam; some types need model connections that do not exist yet | | 3.5 | **Artifact provenance** | Which slices composed which deliverable, resolvable to source bytes | **absent** — the system's own value proposition, unapplied to its output | | 3.6 | Artifact receipt | The submitted string names the artifact rather than restating it | partial — `submit` is the right shape for a receipt and is currently doing both jobs | | 3.7 | Repository as artifact | For code editing, the write is the deliverable | **partial** — `trellis_textedit` + `stage2_selfedit_check.ts` are a working loop; missing file *creation*, a link from run outcome to write, and any byte telling the worker the write was the point | @@ -72,7 +72,7 @@ as a research intake. | 4.2 | Harness-guaranteed capture | Results captured to workspace segments; model sees a stub | shipped | | 4.3 | **MCP available to the orchestrator** | Rule 24 names MCP as the orchestrator's toolbox | **absent** — no MCP reference anywhere in `src/core/agent/`; it is injected into the RLM worker only | | 4.4 | **Action semantics** | A tool call that *does* something, with a result that carries standing | **wrong shape** — `EXTERNAL CONTENT CONTRACT (HARD RULE): MCP results are research context ONLY` | -| 4.5 | **Non-text tool results** | Images and embedded resources the protocol already carries | **absent** — flattened to the literal string `[non-text content: ]` at the boundary | +| 4.5 | **Non-text tool results** | Images and embedded resources the protocol already carries | **absent, PLACEHOLDER** — flattened to the literal string `[non-text content: ]` at the boundary. An image tool needs a model connection Trellis does not have; rough in the seam and defer the capability | | 4.6 | Action authorization | Which side effects need a human gate, and how that is asked | **absent** — the allowlist is the only control, and it is configuration, not consent | ## 5. Serves peers — A2A inbound @@ -89,7 +89,19 @@ Peer agents query Trellis as a human would, without knowing its internals. ## 6. Forms beliefs and doubts -The epistemic layer. Built, and gated on decisions rather than on code. +The epistemic layer. **The gate everyone has been respecting is on a *removal*, and the thing that is +wanted is an *addition* — those were conflated, which is why this has sat.** + +`STANDING_MODEL.md` was ratified July 20, 2026 and its status line says it "authorizes **no build**." +Its §3 says exactly what the withheld authorization covers: if the panel never moves standing, the +promotion machinery reduces to a findings recorder plus a user gate, and *"**that reduction removes +shipped engine surface**"* — so deleting or rewriting shipped disposition code needs its own owner +dated entry and drills. **The gate is on deleting code.** + +Surfacing `doubts` / `beliefs` / `facts` as REPL state spaces is not that. It is an addition, it is +already designed — [DATA_MODEL §7](repl-sandbox/REPL_SANDBOX_DATA_MODEL.md) specifies three +**pre-allocated root handles** at `setup`, `kind = graph-view`, sliced by the algebra and never +materialised whole — and **no gate covers it.** It was never scheduled, not never approved. | # | feature | what it means | status | |---|---|---|---| @@ -148,17 +160,29 @@ It exists so a security pass has a fixed surface to harden against. ## Open questions for the owner -1. **Is the artifact a Trellis-side object with A2A as one rendering?** This list assumes yes (3.1 - before 5.4). The alternative — the artifact *is* the A2A envelope — would make peer agents the - privileged consumer and the human UI the adapter, which seems backwards but is not absurd. -2. **Does an artifact need a principal before it can exist**, or can a single-tenant artifact ship - first and gain ownership later? 1.6 is a large prerequisite to put in front of 3.1. -3. **How much of §6 is in scope for the first artifact-capable release?** Beliefs and doubts are - ratified as principle with no build, and a deliverable that carries standing is a different object - from one that carries content. -4. **Does `TEST_TIME_TRAINING` §12.2 stand?** It holds that large REPL dumps are the expected - workload, which rule 24's second sentence now contradicts. Dated owner correction; only the owner - retires it. +**Answered by the collaborator, July 24, 2026.** + +1. **The artifact is a Trellis-side object; A2A is one rendering.** Confirmed. 3.1 precedes 5.4, and + the human UI and a peer agent are two views of one thing. +2. **An artifact carries standing.** It is filed as a **fact, a belief, or a doubt**, depending on + its purpose and what it carries — so §3 and §6 are one subject, not two, and 3.5 rides the + provenance machinery that already exists rather than needing a new structure. + [DATA_MODEL §7](repl-sandbox/REPL_SANDBOX_DATA_MODEL.md) already designs the shape: `doubts`, + `beliefs` and `facts` as three **pre-allocated root handles** at `setup`, `kind = graph-view`, + sliced by the algebra and never materialised whole. *(My original question meant multi-tenant + owner identity — still open as 1.6, and a smaller question than it looked, since ownership is now + a property of a filed artifact rather than a prerequisite for having one.)* +3. **`TEST_TIME_TRAINING` §12.2 needed no adjudication.** It was misread. §12 is a + literature-applicability analysis; §12.2 argues that LaCT's long-context results *do* transfer, + which is a claim about research relevance and not about desired behaviour. **TTT is the mechanism + for rule 24's second sentence** — the harness composes a prompt from internal primitives, the + composed prompt sets the mode, the model self-plays over REPL data, and **properly filtered + programmatic slicing is the rewarded behaviour**, scored by RLVCG (arXiv:2607.19044). It targets a + local open-weights model that does not exist yet (§7 R3; `TRELLIS_RLM_BACKEND` is root-agent only, + worker transport not configurable), which is why it reads as forward-looking rather than current. + +**Still open for the owner:** 1.6, whether a single-tenant artifact ships before multi-tenant +identity exists. *Siblings: [AMBIENT.md rule 24](../../AMBIENT.md) (what is being built) · [RESPONSE_ARTIFACT.md](../architecture/RESPONSE_ARTIFACT.md) (the doctrine and the audit) · From 75f211d51f9b0227d34a8afa6307801ca7209a9b Mon Sep 17 00:00:00 2001 From: Darian Ngo Date: Fri, 24 Jul 2026 19:05:50 -0500 Subject: [PATCH 5/7] The disproving arm becomes standing practice for scope-changing findings Owner-approved July 24 2026. When a fan-out's finding would change what kind of work follows -- rebuild versus repair, absent versus unreachable, defect versus default -- one additional arm is spawned whose only task is to disprove the reading the others are converging on, and whose ground block says so. Narrow on purpose. Routine sweeps get no skeptic; the recurring charge is one agent on scope-changing findings only, which is what makes it affordable enough to actually hold. The reason it is a separate arm rather than an instruction to the others: siblings primed to find a problem find it, each one honestly, and their agreement is then the shared prior restated rather than corroboration. Nothing inside the fan-out can catch that, because no sibling is looking. Provenance recorded with it, because the practice is worth less without the case that produced it: three sweeps primed on "the build drifted toward retrieval" found drift correctly everywhere they looked, and a fourth primed to bound the claim found the capability the other three reported missing -- built, and merely unreachable. Without that arm the session would have carried a rebuild estimate for work already done. Verified: check:repo-surface PASS (0 issues). --- .claude/skills/subagent-composition/SKILL.md | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/.claude/skills/subagent-composition/SKILL.md b/.claude/skills/subagent-composition/SKILL.md index 745df14..4061475 100644 --- a/.claude/skills/subagent-composition/SKILL.md +++ b/.claude/skills/subagent-composition/SKILL.md @@ -169,6 +169,45 @@ What each slot absorbs — the variable names carry the rule; this table annotat - **`--agent {name}` silently ignores `skills:`.** Session-agent mode and subagent spawn are different paths; only the spawn path preloads. Validate agent behavior through an actual spawn, never through `--agent`, or a working definition reads as broken. - **`tools:` is enforced, not advisory.** A probe declaring `tools: WebSearch` reported exactly that one tool — the allowlist replaces inheritance rather than trimming it. +## The disproving arm — required when a finding would change scope + +**Trigger, and it is narrow: the fan-out's finding would change what kind of +work follows** — rebuild versus repair, absent versus unreachable, defect versus +default. Then one additional arm is spawned whose **only** task is to disprove +the reading the others are converging on, and whose ground block says so in +those words. Routine sweeps do not get one; the charge is one agent on +scope-changing findings, not a standing skeptic on every dispatch. + +**Why it is a separate arm rather than an instruction to the others.** Siblings +primed to find a problem find it, and each one honestly. Their returns then +agree, and agreement among arms that share a prior is not corroboration — it is +the prior, restated N times. Nothing in the fan-out can catch that from inside, +because no sibling is looking. + +**Composing it.** Same ground block as its siblings, and then the inversion +stated plainly: name what the others are assembling evidence *for*, say that +nobody else is assigned to look for the counter-evidence, and say that if it is +not found here it will not be found. Give it the strongest form of the claim to +attack, not a softened one. Require an address and a quotation per point, so a +charitable reading cannot pass as a finding — and require it to report +"the diagnosis holds" plainly when that is the honest result, since an arm that +can only come back one way is decoration. + +```md +{Three_Sibling_Agents_Are_Gathering_Evidence_That_X}. Your job is the opposite: +find where that reading is overstated, and report the strongest honest case for +{Not_X}. You do not manufacture a defence — if the diagnosis holds everywhere +you looked, say so — but nobody else is assigned to look for the +counter-evidence, so if you do not find it, it will not be found. +``` + +**Provenance.** Trellis, July 24 2026. Three sweeps primed on "the build drifted +toward retrieval" found drift, correctly and everywhere they looked. A fourth, +primed to bound the claim, found the capability the other three had reported +missing — built, and merely unreachable. Without it the session would have +carried a rebuild estimate for work already done. Owner-approved as standing +practice the same day. + ## Fan-out discipline Compose one **shared ground block** and reuse it verbatim across siblings; drift between copies produces findings that cannot be reconciled. Give each sibling a **disjoint write scope**, or give them all `isolation: worktree` (`Agent_Isolation_Worktree_Mode`). Siblings cannot see each other, so any cross-cutting judgment belongs to you after they return — never to a sibling. Prefer running the last agent synchronously (`Agent_Foreground_Synchronous_Wait`, `run_in_background: false`) when you need its result to proceed. From ab11f8d3a3e50dd460a6df9d604ec29ae90fc220 Mon Sep 17 00:00:00 2001 From: Darian Ngo Date: Fri, 24 Jul 2026 19:15:53 -0500 Subject: [PATCH 6/7] Identity is the deployment boundary, and the artifact goes back into the store Owner rulings, July 24 2026, closing both open questions in the feature list. Identity: one user, one instance. Ownership is the deployment boundary and is never represented in the store -- which is why nothing in the substrate names an owner and nothing needs to. Enterprise expands by cloning a base image of the raw data, each clone owned by its user. 1.6 is closed as not applicable rather than deferred. Sequencing: artifacts now, with no recipient slot. I had proposed carrying that slot as cheap insurance; under one-user-one-instance it earns nothing, because a hedge against ambiguous recipients is only worth its column when more than one recipient is possible. The migration story is image cloning, which carries artifacts along with everything else and needs no backfill against append-only rows. The collaborator's clarification is the one that changes the shape of the list. The artifact is NOT a terminal output. A run composes it, it is filed into the user's own REPL store, and the judges promote and classify it -- so it lands as a fact, a belief or a doubt, carries standing like anything else the user owns, and becomes part of the corpus the next query slices. The output becomes input. Two consequences recorded. Sections 3 and 6 are one pipeline rather than two subjects: a deliverable that carries standing is not a different object from one that carries content, it is the same object after the judges have seen it. And 3.5 needs no new provenance structure, because an artifact filed into the store inherits the machinery every other stored thing already crosses. Nothing in the list is now blocked on an owner decision. What remains is build authorization, which is a different gate. Verified: check:repo-surface PASS (0 issues). --- docs/product/FEATURE_LIST.md | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/docs/product/FEATURE_LIST.md b/docs/product/FEATURE_LIST.md index c6bd3c7..6af07a2 100644 --- a/docs/product/FEATURE_LIST.md +++ b/docs/product/FEATURE_LIST.md @@ -29,7 +29,7 @@ critical path. | 1.3 | Structural chunking | Syntax-aligned, size-budgeted blocks; byte-exact | shipped | | 1.4 | Live-blocks-only retrieval | Superseded versions are archive, reachable only by explicit address | shipped | | 1.5 | Repository snapshot ingest | Whole-repo scoped snapshots, carry-forward for out-of-scope paths | shipped | -| 1.6 | **Multi-tenant identity** | A principal the store can name, so "this user's corpus" is expressible | **absent** — `SessionTable` holds `CID → session id` and nothing else. Prerequisite for 3.5 and for any shared deployment | +| 1.6 | Multi-tenant identity | A principal the store can name | **CLOSED, not applicable** (owner, 2026-07-24) — **one user, one instance.** Ownership is the deployment boundary, so it needs no representation. Enterprise scales by cloning a base image of the raw data, each clone owned by its user. Nothing in the substrate names an owner and nothing needs to | ## 2. Reasons over it @@ -51,6 +51,17 @@ The worker. This is the layer that drifted toward retrieval. **The hole, and the reason this list exists.** No layer of the system can express a deliverable that is not a string. +**The artifact is not a terminal output — it is a contribution to the store** (owner and +collaborator, 2026-07-24). A run composes it, it is **filed into the user's own REPL store**, and the +judges **promote and classify** it, so it lands as a fact, a belief, or a doubt and carries standing +like anything else the user owns. It then becomes part of the corpus the next query slices. + +That closes the loop this system is named for: **the output becomes input.** It also means §3 and §6 +are one pipeline rather than two subjects — a deliverable that carries standing is not a different +object from one that carries content, it is the same object after the judges have seen it. And it is +why 3.5 needs no new provenance structure: an artifact filed into the store inherits the machinery +every other stored thing already crosses. + | # | feature | what it means | status | |---|---|---|---| | 3.1 | **Response artifact object** | A durable, addressable, Trellis-side object a run composes and the orchestrator links. Not a rendering — the thing itself | **absent** | @@ -89,8 +100,11 @@ Peer agents query Trellis as a human would, without knowing its internals. ## 6. Forms beliefs and doubts -The epistemic layer. **The gate everyone has been respecting is on a *removal*, and the thing that is -wanted is an *addition* — those were conflated, which is why this has sat.** +The epistemic layer — **and, after the 2026-07-24 rulings, the second half of §3 rather than a +separate concern.** The judges are what turn a composed artifact into a filed one with standing. + +**The gate everyone has been respecting is on a *removal*, and the thing that is wanted is an +*addition* — those were conflated, which is why this has sat.** `STANDING_MODEL.md` was ratified July 20, 2026 and its status line says it "authorizes **no build**." Its §3 says exactly what the withheld authorization covers: if the panel never moves standing, the @@ -181,8 +195,18 @@ It exists so a security pass has a fixed surface to harden against. local open-weights model that does not exist yet (§7 R3; `TRELLIS_RLM_BACKEND` is root-agent only, worker transport not configurable), which is why it reads as forward-looking rather than current. -**Still open for the owner:** 1.6, whether a single-tenant artifact ships before multi-tenant -identity exists. +**Ruled by the owner, 2026-07-24 — both open questions closed.** + +- **Identity: one user, one instance.** Ownership is the deployment boundary and is never + represented in the store. Enterprise expands by cloning a base image of the raw data, each clone + owned by its user; symlinking the shared base is the variant. 1.6 is closed as not applicable. +- **Sequencing: artifacts now.** They ship into the single-tenant store immediately, with no + recipient slot — a hedge that only earns its place when more than one recipient is possible, which + under one-user-one-instance it never is. The migration story is image cloning, which carries + artifacts with everything else and needs no backfill against append-only rows. + +**Nothing in this list is now blocked on an owner decision.** What remains open is build +authorization, which is a different gate. *Siblings: [AMBIENT.md rule 24](../../AMBIENT.md) (what is being built) · [RESPONSE_ARTIFACT.md](../architecture/RESPONSE_ARTIFACT.md) (the doctrine and the audit) · From 02d09ce4123d13a5cde2d2d1b2626e79dba9ea8e Mon Sep 17 00:00:00 2001 From: Darian Ngo Date: Fri, 24 Jul 2026 19:23:08 -0500 Subject: [PATCH 7/7] The feature list is authorized for build, and says what that does not waive Owner authorization, July 24 2026, for execution in a future session. The ordering the list was written to defend is the ordering the authorization follows: feature list, then build, then harden. What it covers and what it does not is stated with it, because a list of rows reads like a list of permissions. It authorizes building against this list. It does not waive the gates that ride on KINDS of work -- prompt bytes still run under rule 16, paid runs under rule 7, standing configuration under rule 21(b), and any change that removes shipped engine surface still needs its own dated entry. That last one is not hypothetical: section 6 exists in its current shape because a gate on a removal was read as a gate on the whole epistemic layer, and the addition it was blocking was never gated at all. A row's presence here is permission to build it, never permission to skip the gate its construction crosses. Verified: check:repo-surface PASS (0 issues). --- docs/product/FEATURE_LIST.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/product/FEATURE_LIST.md b/docs/product/FEATURE_LIST.md index 6af07a2..dad969d 100644 --- a/docs/product/FEATURE_LIST.md +++ b/docs/product/FEATURE_LIST.md @@ -2,10 +2,18 @@ > *Trellis: it's an expert at working with your data.* -**Status: PROPOSED, July 24, 2026. A planning artifact, not a design record and not an -authorization.** Written before security hardening deliberately: a hardening pass over an -unspecified feature set produces a queue of follow-ups rather than a boundary, because every -control has to be re-litigated the moment a feature it did not anticipate arrives. +**Status: BUILD AUTHORIZED — owner (Cnid), July 24, 2026, for execution in a future session.** +Written before security hardening deliberately: a hardening pass over an unspecified feature set +produces a queue of follow-ups rather than a boundary, because every control has to be re-litigated +the moment a feature it did not anticipate arrives. That ordering is why this list exists, and the +authorization follows it — **feature list, then build, then harden.** + +**What the authorization covers and what it does not.** It authorizes building against this list. It +does not waive the gates that ride on *kinds* of work: prompt bytes still run under rule 16, paid +runs under rule 7, standing configuration under rule 21(b), and any change that *removes* shipped +engine surface still needs its own dated entry (see §6, where that distinction is the reason the +epistemic layer has sat unbuilt). A row's presence here is permission to build it, never permission +to skip the gate its construction crosses. **Governed by [AMBIENT.md rule 24](../../AMBIENT.md).** Every row below is a consequence of that rule or a component that serves it. Where a row's status contradicts the rule, the rule wins and the