Conversation
…clone
Remove the last three places a topic's identity or behavior could come from
compiled code or an operator-cloned document rather than from the topic's own
RLM. Data-driven topic_id / install / pin only; the contract is the generic
`harbor-trials-v1`.
(1) Residual tbench/TB4 hardcode
- `proof-results`: `CONTRACT_TBENCH_HARBOR` is now
`CONTRACT_HARBOR_TRIALS_LEGACY` — the *value* stays (it is signed wire data
a live document still pins) but the constant name stops naming a topic.
New topics pin `harbor-trials-v1`.
- Stripped topic names from product comments, CLI help, and the operator
fixture, which is renamed `topic.install-bundle.json` and regenerated with a
neutral slug / custom id / alias. `proof-admin topic validate --bundle
topic.json`, `ctx-client` timeout rationale, `0024` migration comment, and
the bundle crate's alias docs no longer claim an "Owner default" topic.
(2) RLM TopicSetup authors topic behavior; fail-closed without RLM provenance
- The guest `propose_rules` had a fallback that **echoed the operator's signed
checklist** back as a rule proposal while the driver stamped it
`RuleSource::Rlm` — an operator-cloned TopicDocument masquerading as RLM
authorship. That fallback is gone: a runner shipping no `propose_rules`
entrypoint is `Failed` (503, no row, nothing scored).
- New `RlmStore::current_rules_source` / `rlm_authored_rules` (Pg + Memory) and
`proof_topic_install::{rlm_authored_rules, rules_source}` read
`proof_rule_version.source` directly. `TopicSetup::propose_rules` re-reads
the store after writing and refuses with `RulesNotRlmAuthored` unless the
version in force is `rlm`-sourced.
- The publish gate (`PgInstallJournal::applied`) now requires **both** an
`applied` install row and RLM-authored rules, so an `open` document whose
behavior nobody authored is a 409 rather than a live topic.
- `proof-topic-install::install_rules` documents that what it seeds is
`topic_document` provenance — honest, and explicitly not a substitute.
(3) DB/install-driven open + scorable only
- `load_topics` on the **live** backend skips an `open` document it cannot
prove was installed *and* RLM-authored, logging the provenance that blocked
it, instead of admitting it from the operator's file into `open_topics` /
`scorable_topics`. Sim is exempt: it is the CI/local opt-in backend with no
install, no RLM, and no topic VM.
- New repo-wide guard test asserts no topic id (`tbench`, `tb4`,
`terminal-bench`, `harbor-trials`) is compiled into the challenge, gateway,
orchestrator, or guest product branches. Proven non-vacuous: it catches an
injected literal in non-test logic.
Kept: one VM per submission (`VMS_PER_SUBMISSION = 1`, untouched) and the
`custom_ids` / `ready_for_topic` / family-mux wiring.
Gates: fmt, clippy -D warnings, all 5 xtask checks green; `cargo test
--workspace` has zero new failures vs base 870a3b8 (the 4 remaining failures
are pre-existing root-permission tests). `cargo deny` advisories fails
identically on base.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
@greptileai review DRAFT — merge HOLD until Owner staging A→Z + Mathis GO. Please review the three removals:
Kept intact: one VM per submission ( Two things worth your attention specifically: (a) the new repo-wide guard test |
Greptile SummaryThe update fixes PostgreSQL whole-topic authoring by persisting the canonical rule digest required by Confidence Score: 5/5Safe to merge; there are no outstanding blocking issues. Reviews (33): Last reviewed commit: "fix(proof): write the rule digest in the..." | Re-trigger Greptile |
| fn open_topic_admissible(pool: &sqlx::PgPool, topic_id: &str) -> Result<(), String> { | ||
| let runtime = tokio::runtime::Handle::try_current() | ||
| .map_err(|_| "no async runtime to read the install journal".to_owned())?; | ||
| runtime.block_on(async { |
There was a problem hiding this comment.
Enter Runtime Before Admission
Live startup invokes load_topics after its earlier runtime calls have returned. For each live open topic, admission calls Handle::try_current(), which fails because no runtime is entered; the loader logs that error and skips the topic. As a result, valid installed RLM-authored topics are never loaded and cannot accept submissions. Run this admission flow inside the runtime owned by run.
Artifacts
- This executable temporarily adds and runs the focused no-entered-runtime test, then restores the production source; it directly exercises the admission guard.
- This command output captures the exact startup call path and the open-topic skip branch alongside the `Handle::try_current` guard; it shows the call occurs after prior block-on scopes have returned.
- This executed test output shows one focused test passing after observing the exact no-runtime admission error; it confirms the open-topic admission guard rejects outside an entered Tokio runtime.
| if !proof_topic_install::applied_install(&self.pool, topic_id) | ||
| .await | ||
| .map_err(|e| e.to_string())? | ||
| { | ||
| return Ok(false); | ||
| } | ||
| proof_topic_install::rlm_authored_rules(&self.pool, topic_id) | ||
| .await | ||
| .map_err(|e| e.to_string()) |
There was a problem hiding this comment.
The publish gate separately accepts the newest applied install and the newest RLM-authored rule version, without requiring that the RLM version is the version recorded by that install. A topic can therefore be admitted when its installed rule version came from the topic document while an unrelated later version is RLM-authored. Join the applied install to its exact rules_version and require that joined version to be RLM-authored before admitting the open topic.
Artifacts
- A focused Rust integration test creates an applied install pinned to rule version 1 and a newer RLM rule version 2, then invokes `PgInstallJournal::applied`; it demonstrates the missing version binding.
- The executed test expected refusal because installed rule version 1 has topic-document provenance, but the live service returned true and the command exited 101; this confirms the defect.
- The same executed service reproduction expected the observed independent predicate result and passed with exit code 0, recording `result=true`; this proves current admission behavior.
- The command removed the temporary repository test copy while retaining the authored test and before/after command captures in the artifact directory; the proof files remain available.
| self.store.put_rules(&rules).await?; | ||
| // The read-back is the gate, not a formality: it is what makes "the | ||
| // RLM authored this topic's behavior" a fact the store can prove, | ||
| // rather than a label this driver attached. | ||
| let source = self.store.current_rules_source(&topic.id).await?; | ||
| if source != Some(RuleSource::Rlm) { | ||
| return Err(SetupError::RulesNotRlmAuthored { | ||
| topic_id: topic.id.clone(), | ||
| provenance: source | ||
| .map_or_else(|| "no rule version".to_owned(), |s| format!("{s:?}")), | ||
| version: rules.version, | ||
| }); | ||
| } | ||
| Ok(rules) |
There was a problem hiding this comment.
After writing rules, setup checks only the source of whichever version is newest, rather than the version it wrote. A concurrent RLM write can make that source check pass while setup returns and persists a baseline for its now-stale earlier version. The topic can then open with a baseline that does not match the rules in force. Read back and verify the exact written version or digest, or serialize rule updates through the baseline transition.
Artifacts
- Evidence file captured while the check ran.
- The full command output behind this check.
- The full command output behind this check.
| let sources: [(&str, &str); 6] = [ | ||
| ( | ||
| "gateway-core/src/topic_routes.rs", | ||
| include_str!("../../gateway-core/src/topic_routes.rs"), | ||
| ), | ||
| ( | ||
| "gateway-core/src/admin_route.rs", | ||
| include_str!("../../gateway-core/src/admin_route.rs"), | ||
| ), | ||
| ( | ||
| "proof-vm-guest/src/runner.rs", | ||
| include_str!("../../proof-vm-guest/src/runner.rs"), | ||
| ), | ||
| ( | ||
| "proof-vm-guest/src/lib.rs", | ||
| include_str!("../../proof-vm-guest/src/lib.rs"), | ||
| ), | ||
| ( | ||
| "proof-rlm/src/vm.rs", | ||
| include_str!("../../proof-rlm/src/vm.rs"), | ||
| ), | ||
| ( | ||
| "proof-rlm/src/runner.rs", | ||
| include_str!("../../proof-rlm/src/runner.rs"), | ||
| ), | ||
| ]; | ||
| for (label, source) in sources { | ||
| let non_test = source.split("#[cfg(test)]").next().unwrap_or(""); |
There was a problem hiding this comment.
Hardcode Guard Has Blind Spots
The topic-ID regression guard does not scan crates/proof-challenge/src/topic_routes.rs, and it removes everything after the first #[cfg(test)] marker. proof-vm-guest/src/runner.rs has production code after that marker, so a prohibited topic literal in either excluded area would pass the guard. This is a non-blocking coverage gap that can allow the removed hardcoded routing behavior to return undetected; enumerate all product files and remove only actual test items structurally.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Artifacts
- Authored non-mutating Python probe that recreates the reviewed guard algorithm and runs virtual mutations against the specified product paths, showing the guard has both blind spots.
- Executed the authored probe against the unmodified sources; it shows the challenge path is not listed and production code exists after the guest file’s first test marker, establishing the vulnerable baseline.
- Executed virtual `tbench` mutations through the exact guard algorithm; the omitted challenge mutation is detectable if scanned but unlisted, and the guest mutation after the first test marker is missed, confirming both blind spots.
- Executed the repository’s focused Rust guard test successfully with one passing test, showing the current test passes despite the demonstrated coverage gaps.
… rules, supersede guard, guard blind spots
P1 — live startup skipped every open topic. `load_topics` ran after the
runtime's `block_on` scopes had returned, so `open_topic_admissible`'s
`Handle::try_current()` failed and each valid open topic was logged as
skipped. The loader is now `async` and driven through `rt.block_on`, and
the gate needs no ambient runtime. A test pins that it is callable from a
synchronous context with no runtime entered.
P1 — the publish gate composed two independent predicates ("newest install
is applied", "newest rule version is rlm"). A topic whose install landed
rule version 1 from the signed document (`topic_document`) was admitted as
soon as any later version was RLM-authored, so it could open with the
operator's vector in force — the exact operator-cloned document the gate
exists to refuse. `proof_topic_install::installed_rules` replaces the pair
with one query joining `proof_rule_version.version =
proof_topic_install.rules_version`, and both the publish route and the
startup gate read it. The join is what binds provenance to the vector in
force.
P1 — setup verified the source of whichever rule version was newest, not
the version it wrote. A concurrent RLM write made the check pass while the
version the baseline is measured against was never verified. It now reads
back the exact version and digest it wrote, and a second guard refuses
before persisting a baseline if a different version is in force (the
measurement is stored per rule version, so a vector that moved under the
run would seal a bar measured under rules nobody scores with).
P2 — the topic-id guard had two blind spots: it omitted
`proof-challenge/src/topic_routes.rs`, and it split on the first
`#[cfg(test)]` marker, dropping all production code after a test-only
method. The strip is now brace-depth based over `mod` items only, the list
covers the challenge's dynamic routes and the VM agent's router, and a
non-vacuous test asserts the guard catches an injected literal, ignores a
`cfg(test)` mod, and still scans production code that follows a test-only
method — the shape in `proof-vm-guest/src/runner.rs` that the old split
stopped guarding.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
OWNER LIVE — staging tip + gates 1–6 (SN100 dynamic-topic track)Stack: #297 → #298 → #299 → #300 → #301 (
0. Tip the stack on cortex-stagingcd /opt/cortex # or wherever the staging checkout lives
git fetch origin
git checkout droid/2edcb0c8-100-rlm-autonomous-strip-tbe
git reset --hard 7b05a2933eed1b586a4a8a60babbba9deef5b662 # #301 HEAD
git rev-parse HEAD # expect 7b05a293…Confirm the tip really carries the ceiling raise (from #300) and the RLM-autonomy strip (#301): git log --oneline -6
grep -n "MAX_PROOF_DEADLINE_S_CEILING" crates/proof-task/src/pin.rs # expect 14_400Rebuild + restart the challenge service on the master: docker compose -f deploy/compose/docker-compose.yml --profile master build proof-challenge
docker compose -f deploy/compose/docker-compose.yml --profile master up -d proof-challenge
docker compose -f deploy/compose/docker-compose.yml --profile master logs --tail=200 proof-challengeExpected PASS in the boot log: New in #301 (the fix for this): the admission gate used to run outside the 1. Measured baseline (no
|
|
@greptileai review Four findings from the previous review are fixed at HEAD
Repo gates on |
The install-bound read closed one direction of the provenance defect: a topic whose install landed the signed document's vector while a later version happened to be RLM-authored. The opposite direction was still open. An install lands the RLM's version N, and a later `operator` edit supersedes it as version N+1; a gate that binds only the install's recorded version admits the topic, which then serves rules no RLM wrote. `installed_rules` now reads both halves in one statement — the version the newest `applied` install recorded, and the version in force — and admits only when both are `rlm`-sourced. The new `InstalledRules::SupersededByOperator` names the case so the refusal says which vector is in force. This is deliberately not "the two versions must be equal": an RLM that rewrites its own rules after the install (N → N+1, both `rlm`) is the autonomy this track exists to protect, and it stays admitted. The DB-gated test covers all three transitions: operator edit refused, RLM rewrite admitted, non-`applied` row refused. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
| brace_depth = brace_depth.saturating_add(line.matches('{').count()); | ||
| for _ in 0..line.matches('}').count() { | ||
| brace_depth = brace_depth.saturating_sub(1); | ||
| if depth_test == Some(brace_depth) { | ||
| depth_test = None; | ||
| } | ||
| } | ||
| if let Some(start) = depth_test { | ||
| if brace_depth < start { | ||
| depth_test = None; |
There was a problem hiding this comment.
The test-module stripper counts braces in comments, normal strings, raw strings, and macro input as Rust block delimiters. A { in a test module leaves stripping active after that module closes, so later production code containing a prohibited topic literal is omitted from the scan. This allows hardcoded topic-routing behavior to return without this regression guard detecting it; identify test modules structurally or ignore non-code braces while tracking depth.
Artifacts
- The authored executable copies the current helper and tests a baseline plus comment, normal-string, raw-string, and macro-input brace cases; it is the exact executed source.
- Running the reproducer in baseline mode preserved the later `tbench` literal after ordinary test-module stripping, establishing the comparison behavior.
- Running the reproducer with braces in a comment, normal string, raw string, and macro input produced empty stripped output in every case, confirming the under-scan.
- The exact current repository guard test passed on HEAD 7b05a29 even though the focused reproducer demonstrates that this source shape can evade it.
Greptile found the new guard's own blind spot: the stripper counted `{` and
`}` in raw bytes, so a brace inside a `//` comment, a `"…"` literal, an
`r#"…"#` block, or a macro's input left the depth non-zero after a
`#[cfg(test)]` module closed. Every line after it was then dropped from the
scan, and a prohibited topic literal placed there passed the guard.
`mask_non_code` now blanks comments, string bodies (normal, byte, and raw),
and character literals before any depth is computed, keeping byte offsets
and newlines so line-by-line pairing with the original source holds. The
attribute and `mod` markers are read from the masked line too, so a `mod `
inside a string is not mistaken for a module.
A char literal is distinguished from a lifetime (`'a` is code and stays),
and an unbalanced brace in text can no longer end a module early or keep
one open. The regression test drives all four shapes Greptile named, each
followed by production code carrying a literal, and asserts both that the
literal is seen and that the module is still stripped.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
@greptileai review The brace-counting blind spot you found is fixed at HEAD
New regression test Also fixed in this round (not reported by you, found while hardening the same read): the admission gate now refuses when the install recorded an RLM-authored version but a later |
The crate-local guards in `proof-rlm`, `proof-experiment`, and
`proof-topic-install` strip test code with
`split("#[cfg(test)]").next()`, which stops at the first marker even when
it annotates a method rather than a module. Their sources are safe today —
each marker starts a trailing `mod tests` — but the strip is the same
shape that hid production code in the guest's `runner.rs`, so a literal
added after such a marker would pass them.
Running those sources through the structural strip here closes that
latent gap without rewriting four crates' tests: the same files are now
scanned by a strip that removes only `#[cfg(test)] mod` items and counts
braces in code rather than in text. Proven non-vacuous by injecting a
literal into `proof-rlm/src/lib.rs`, which the guard now names.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
OWNER LIVE — staging tip + gates 1–6 (SN100 dynamic-topic track)Stack: #297 → #298 → #299 → #300 → #301 (
Commits since the previous tip (
0. Tip the stack on cortex-stagingcd /opt/cortex # or wherever the staging checkout lives
git fetch origin
git checkout droid/2edcb0c8-100-rlm-autonomous-strip-tbe
git reset --hard e36d340f0b5aa2091ccec00922be69384566303f # #301 HEAD
git rev-parse HEAD # expect e36d340f…Confirm the tip really carries the ceiling raise (from #300) and the RLM-autonomy strip (#301): git log --oneline -6
grep -n "MAX_PROOF_DEADLINE_S_CEILING" crates/proof-task/src/pin.rs # expect 14_400Rebuild + restart the challenge service on the master: docker compose -f deploy/compose/docker-compose.yml --profile master build proof-challenge
docker compose -f deploy/compose/docker-compose.yml --profile master up -d proof-challenge
docker compose -f deploy/compose/docker-compose.yml --profile master logs --tail=200 proof-challengeExpected PASS in the boot log: New in #301 (the fix for this): the admission gate used to run outside the 1. Measured baseline (no
|
The Rust side already renamed `CONTRACT_TBENCH_HARBOR` to `CONTRACT_HARBOR_TRIALS_LEGACY`, keeping the *value* because it is a signed wire spelling while dropping the topic name from the identifier. The guest adaptor still carried `CONTRACT_TBENCH`, which names a topic the harness does not know. Renamed to `CONTRACT_HARBOR_TRIALS_LEGACY`, matching the Rust constant and its rationale. The string is untouched: a topic signed before the generic id existed pins `tbench-harbor-v1` in its signed `constraints.params.results_contract`, and a signed document cannot be edited. Nothing branches on a topic; the harness accepts both spellings. The deploy gate `assert-harbor-runner-results-emit.sh` asserts the new identifier, and the adaptor suites (148 Python tests, the shell suite) pass. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
@greptileai review HEAD is now
|
The guard covered ten modules chosen by hand. Sixteen more decide what a topic may do and were unguarded: the install executor's engine, routes, section reader, and gate; the guest's fetch and staging; the VM agent's router, auth, hypervisor, and stamp; the gateway's auth, attestation, and proxy paths; and `gateway-core/src/lib.rs`. All thirty-two are now scanned with the structural strip, and the tree is clean for every one of them today — this closes the surface a future edit could put a literal back into. The guard's self-test still asserts it names the file the check was written for and that an injected literal is caught. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
@greptileai review HEAD is Also in this batch: the guest adaptor's |
Both docs described the state before this stack shipped the install path, and both were wrong in the same direction: they understated what is enforced. `deploy/guest/runners/README.md` said a runner without a `propose_rules` entrypoint makes the agent propose the signed topic's own `checklist`. That echo was removed: it is exactly how an operator-cloned document gets recorded as `source = rlm`. The guest now refuses the job (Failed → 503, no row, nothing scored), and the row says so. `docs/COMPLETENESS.md` called topic installs a **skeleton** with the real install unimplemented (exit 3) and `enable` / `disable` / `seal` as stubs. All three shipped: `topic install` applies the bundle's RLM section and journals it, `seal` drives `mark_sealed`, and the gate rows live in `0026_proof_topic_gate.sql`. The row now records the three migrations, the mounted dynamic route table, the install-bound provenance gate (both the recorded version and the version in force), and — explicitly — that the six live gates are staged but **not** claimed green. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
OWNER LIVE — staging tip + gates 1–6 (SN100 dynamic-topic track)Stack: #297 → #298 → #299 → #300 → #301 (
Commits since the previous tip (
0. Tip the stack on cortex-stagingcd /opt/cortex # or wherever the staging checkout lives
git fetch origin
git checkout droid/2edcb0c8-100-rlm-autonomous-strip-tbe
git reset --hard 9b55fe732bffb149bd04b8e5afa6e3085dec71d9 # #301 HEAD
git rev-parse HEAD # expect 9b55fe73…Confirm the tip really carries the ceiling raise (from #300) and the RLM-autonomy strip (#301): git log --oneline -6
grep -n "MAX_PROOF_DEADLINE_S_CEILING" crates/proof-task/src/pin.rs # expect 14_400Rebuild + restart the challenge service on the master: docker compose -f deploy/compose/docker-compose.yml --profile master build proof-challenge
docker compose -f deploy/compose/docker-compose.yml --profile master up -d proof-challenge
docker compose -f deploy/compose/docker-compose.yml --profile master logs --tail=200 proof-challengeExpected PASS in the boot log: New in #301 (the fix for this): the admission gate used to run outside the 1. Measured baseline (no
|
Every real topic id is a hyphen slug (`[a-z0-9][a-z0-9-]{1,62}`), and a
bare SQL identifier cannot contain a hyphen. The migration guard required
a literal `{topic_id}_` prefix, so its requirement was **unsatisfiable**:
`CREATE TABLE fixture-topic-v0_scratch` is a syntax error at the first
`-`, and the underscore spelling was refused as unscoped. An operator
running `topic install --drive-rlm` would provision the VM, run the paid
baseline, and only then hit the deny-list — a paid run that could never
publish.
`topic_sql_prefix` maps `-` to `_`, and `is_topic_scoped` accepts that
identifier-safe spelling alongside the literal one. It stays a boundary:
ids contain no underscores, so the mapping is injective and two ids cannot
collide on one prefix. A sibling topic's table, a `topic_*` name, and a
`proof_*` object are all still refused.
The refusal message also claimed `topic_*` was allowed, which the code has
always refused; it now names the prefix that actually works.
This was invisible in CI: the guard's own suite only used `tb4` (no
hyphen), and the tests that would have caught it are DB-gated and CI has
no Postgres. Found by running the DB-gated suites against a real database.
Two `proof-admin` tests that had never executed now pass; one of them also
asserted the topic's alias where the code correctly prints the custom id.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
🔴 LIVE blocker found and fixed —
|
| Target | Failing | Cause |
|---|---|---|
db --test gateway_store |
3 | s1/s2/s2b raw-weight store |
gateway-store-pg --test pg_stores |
3 | s1/s2/s4 |
proof-rlm-scorer --lib |
1 | max_zip_numeric_id… — root-permission, can't fail as root |
proof-challenge-bin |
1 | seed_pf_allocator… — root-permission |
proof-vm-guest --lib |
2 | agent_tests work-tree sync — root-permission |
I am not claiming these are fine — they are outside this track's scope and unchanged by it. The two raw_weight/pg_stores clusters are worth their own look, since they are the weights path.
OWNER LIVE — staging tip + gates 1–6 (SN100 dynamic-topic track)Stack: #297 → #298 → #299 → #300 → #301 (
Commits since the previous tip (
|
Follow-up: root cause of the pre-existing
|
|
@greptileai review HEAD is
The refusal message also claimed Everything else in this batch is unchanged: the brace-masking strip, the install-bound admission read, the operator-supersede refusal, and the 32-module guard coverage. |
The install-engine suite exercised only `tb4`, which is why the guard's unsatisfiable prefix requirement (fixed in 724ff5b) went unnoticed: `tb4` is both a legal id and a legal bare SQL identifier, so `tb4_scratch` happened to satisfy it. Every real id is a hyphen slug, where it cannot. This test drives the whole path for `fixture-topic-v0`: the migration applies, its table exists in the schema, and a sibling's table, a `topic_*` name, and a `proof_*` object are all still refused — the fix widened the namespace rather than removing it. Proven non-vacuous: with the guard's pre-fix logic restored, the test fails with exactly the LIVE blocker's refusal. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
OWNER LIVE — staging tip + gates 1–6 (SN100 dynamic-topic track)Stack: #297 → #298 → #299 → #300 → #301 (
Commits since the previous tip (
|
|
@greptileai review HEAD is The new test drives the whole path for |
| if schema == Some(topic.as_str()) || schema == Some(sql_topic.as_str()) { | ||
| return true; | ||
| } | ||
| bare.starts_with(&format!("{topic}_")) | ||
| bare.starts_with(&format!("{topic}_")) || bare.starts_with(&format!("{sql_topic}_")) |
There was a problem hiding this comment.
Preserve Quoted Hyphenated Names
The newly accepted literal spelling is unusable for hyphenated topic IDs. is_topic_scoped accepts fixture-topic-v0_scratch and fixture-topic-v0.runs, but migration parsing splits quoted identifiers at - before this check. Valid SQL such as CREATE TABLE "fixture-topic-v0_scratch" (id integer) and CREATE TABLE "fixture-topic-v0".runs (id integer) is therefore rejected as an unscoped object named fixture. The SQL-safe underscore spelling still works, so this does not block merging, but authors cannot use the documented quoted literal namespace.
OWNER LIVE — staging tip + gates 1–6 (SN100 dynamic-topic track)Stack: #297 → #298 → #299 → #300 → #301 (
Commits since the previous tip (
|
| Gate | Code-level coverage | Status |
|---|---|---|
| 1 baseline | proof-topic-install --test install_engine (15/15 against real Postgres, incl. a hyphenated id installing its migrations) |
pass |
2 seal / can_score |
install_engine: install-bound admission read, operator-supersede refusal; proof-rlm-scorer --test rlm_e2e 15/15 |
pass |
3 miner pf_ + Harbor |
proof-results contract tests; deploy/scripts/assert-harbor-runner-results-emit.sh (results.json written before report.json); adaptor suites 148 Python + shell |
pass |
| 4 two submits ⇒ two VMs | proof-rlm-scorer --test rlm_e2e::two_concurrent_submits_reach_the_runner_as_two_runs; vms_per_submission == 1 asserted in install_engine |
pass |
| 5 disable fail-closed | install_engine::the_operator_gate_is_a_journal_and_the_newest_row_wins (DB-gated) |
pass |
| 6 gateway Bearer publish | gateway-core admin-route tests (12/12) + gateway::the_admin_rule_is_re_exported_not_re_implemented — both run in CI without a database |
pass |
Gate 3 needs the most LIVE attention. It is the only gate whose real path (a
miner's artefact through Harbor, a pf_ row, results.json, trial logs) has no
equivalent outside staging.
Known blocker for full ARCH-PIN-100PCT-RLM-AUTONOMOUS
Rules provenance is gate-enforced end to end. Migrations and APIs are not.
migrations and apis reach the install from the operator's bundle rlm section
(SectionPlan), and VmJobOutput has no variant for the RLM to propose them — the
RLM authors rules only (propose_rules). Nothing proves the topic's RLM wrote
its migrations or APIs.
Closing it is a design change plus a refactor, not a patch:
- New
VmJob::ProposeInstall+VmJobOutput::Installvariants, and a guest
propose_installentrypoint with the same fail-closed posture as
propose_rules(no echo of the operator's section). - Provenance columns for migrations/APIs (a
0027migration), and the admission
gate extended to read them. crates/proof-rlmis at 1494/1500 non-test LOC andcrates/proof-vm-guestat
1495/1500. Both need extraction before any of the above lands, orloc-cap
fails.
I have not half-implemented it. It changes what an install accepts, so it wants
an explicit decision rather than a drive-by in this PR.
Also required for DONE (not a staging gate)
- No residual
tbenchproduct hardcode. The repo-wide guard now scans all
32 product modules in the challenge stack (challenge, gateway, install
executor, VM agent, guest, RLM, experiment), with a strip that removes only
#[cfg(test)] moditems and counts braces in code, not in comments,
strings, raw strings, or macro input. Only two literals remain, both signed
wire values incrates/proof-results:harbor-trials-v1and its legacy
spellingtbench-harbor-v1, kept because a signed document cannot be edited.
The guest adaptor's constant is renamedCONTRACT_HARBOR_TRIALS_LEGACY; the
string is untouched. Nothing branches on a topic. - No operator-cloned
tb4as source of truth. Rules must come from the RLM
(proof_rule_version.source = 'rlm'for the version the install recorded).
Topic setup now verifies the exact version and digest it wrote, and refuses to
persist a baseline if a different version is in force when the paid run returns. - Rules source = RLM (ARCH-PIN-100PCT-RLM-AUTONOMOUS): met for rules, NOT met
for migrations/APIs. The rules vector in force must be RLM-authored before a
topic may open — enforced by the publish gate and the boot loader, both reading
the version the install recorded and the version in force. Migrations and
APIs are still operator-supplied by the bundle'srlmsection: the RLM has no
job to propose them. See the blocker section above.
Evidence to paste back
For each gate, paste the exact command output and the HTTP status. A gate is PASS
only with evidence; anything that fails, paste the FAIL text and the agent will
diagnose → fix → push a new draft commit and update this checklist.
Greptile found that the literal spelling I documented in 724ff5b is unreachable. `is_topic_scoped` accepts `"fixture-topic-v0_scratch"`, but `tokens` split on every character that is not alphanumeric, `_`, or `.`, so the `-` broke the quoted run into `fixture` / `topic` / `v0_scratch` — none of which is inside the topic's namespace, so a legal quoted name was refused as unscoped. `tokens` now keeps a double-quoted run whole, stripping the quotes (and folding `""` to one literal quote) so the token is the name the database stores and the deny rules match it exactly as before. The SQL-safe underscore spelling always worked; this makes the documented literal one work too. Non-vacuous: with the tokenizer reverted, both new tests fail — the quoted-name test on the refusal, and the escaped-quote test showing the name tokenized as `["a"]`. Quoting remains no escape hatch: a sibling, a `topic_*` name, and a `proof_*` object are all still refused when quoted. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
A denylist of names this test happens to think of cannot hold an authorship boundary. Your bypass is the proof: The check now enumerates the whole permitted surface: Verified against your exact bypass: injecting those two neutral-named variants fails with Gates on @greptileai review |
The evidence pack for the RLM-authorship DONE criterion, covering all six checklist items with the code path and the command/observed result for each. Two things in it are stated rather than smoothed over: **`pin_policy` does not exist in this tree.** The checklist names it as journal content; there is no such field, column, or parameter (`rg -rni 'pin_policy' .` is empty). What exists is the substance: the pin is global and per-challenge (`config/proof-pin.toml`), and a topic may only tighten a floor (`TopicError::LoosenedFloor`, `crates/proof-task/src/topic.rs`). Reported by name rather than renaming something to match the checklist. **Live staging journal rows are not claimed.** Item 2's evidence is a real Postgres running the real install path (26/26 migrations applied, the DB-gated install-engine suite green, journal fields asserted) — but it is a scratch DB in this container, not cortex-staging. The pack gives Arch the three queries to read the staging rows directly. Also recorded honestly: `cargo deny` fails on RUSTSEC-2026-0285 (`rustls 0.23.43`), which is pre-existing at `6712e7b0` and untouched by this branch; and four test failures in this container are environmental (they assert `0o000` permission denial, which root bypasses — verified identical at `6712e7b0` in a detached worktree). Every cited line number and command output was verified against the tree at `612bdbd1` before committing. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
RLM-authorship evidence packPack:
RLM-authorship evidence packChecklist: Tip under review:
Item 1 — CLI is trigger-only (no operator topic logic)Claim. Surface (
Evidence — no topic literal anywhere in the CLI's source: Evidence — the bundle is hand-authored input, not generated by any command. There is no Item 2 — TopicSetup journal: what lands, and with what provenanceClaim. An install lands migrations, routes, rules, the submission-format and scoring 2a. The journal schema
2b. The executor binding (the pin-carrying record)
pub handler: String, // allow-listed family ("harbor")
pub runner_id: Option<String>, // the signed document's runner
pub custom_id: String, // the topic's metric.custom_id
pub pack_digest: Option<String>, // the signed document's pack pin
pub vms_per_submission: u32, // always VMS_PER_SUBMISSION (=1, item 5)
pub submission_format_digest: Option<String>,
pub scoring_digest: Option<String>,2c. Provenance: the install seeds
|
| Gate | Path | Refuses |
|---|---|---|
propose_rules read-back |
crates/proof-topic-setup/src/lib.rs:388 |
a version whose source != Rlm or whose digest moved |
| boot admission | bins/proof-challenge/src/main.rs:854 load_topics |
an open doc whose install is not applied or whose rules are not RLM-authored (skipped, logged) |
| seal-time | crates/proof-topic-setup/src/lib.rs:415 + :439 |
a measurement taken under a superseded / no-longer-in-force rule version |
2d. Observed result — DB-gated install tests, live Postgres
The install engine's suite is DB-gated (crates/proof-topic-install/tests/install_engine.rs:3
— runs when DATABASE_URL names a Postgres). Run here against a real Postgres 18 with
this branch's migrations applied:
$ sqlx migrate run --source crates/db/migrations # applied 26/26
$ DATABASE_URL=postgres://…/proof_authz_evidence cargo test -p proof-topic-install
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 7 passed; 0 failed; …
test result: ok. 15 passed; 0 failed; …
test result: ok. 6 passed; 0 failed; …
The journal-recording test
(install_engine.rs:197 a_permitted_bundle_installs_and_the_journal_records_it) asserts the
item-2 fields directly:
report.migrations_applied == ["0001_scratch", "0002_index"] # in bundle order
report.rules_version == 1
report.rule_ids == ["no_short_circuit", "no_holdout_leak"]
report.binding.handler == "harbor"
report.binding.runner_id == Some("operator_adaptor_v0") # the document's runner
report.binding.vms_per_submission == 1
report.binding.submission_format_digest.is_some()
report.binding.scoring_digest.is_some()
report.apis.len() == 2
SELECT to_regclass('tb4_scratch') == "tb4_scratch" # the migration really ran
Caveat, stated plainly. This is a real database running the real install path, but it
is a scratch DB in this container, not cortex-staging. The staging-side journal rows
(proof_topic_install / proof_topic_api / proof_rule_version for the live topic) are
Owner/Dev evidence I cannot read from here. If Arch needs the staging rows verbatim, the
query is:
SELECT topic_id, state, rules_version, rule_ids, migrations,
binding->>'handler', binding->>'runner_id', binding->>'vms_per_submission',
binding->>'submission_format_digest', binding->>'scoring_digest'
FROM proof_topic_install ORDER BY id DESC LIMIT 5;
SELECT topic_id, method, path FROM proof_topic_api ORDER BY topic_id, path;
SELECT topic_id, version, source FROM proof_rule_version ORDER BY version DESC LIMIT 5;
-- source must read 'rlm' for the version in force2e. pin_policy — not a field in this tree
The checklist names pin_policy as journal content. There is no such field, column, or
parameter anywhere:
$ rg -rni 'pin_policy|pinpolicy|pin policy' . --glob '!target'
(no output)
What exists instead, and is the substance of that item:
- The pin is global and per-challenge (
config/proof-pin.toml), not per-topic:
gpu_class = "1x",max_proof_deadline_s_ceiling = 14400,eval_image_digest,
flops_budget_max,epsilon_*floors. - A topic may only tighten a pin floor, never loosen it:
crates/proof-task/src/topic.rs:506 TopicError::LoosenedFloor({field} = {got} loosens the pin floor {floor}), raised formetric.quality_floor_nll,metric.epsilon_rel, and the
budget ceiling. - The per-topic pins that do travel are in the signed document
(constraints.model_pin,constraints.params.experiment_pack_digest,
eval_executor.{require_offer_commitment, max_proof_deadline_s}) and are recorded in the
binding / the document itself, not in apin_policyobject.
So item 2's pin_policy is not satisfied as named; the equivalent guarantee is
tighten-only floors against a global pin, at the path above. Flagging rather than
renaming something to match the checklist.
Item 3 — Source of truth is not an operator clone of legacy tbench
Claim. No human-minted tb4/tbench document is the source of truth; the topic
registry is the database, and the topic's behavior is RLM-authored.
Evidence 3a — no committed topic document. No topic draft (.yaml/.yml) exists
anywhere in the tree, and no committed .json carries "id": "tb4" as a topic document.
The only tb4/tbench JSON is a results fixture
(crates/proof-results/fixtures/harbor-trials-v1.json), which is a wire-shape fixture, not
a topic.
Evidence 3b — the "locked default" prose is gone. Commit 8e36538a ("the bundle owns
topic behavior; the CLI hands it to the RLM") removed the earlier wording that made tb4 /
tbench locked defaults:
- … Locked defaults: first slug `tb4` with temporary alias `tbench` (`proof_topic_alias`), …
- … Locked defaults: first topic slug **`tb4`** with temporary alias **`tbench`** …
Evidence 3c — remaining tb4 references are test fixtures, below #[cfg(test)].
In crates/proof-topic-bundle/src/lib.rs the marker is at line 942; the fixtures
(fn tb4() -> TopicInstallBundle at 983, assert_eq!(plan.topic_id, "tb4") at 1022) are
all after it. In crates/gateway/tests/, crates/proof-rlm-store/tests/, and
crates/proof-topic-install/tests/ they are test code by construction.
Evidence 3d — which topics exist is a DB fact. proof_topic_version +
proof_topic_install; the challenge's load_topics admits only documents whose install is
applied and whose rules are RLM-authored (§2c).
Diff vs a human-minted tb4 YAML: there is no such YAML in this repo to diff against.
The pre-8e36538a prose is the closest thing that existed, and it was documentation of an
intended default, not a document the host read. If a human-minted tb4 document exists
outside this repo (operator-held), the SoT test is: the host serves what is in
proof_topic_version, and it only admits it when the DB proves install + RLM authorship —
so an operator's local YAML has no path to being served.
Item 4 — Residual tbench/tb4 product hardcode: ZERO on tip
Claim. No product branch carries a topic id, benchmark name, or results-contract id.
Guard — crates/proof-topic-bundle/src/lib.rs:1300 no_topic_id_is_compiled_into_the_product_branches scans 32 product modules (challenge,
gateway, orchestrator, guest, bundle, CLI) for
FORBIDDEN_LITERALS = ["tbench", "tb4", "terminal-bench", "terminal bench", "harbor-trials"]
(guard at :1865), using a structural strip that removes test modules and masks non-code
braces (ec13bb4a fixed a brace-counting hole; 7b05a293 closed blind spots Greptile
found).
$ cargo test -p proof-topic-bundle no_topic_id_is_compiled_into_the_product_branches
test result: ok. 1 passed; 0 failed
Before/after grep, by hand, on the modules with the most historical hits — counting only
lines before each file's first #[cfg(test)]:
| Module | Raw hits | First #[cfg(test)] |
Hits in production code |
|---|---|---|---|
crates/proof-challenge/src/topic_routes.rs |
20 | line 257 | 0 |
crates/gateway-core/src/topic_routes.rs |
12 | line 103 | 0 |
crates/gateway-core/src/admin_route.rs |
1 | line 83 | 0 |
crates/proof-challenge/src/lib.rs |
0 | — | 0 |
crates/proof-challenge/src/emit.rs |
0 | — | 0 |
crates/gateway-core/src/proxy_paths.rs |
0 | — | 0 |
Known remaining occurrences on tip, none of which is a product branch:
| Where | What | Why it stays |
|---|---|---|
crates/proof-results/src/lib.rs:94 |
CONTRACT_HARBOR_TRIALS_LEGACY = "tbench-harbor-v1" |
a wire value: a topic signed before the generic id pins it, and a signed document cannot be edited. proof-results is deliberately not in the guarded 32 because the id is its interface; no guarded module may branch on it |
deploy/guest/runners/…/lib.sh, summarize.py comments |
metal RCA notes (tbench-x0004, x0032, x0039) |
comments recalling which metal run showed a defect |
deploy/guest/runners/…/tests/* |
fixtures | test code |
docs/runbooks/* |
metal RCAs | documentation |
The shipping adaptor has its own genericity guard —
deploy/guest/runners/rlm_fc_in_guest_harbor/tests/test_filter_tasks.py:399 test_no_compiled_task_names_or_modes asserts filter_tasks.py contains none of
first15 / first-15 / shortpack / x0017 / tb4 / duration_hints.
Item 5 — 1 VM per submission, and no product default of max_experiment_vms = 1
Claim. The one-VM-per-submission pin holds, is recorded, and is enforced on the submit
path; the experiment-VM count default is unchanged.
Evidence 5a — the pin.
// crates/proof-topic-install/src/install.rs:79
pub const VMS_PER_SUBMISSION: u32 = 1;Its doc comment states the intent: "the constant is the pin the install records; a future
slice cannot quietly allow a second concurrent VM per submission without changing this value
and the journal rows that carry it." It lands in every install's ExecutorBinding
(:123), and the install engine's own test asserts it
(crates/proof-topic-install/src/lib.rs:147 assert_eq!(VMS_PER_SUBMISSION, 1)).
Evidence 5b — the host refuses a topic installed under a different pin. On the submit
path (crates/proof-http/src/operator.rs:108):
Some(n)withn != 1is refused on the submit path: this build runs one VM per
submission, and a topic installed with a different pin is one it cannot honour.
Evidence 5c — the product default is untouched (checklist item 4 of the Arch PIN):
$ grep -n 'DEFAULT_MAX_EXPERIMENT_VMS: usize' crates/proof-vm-agent/src/router.rs
55:pub const DEFAULT_MAX_EXPERIMENT_VMS: usize = 2;
Still 2. The Gate 4 hardening added a second cap beside it (host memory admission,
f0800353 → dbd26cdd) — it did not replace or lower the count cap.
Item 6 — Tips, checks, and the stacked PRs
Branch tips
| PR | Branch | Tip | Base | Draft | Merge state |
|---|---|---|---|---|---|
| #297 | droid/795020b8-sn100-p0-topics-table-admin |
f298c4d7cfbf |
main |
yes | BLOCKED (branch protection) |
| #298 | droid/9f68584e-sn100-p1a-rlm-topic-install |
b735f3358d3d |
#297 | yes | CLEAN |
| #299 | droid/9822d526-sn100-100-live-gaps-p1b-disa |
37fa0920610c |
#298 | yes | CLEAN |
| #300 | droid/933f76bf-b1-raise-max-proof-deadline |
870a3b875533 |
#299 | yes | CLEAN |
| #301 | droid/2edcb0c8-100-rlm-autonomous-strip-tbe |
612bdbd1 |
#300 | yes | CLEAN |
main is aabd1724eb90. The stack is linear: #301 → #300 → #299 → #298 → #297 → main.
Checks
| PR | CI (ci.yml) |
Greptile |
|---|---|---|
| #297 | SUCCESS (run 34858683718, 5m16s) + CodeQL SUCCESS + Analyze SUCCESS |
SUCCESS |
| #298 | not triggered (base is a droid branch, not main) |
SUCCESS |
| #299 | not triggered | SUCCESS |
| #300 | not triggered | SUCCESS |
| #301 | not triggered | SUCCESS (69 files reviewed, 0 comments) |
Why CI runs only on #297: ci.yml triggers on pull_request: branches: [main]. #297 is
the only PR in the stack whose base is main; #298–#301 are stacked on each other, so
GitHub never fires that workflow for them. To compensate, every gate ci.yml runs was
executed locally on the tip — see below.
Local gate run on 612bdbd1 (CI parity)
| Gate | Result |
|---|---|
cargo fmt --all -- --check |
pass |
cargo clippy --workspace --all-targets -- -D warnings |
pass |
cargo test --workspace |
pass (4 pre-existing failures excluded — see caveat) |
cargo run -p xtask -- loc-cap |
pass |
cargo run -p xtask -- consensus-lint |
pass |
cargo run -p xtask -- spec-check |
pass |
cargo run -p xtask -- design-check |
pass |
cargo run -p xtask -- external-docs-check |
pass |
cargo deny check |
advisories FAILED — pre-existing, see caveat |
adaptor suites (python3 -m unittest …, test_adaptor.sh) |
pass |
deploy/scripts/test_proof_slice_preflight.sh |
pass |
Two caveats, stated rather than buried:
cargo deny checkfails onRUSTSEC-2026-0285(rustls 0.23.43, "TLS 1.3 handshake
messages incorrectly accepted across encryption level boundaries"). This is pre-existing
and not introduced by this branch:rustls 0.23.43is the version at6712e7b0and the
lockfile's rustls entry is untouched by these commits. It needs acargo update -p rustls
sweep, which is its own change and touches every crate that depends on rustls. Flagged,
not silently ignored. Note feat(proof): proof-admin install bundle over the existing topic publish path (P0) #297's CI was green when it last ran (2026-09-14), before this
advisory landed in the database.- Four test failures in this container are environmental, not regressions. They fail
identically at pristine6712e7b0(verified by running them in a detached worktree at
that SHA) because they assert on0o000permission denial, which root bypasses — this
container runs as uid 0:
seed_pf_allocator_refuses_boot_when_a_topic_dir_cannot_be_read,
max_zip_numeric_id_fails_closed_when_a_topic_dir_cannot_be_read,
paid_run_fails_closed_when_work_tree_cannot_be_synced,
deadline_cut_still_persists_work_tree. They are excluded from the "pass" above, not
hidden.
Summary
| # | Item | Verdict |
|---|---|---|
| 1 | CLI trigger-only | met — zero topic literals in bins/proof-admin/src; no bundle-generating command |
| 2 | Journal: rules source=rlm, migrations, proof_topic_api, submission_format, runner |
met (DB-gated tests pass against real Postgres; journal fields asserted). pin_policy does not exist — the equivalent is a global pin + tighten-only floors, paths given |
| 3 | SoT ≠ operator clone of legacy tbench |
met — no committed topic document; "locked defaults" prose removed in 8e36538a; remaining tb4 refs are test fixtures |
| 4 | Residual product hardcode ZERO | met — 32-module guard passes; hand grep shows 0 hits before every #[cfg(test)]; the legacy tbench-harbor-v1 string is a signed-document wire value, not a branch |
| 5 | 1 VM/submission | met — VMS_PER_SUBMISSION = 1, recorded per install, refused on the submit path when mismatched; DEFAULT_MAX_EXPERIMENT_VMS still 2 |
| 6 | Tips / checks / PRs | given — table above; CI fires only on #297 by design, local CI-parity run on the tip |
Not claimed: live cortex-staging journal rows (Owner/Dev territory), and the pin_policy
field by that name. Everything else above is reproducible from this checkout at 612bdbd1.
Not merged. PR #301 is a draft; the merge HOLD stands pending Mathis GO.
|
Evidence pack is in: @greptileai review |
…fore/after Incorporates Dev's live `cortex-staging` dig and closes the checklist boxes that were still thin. **Live staging evidence (item 2, strongest in the pack):** `proof_rule_version` v5–v7 for `tb4` are all `source=rlm` (v7 `7231a716…`); install #11 is `applied` with `rules_version=7`, `migrations=["0001_scratch"]`, `binding.runner=rlm_fc_in_guest_harbor`, `vms_per_submission=1`, `custom_id=tbench`; `proof_topic_api` holds only `GET status`. **The authorship gap, stated exactly (§2f).** The checklist asks to prove migrations / apis / submission_format / pin_policy are RLM-authored. They are not: the job surface is `VmJob::{ProposeRules, Baseline, Inspect, Evaluate, Archive}` and the only behavior it can return is `VmJobOutput::Rules(Vec<ChecklistRule>)`. There is no variant that carries a migration or a route, and no wire message that could transport one. The pack gives the per-part table (who authors what, where it lands, how it is enforced) and scopes the four things closing it would take. Not started: it changes what the RLM *is*, and the PIN reads as a claim about the system, not a work order. **The live SoT gap (§3e).** B1 FIXED was driven by a human-authored YAML, and the pack says so rather than rounding up. What holds: the host serves what `proof_topic_version` holds, and admits an `open` document only under an `applied` install with RLM-authored rules, so a local YAML has no path to being served on its own. What does not: the document and the schema are the operator's. The honest label is "RLM-ruled, operator-declared". **Before/after inventory (item 4).** `bins/proof-admin/src/main.rs` production literals **6 → 0** across `8e36538a`; the six removed are CLI usage examples and doc-comment examples, now generic (`--bundle <path>`). The one `terminal-bench` left in `bins/proof-admin/tests/cli.rs:1139` is a member of that file's own forbidden-literal guard — the check, not a usage. (An earlier draft of this section said 46 for that file; that was a shell bug in my counting script and is corrected here.) Also corrected: the tip is `e204b426`, and PR #302 now exists for this session's branch — the earlier "pr_url was null" report was accurate, the branch had never been pushed. Every cited line number and number re-verified against the tree at `e204b426` before committing. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
RLM-authorship evidence pack (v2)Pack:
RLM-authorship evidence packChecklist: Tip under review:
Verdict summary
The one open authorship gap is item 2, and it is architectural, not a defect: the RLM Item 1 — CLI is trigger-only (no operator topic logic)Claim. Surface (
Evidence — no topic literal anywhere in the CLI's source: Evidence — the bundle is hand-authored input, not generated by any command. There is no Item 2 — TopicSetup journal: what lands, and with what provenanceClaim. An install lands migrations, routes, rules, the submission-format and scoring 2a. The journal schema
2b. The executor binding (the pin-carrying record)
pub handler: String, // allow-listed family ("harbor")
pub runner_id: Option<String>, // the signed document's runner
pub custom_id: String, // the topic's metric.custom_id
pub pack_digest: Option<String>, // the signed document's pack pin
pub vms_per_submission: u32, // always VMS_PER_SUBMISSION (=1, item 5)
pub submission_format_digest: Option<String>,
pub scoring_digest: Option<String>,2c. Provenance: the install seeds
|
| Gate | Path | Refuses |
|---|---|---|
propose_rules read-back |
crates/proof-topic-setup/src/lib.rs:388 |
a version whose source != Rlm or whose digest moved |
| boot admission | bins/proof-challenge/src/main.rs:854 load_topics |
an open doc whose install is not applied or whose rules are not RLM-authored (skipped, logged) |
| seal-time | crates/proof-topic-setup/src/lib.rs:415 + :439 |
a measurement taken under a superseded / no-longer-in-force rule version |
2d. Observed result — live cortex-staging [staging]
Dev's dig on cortex-staging, which is the strongest evidence in this pack because it is the
real host:
| Fact | Value |
|---|---|
proof_rule_version for tb4, versions v5–v7 |
all source=rlm |
| v7 digest | 7231a716… |
| install #11 | applied, bundle sha256:d2429e99… |
└ rules_version |
7 |
└ migrations |
["0001_scratch"] |
└ binding.runner |
rlm_fc_in_guest_harbor |
└ binding.vms_per_submission |
1 |
└ binding.custom_id |
tbench |
proof_topic_api |
only GET status for tb4 |
What this proves. The rule vector in force on staging is RLM-authored, three versions
deep (v5–v7), and the install journal's rules_version matches it (7). The VM pin is 1.
The routes table is live and scoped to the topic.
What it also shows — and this is the gap. migrations = ["0001_scratch"] and
apis = [GET status] are the bundle's section, applied by the install. Nothing in the
journal records an RLM as their author, because nothing can: see §2f.
2e. Observed result — real database, real install path [local-db]
The install engine's suite is DB-gated (crates/proof-topic-install/tests/install_engine.rs:3
— runs when DATABASE_URL names a Postgres). Run here against a real Postgres 18 with
this branch's migrations applied:
$ sqlx migrate run --source crates/db/migrations # applied 26/26
$ DATABASE_URL=postgres://…/proof_authz_evidence cargo test -p proof-topic-install
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 7 passed; 0 failed; …
test result: ok. 15 passed; 0 failed; …
test result: ok. 6 passed; 0 failed; …
The journal-recording test
(install_engine.rs:197 a_permitted_bundle_installs_and_the_journal_records_it) asserts the
item-2 fields directly:
report.migrations_applied == ["0001_scratch", "0002_index"] # in bundle order
report.rules_version == 1
report.rule_ids == ["no_short_circuit", "no_holdout_leak"]
report.binding.handler == "harbor"
report.binding.runner_id == Some("operator_adaptor_v0") # the document's runner
report.binding.vms_per_submission == 1
report.binding.submission_format_digest.is_some()
report.binding.scoring_digest.is_some()
report.apis.len() == 2
SELECT to_regclass('tb4_scratch') == "tb4_scratch" # the migration really ran
The scratch DB was dropped after the run. This is a real install path on a real database —
but it is not cortex-staging; §2d is.
2f. The authorship gap, stated exactly
The checklist asks to prove migrations / apis / submission_format / pin_policy are
RLM-authored. They are not. Here is the whole boundary:
| Part | Who authors it | Where it lands | Enforced how |
|---|---|---|---|
| rules | the RLM (propose_rules in its topic VM) |
proof_rule_version.source='rlm' |
three gates: propose_rules read-back (proof-topic-setup/src/lib.rs:388), boot admission (bins/proof-challenge/src/main.rs:854), seal-time version checks (:415, :439) |
migrations |
the operator (bundle rlm section) |
applied by the install; names in proof_topic_install.migrations |
deny-list (proof-topic-sql-guard) before the first statement runs |
apis |
the operator (bundle rlm section) |
proof_topic_api rows |
path/method shape checks; topic-relative paths only |
submission_format |
the operator (bundle) | digest in binding.submission_format_digest |
shape + digest |
scoring |
the operator (bundle) | digest in binding.scoring_digest |
shape + digest |
pin_policy |
does not exist | — | the pin is global (config/proof-pin.toml); topics may only tighten (TopicError::LoosenedFloor, crates/proof-task/src/topic.rs:506) |
Why the RLM cannot author the others today: the job surface is
VmJob::{ProposeRules, Baseline, Inspect, Evaluate, Archive} and the only behavior it can
return is VmJobOutput::Rules(Vec<ChecklistRule>)
(crates/proof-rlm/src/vm.rs:203, :315). There is no variant that carries a migration or
a route, and no wire message (proof-vm-proto::guest) that could transport one. The install
seeds rule version 1 as RuleSource::TopicDocument by design
(crates/proof-topic-install/src/install.rs:493), and says so:
Provenance is the point here. What this seeds is
topic_document… An install therefore
never makes a topic's behavior RLM-authored.
What closing it would require (scoped, not started — it is a real architectural change,
not a doc fix):
- Two new
VmJobOutputvariants (e.g. schema + routes) and the correspondingVmJob
inputs, so the RLM can propose them the way it proposes rules. - A guest-side authorship path: the adaptor would have to emit them from the topic VM, with
the same read-back-and-verify provenance the rule path has. - A new trust story for RLM-written DDL — the deny-list stays, but "the RLM authored
this table" becomes a claim the install must verify rather than assume. - Versioning + supersede semantics matching the rule path (
rules_still_in_forcehas no
analogue for schema), and journal columns recording provenance per part.
I did not do this unasked: it changes what the RLM is, and the PIN reads as a claim about
the system rather than a work order. Say the word and it becomes its own PR.
Item 3 — Source of truth is not an operator clone of legacy tbench
Claim. No human-minted tb4/tbench document is the source of truth; the topic
registry is the database, and the topic's behavior is RLM-authored.
Evidence 3a — no committed topic document. No topic draft (.yaml/.yml) exists
anywhere in the tree, and no committed .json carries "id": "tb4" as a topic document.
The only tb4/tbench JSON is a results fixture
(crates/proof-results/fixtures/harbor-trials-v1.json), which is a wire-shape fixture, not
a topic.
Evidence 3b — the "locked default" prose is gone. Commit 8e36538a ("the bundle owns
topic behavior; the CLI hands it to the RLM") removed the earlier wording that made tb4 /
tbench locked defaults:
- … Locked defaults: first slug `tb4` with temporary alias `tbench` (`proof_topic_alias`), …
- … Locked defaults: first topic slug **`tb4`** with temporary alias **`tbench`** …
Evidence 3c — remaining tb4 references are test fixtures, below #[cfg(test)].
In crates/proof-topic-bundle/src/lib.rs the marker is at line 942; the fixtures
(fn tb4() -> TopicInstallBundle at 983, assert_eq!(plan.topic_id, "tb4") at 1022) are
all after it. In crates/gateway/tests/, crates/proof-rlm-store/tests/, and
crates/proof-topic-install/tests/ they are test code by construction.
Evidence 3d — which topics exist is a DB fact. proof_topic_version +
proof_topic_install; the challenge's load_topics admits only documents whose install is
applied and whose rules are RLM-authored (§2c). On staging, proof_rule_version v5–v7 for
tb4 are all source=rlm [staging] (§2d).
3e. The live SoT gap — B1 FIXED used a human YAML
Dev's dig found the thing that matters: the B1 FIXED run that took Gates 1–6 to GO was
driven by a human-authored YAML topic document. That is not the final authorship SoT, and
this pack does not claim otherwise.
What it means precisely, and what it does not:
| Question | Answer |
|---|---|
| Was the rule vector the RLM's? | Yes — proof_rule_version v5–v7 all source=rlm [staging]. The authorship gate for rules held on the live host. |
| Was the document (statement, metric, constraints, floors, pack pin) the RLM's? | No — an operator wrote it. It is signed by the topic key, so the host trusts it, but a human chose its contents. |
| Does a human YAML have a path to being served? | No, not on its own. The host serves what is in proof_topic_version, and only admits an open document whose install is applied and whose rules are RLM-authored. A local YAML that was never installed and never had rules proposed is not admitted. |
| So is the SoT "operator clone of legacy tbench"? | No — but it is operator-authored, which is one step short of the PIN's intent. |
The honest distinction: the PIN's first clause ("no operator-cloned tb4 as SoT") holds —
nothing in the tree is a clone of the legacy topic, and the host's registry is the DB. The
PIN's deeper intent ("the topic authors itself") is partly met: the rules are the
RLM's, the document and the schema are the operator's.
What would close it: the same work as §2f — the RLM has to be able to propose the parts
of the document that are currently hand-written (at minimum constraints.params, the metric
choice, and the migrations/APIs), with the provenance recorded per part. Until then, a topic
is "RLM-ruled, operator-declared", and the pack should say so rather than round up.
Diff vs a human-minted tb4 YAML: there is no such YAML in this repo to diff
against. The pre-8e36538a prose ("Locked defaults: first slug tb4 with temporary alias
tbench") is the closest thing that existed, and it was documentation of an intended
default, not a document the host read. The live B1 YAML is operator-held on staging, outside
this repo; the check that matters is the one above — the host only serves what
proof_topic_version holds under an applied install with RLM-authored rules.
Item 4 — Residual tbench/tb4 product hardcode: ZERO on tip
Claim. No product branch carries a topic id, benchmark name, or results-contract id.
Guard — crates/proof-topic-bundle/src/lib.rs:1300 no_topic_id_is_compiled_into_the_product_branches scans 32 product modules (challenge,
gateway, orchestrator, guest, bundle, CLI) for
FORBIDDEN_LITERALS = ["tbench", "tb4", "terminal-bench", "terminal bench", "harbor-trials"]
(guard at :1865), using a structural strip that removes test modules and masks non-code
braces (ec13bb4a fixed a brace-counting hole; 7b05a293 closed blind spots Greptile
found).
$ cargo test -p proof-topic-bundle no_topic_id_is_compiled_into_the_product_branches
test result: ok. 1 passed; 0 failed
Before/after inventory. "Production code" below means every line before the file's
first #[cfg(test)]; the pre-strip column is 8e36538a^ (the commit that moved topic
behavior out of product code), the tip column is e204b426.
| Module | Pre-strip | Tip |
|---|---|---|
bins/proof-admin/src/main.rs |
6 | 0 |
crates/proof-challenge/src/topic_routes.rs |
0 | 0 |
crates/proof-challenge/src/lib.rs |
0 | 0 |
crates/proof-challenge/src/emit.rs |
0 | 0 |
crates/gateway-core/src/topic_routes.rs |
0 | 0 |
crates/gateway-core/src/admin_route.rs |
0 | 0 |
crates/gateway-core/src/proxy_paths.rs |
0 | 0 |
The six that were removed from proof-admin's production code were CLI usage examples and
doc-comment examples, e.g.:
62: proof-admin topic validate --bundle tb4.json --pin config/proof-pin.toml
65: proof-admin topic install --bundle tb4.json --env metal --dry-run
171: /// The alias slug (e.g. `tbench`).
173: /// The canonical topic slug it resolves to (e.g. `tb4`).
595: // An alias resolves to its canonical slug first, so `show tbench` finds
596: // `tb4`. Resolution is fail-closed in the store: an alias whose topic has
They are now generic (--bundle <path>), which is the difference between "the CLI knows a
topic" and "the CLI takes one".
bins/proof-admin/tests/cli.rs carries one occurrence of terminal-bench — inside the
CLI's own guard test, as a member of its forbidden list (cli.rs:1139). It is the check,
not a usage.
Whole-tree production-code sweep on the tip (the 32 guarded modules plus the CLI):
$ cargo test -p proof-topic-bundle no_topic_id_is_compiled_into_the_product_branches
test result: ok. 1 passed; 0 failed
Hand grep on the modules with the most historical hits — raw hits vs hits before each
file's first #[cfg(test)]:
| Module | Raw hits | First #[cfg(test)] |
Production-code hits |
|---|---|---|---|
crates/proof-challenge/src/topic_routes.rs |
20 | line 257 | 0 |
crates/gateway-core/src/topic_routes.rs |
12 | line 103 | 0 |
crates/gateway-core/src/admin_route.rs |
1 | line 83 | 0 |
Known remaining occurrences on tip, none of which is a product branch:
| Where | What | Why it stays |
|---|---|---|
crates/proof-results/src/lib.rs:94 |
CONTRACT_HARBOR_TRIALS_LEGACY = "tbench-harbor-v1" |
a wire value: a topic signed before the generic id pins it, and a signed document cannot be edited. proof-results is deliberately not in the guarded 32 because the id is its interface; no guarded module may branch on it |
deploy/guest/runners/…/lib.sh, summarize.py comments |
metal RCA notes (tbench-x0004, x0032, x0039) |
comments recalling which metal run showed a defect |
deploy/guest/runners/…/tests/* |
fixtures | test code |
docs/runbooks/* |
metal RCAs | documentation |
The shipping adaptor has its own genericity guard —
deploy/guest/runners/rlm_fc_in_guest_harbor/tests/test_filter_tasks.py:399 test_no_compiled_task_names_or_modes asserts filter_tasks.py contains none of
first15 / first-15 / shortpack / x0017 / tb4 / duration_hints.
Item 5 — 1 VM per submission, and no product default of max_experiment_vms = 1
Claim. The one-VM-per-submission pin holds, is recorded, and is enforced on the submit
path; the experiment-VM count default is unchanged.
Evidence 5a — the pin.
// crates/proof-topic-install/src/install.rs:79
pub const VMS_PER_SUBMISSION: u32 = 1;Its doc comment states the intent: "the constant is the pin the install records; a future
slice cannot quietly allow a second concurrent VM per submission without changing this value
and the journal rows that carry it." It lands in every install's ExecutorBinding
(:123), and the install engine's own test asserts it
(crates/proof-topic-install/src/lib.rs:147 assert_eq!(VMS_PER_SUBMISSION, 1)).
Evidence 5b — the host refuses a topic installed under a different pin. On the submit
path (crates/proof-http/src/operator.rs:108):
Some(n)withn != 1is refused on the submit path: this build runs one VM per
submission, and a topic installed with a different pin is one it cannot honour.
Evidence 5c — the product default is untouched (checklist item 4 of the Arch PIN):
$ grep -n 'DEFAULT_MAX_EXPERIMENT_VMS: usize' crates/proof-vm-agent/src/router.rs
55:pub const DEFAULT_MAX_EXPERIMENT_VMS: usize = 2;
Still 2. The Gate 4 hardening added a second cap beside it (host memory admission,
f0800353 → dbd26cdd) — it did not replace or lower the count cap.
Item 6 — Tips, checks, and the stacked PRs
Branch tips
| PR | Branch | Tip | Base | Draft | Merge state |
|---|---|---|---|---|---|
| #297 | droid/795020b8-sn100-p0-topics-table-admin |
f298c4d7cfbf |
main |
yes | BLOCKED (branch protection) |
| #298 | droid/9f68584e-sn100-p1a-rlm-topic-install |
b735f3358d3d |
#297 | yes | CLEAN |
| #299 | droid/9822d526-sn100-100-live-gaps-p1b-disa |
37fa0920610c |
#298 | yes | CLEAN |
| #300 | droid/933f76bf-b1-raise-max-proof-deadline |
870a3b875533 |
#299 | yes | CLEAN |
| #301 | droid/2edcb0c8-100-rlm-autonomous-strip-tbe |
e204b426 |
#300 | yes | CLEAN |
| #302 | droid/1d0afa5f-sn100-stay-lit-cont-gate1-pa |
e204b426 |
#300 | yes | CLEAN |
main is aabd1724eb90. The stack is linear: #301 → #300 → #299 → #298 → #297 → main.
#301 is the canonical stack position. #302 is a mirror this session opened so the branch
has its own URL (the earlier "pr_url was null" report was accurate for
droid/1d0afa5f-…, which had never been pushed — it is pushed now, and #302 is its PR).
Both PRs carry the same HEAD; merge #301.
Checks
| PR | CI (ci.yml) |
Greptile |
|---|---|---|
| #297 | SUCCESS (run 34858683718, 5m16s) + CodeQL SUCCESS + Analyze SUCCESS |
SUCCESS |
| #298 | not triggered (base is a droid branch, not main) |
SUCCESS |
| #299 | not triggered | SUCCESS |
| #300 | not triggered | SUCCESS |
| #301 | not triggered | SUCCESS (69 files reviewed, 0 comments) |
| #302 | not triggered (mirror of #301) | see PR |
Why CI runs only on #297: ci.yml triggers on pull_request: branches: [main]. #297 is
the only PR in the stack whose base is main; #298–#301 are stacked on each other, so
GitHub never fires that workflow for them. To compensate, every gate ci.yml runs was
executed locally on the tip — see below.
Local gate run on 612bdbd1 (CI parity)
| Gate | Result |
|---|---|
cargo fmt --all -- --check |
pass |
cargo clippy --workspace --all-targets -- -D warnings |
pass |
cargo test --workspace |
pass (4 pre-existing failures excluded — see caveat) |
cargo run -p xtask -- loc-cap |
pass |
cargo run -p xtask -- consensus-lint |
pass |
cargo run -p xtask -- spec-check |
pass |
cargo run -p xtask -- design-check |
pass |
cargo run -p xtask -- external-docs-check |
pass |
cargo deny check |
advisories FAILED — pre-existing, see caveat |
adaptor suites (python3 -m unittest …, test_adaptor.sh) |
pass |
deploy/scripts/test_proof_slice_preflight.sh |
pass |
Two caveats, stated rather than buried:
cargo deny checkfails onRUSTSEC-2026-0285(rustls 0.23.43, "TLS 1.3 handshake
messages incorrectly accepted across encryption level boundaries"). This is pre-existing
and not introduced by this branch:rustls 0.23.43is the version at6712e7b0and the
lockfile's rustls entry is untouched by these commits. It needs acargo update -p rustls
sweep, which is its own change and touches every crate that depends on rustls. Flagged,
not silently ignored. Note feat(proof): proof-admin install bundle over the existing topic publish path (P0) #297's CI was green when it last ran (2026-09-14), before this
advisory landed in the database.- Four test failures in this container are environmental, not regressions. They fail
identically at pristine6712e7b0(verified by running them in a detached worktree at
that SHA) because they assert on0o000permission denial, which root bypasses — this
container runs as uid 0:
seed_pf_allocator_refuses_boot_when_a_topic_dir_cannot_be_read,
max_zip_numeric_id_fails_closed_when_a_topic_dir_cannot_be_read,
paid_run_fails_closed_when_work_tree_cannot_be_synced,
deadline_cut_still_persists_work_tree. They are excluded from the "pass" above, not
hidden.
Summary
| # | Item | Verdict |
|---|---|---|
| 1 | CLI trigger-only | met — zero topic literals in bins/proof-admin/src; no bundle-generating command; six such literals removed by 8e36538a (before/after in §4) |
| 2 | Journal: rules source=rlm, migrations, proof_topic_api, submission_format, runner |
partly met — rules are RLM-authored on live staging (v5–v7, source=rlm); migrations / apis / submission_format / scoring come from the operator bundle; pin_policy does not exist (§2f gives the exact boundary and what closing it needs) |
| 3 | SoT ≠ operator clone of legacy tbench |
met in code; live gap flagged — B1 FIXED was driven by a human YAML (§3e). The host's SoT is the DB, and an operator YAML has no path to being served without an applied install + RLM-authored rules |
| 4 | Residual product hardcode ZERO | met — proof-admin production literals 6 → 0; 32-module guard passes; hand grep shows 0 hits before every #[cfg(test)]; the legacy tbench-harbor-v1 string is a signed-document wire value, not a branch |
| 5 | 1 VM/submission | met — VMS_PER_SUBMISSION = 1, recorded per install (1 on staging install #11), refused on the submit path when mismatched; DEFAULT_MAX_EXPERIMENT_VMS still 2 |
| 6 | Tips / checks / PRs | given — stack table above; CI fires only on #297 by design, local CI-parity run on the tip |
The open item is 2, and it is architectural. The RLM authors the rule vector — proven
live. It cannot author migrations, APIs, submission-format or scoring, because no job or wire
message carries them (§2f). Closing that changes what the RLM is; it is scoped in §2f and
not started.
Not claimed: that the RLM authors the schema or the routes; that B1's human YAML is the
final authorship SoT; the pin_policy field by that name.
Not merged. PR #301 is a draft; the merge HOLD stands pending Mathis GO.
Greptile's P1 security finding on the tip, reproduced independently before fixing. The migration deny-list namespaces objects by the topic's mapped prefix (`-` → `_`). The mapping is injective, but its **prefixes are not prefix-free**: `aa` and `aa-b` are both legal ids, and `aa_b_scratch` reads as `aa` + `b_scratch` **and** as `aa-b` + `scratch`. Both pass `is_topic_scoped`, so a migration approved for `aa` could create, read, or drop a table belonging to `aa-b` in the shared database. **The per-topic guard cannot fix this.** It sees one topic. Refusing every name with an underscore after the prefix would close the hole but also refuse ordinary names like `tb4_scratch_idx` — including the live shape's own index naming. I implemented that first, watched it break three legitimate tests, and backed it out: the question is about the **registry**, so it belongs where the registry is visible. - `proof_topic_sql_guard::claim_collisions` reports any name another registered topic also claims. - `Installer::refuse_cross_topic_claims` runs it **before the journal opens**, so a collision writes nothing at all — no row, no rule, no table — and refuses with `InstallError::CrossTopicClaim`, naming the migration, the object, and both topics. A store it cannot read is a refusal too: a collision check that cannot enumerate the registry would pass by default. Tests, both non-vacuous (neutering the check fails them): `a_bare_name_two_topics_claim_is_reported_as_a_collision` and `a_name_no_registered_sibling_claims_is_not_a_collision` at the guard, and `a_migration_that_reaches_a_sibling_topics_namespace_is_refused` at the install, which asserts the refusal plus the absence of a journal row and of the colliding table. Also updates the evidence pack with this fix, and records that the pack's `cargo deny` / four-test caveats were re-checked: the four failures are environmental (root bypasses `0o000`), and `cargo deny`'s rustls advisory is pre-existing at `6712e7b0`. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Security fix + pack update —
|
| self.refuse_cross_topic_claims(&request.topic.id, &checked) | ||
| .await?; |
There was a problem hiding this comment.
The collision check only reads published topic documents, but installation applies migrations before the CLI publishes its document. Two prefix-overlapping topics can therefore install while neither is registered, or a failed publish can leave an installed topic permanently absent from later collision checks. Their migrations can then claim and modify the same shared-database object, bypassing the cross-topic isolation boundary. Reserve the namespace atomically across collision checking, migration application, and registration, including installed-but-unpublished topics.
How this was verified: Two unpublished topics,
aaandaa-b, both installed againstaa_b_scratchand left changes on the same table.
Artifacts
- Focused PostgreSQL reproduction script for unpublished prefix-overlapping installations.
- Captured successful execution showing both unpublished installations completed against the overlapping namespace.
- Database inspection showing the shared table contains changes from both installations.
| named.extend(proof_topic_sql_guard::referenced_objects( | ||
| &statement.blanked, | ||
| )); |
There was a problem hiding this comment.
The cross-topic detector extracts names only from the blanked statement text, while executable SQL function bodies are retained separately. A migration for aa can install a function containing DELETE FROM aa_b_scratch; with sibling aa-b registered, the install succeeds and the function can delete the sibling topic’s data. Include references from function bodies when checking cross-topic claims.
How this was verified: An installed
aafunction deleted the seeded row in registered siblingaa-b’saa_b_scratchtable.
Artifacts
- Creates a disposable PostgreSQL database, generates and runs the focused installer test, and shows that the current check omits function-body references.
- Captured execution of the focused PostgreSQL installer test showing the install passed and the installed function deleted the registered sibling's row, confirming the bypass.
The authorship pin said the RLM must author rules + SQL migrations + dynamic APIs + submission_format + pin_policy. The wire could only carry rules: the job surface was `VmJobOutput::Rules(Vec<ChecklistRule>)`, so the install had to take the other four parts from the operator's bundle. This closes that. **One document, five parts.** `proof-topic-authoring` is the new home of what a topic's RLM authors — the shape, the bounds, the canonical digests, and the tightening rule that makes a pin policy a policy (a topic may raise a floor, lower a ceiling, and never the reverse; three knobs are equalities, not tightenings, because every topic is measured against the same image and machine class). It holds no database and no VM, which is the point: the guest checks an answer before it becomes a job output, and the install checks it again before it applies anything, and both link the same code. **The wire carries the set.** `VmJobOutput::Authored(Box<TopicAuthoring>)` is what `ProposeRules` answers, and `VmJob` carries the set an RLM is re-authoring. `Rules(Vec<ChecklistRule>)` survives for an adaptor baked before the set existed — and it is treated as what it is: a **fragment**. The host records the rules with honest `rlm` provenance and refuses to treat the topic as set up, naming the parts that have no author. It never widens a fragment into a set, because the parts that would fill it are the operator's. **The install applies the RLM's set, and says who wrote what.** When the driver got one, `Installer` applies *it* and not the bundle's section: the declaration of intent is superseded by the topic's own answer. The journal's `binding.authorship` records `rlm` (or `topic_document`) per part, with each part's digest and, for migrations and routes, what landed — so "the RLM authored this topic" is a fact an audit reads back per part rather than a label on the row as a whole. **Fail-closed, three times over.** The guest holds an answer to the deny-list and to the document's own knobs before it answers; the setup driver refuses an incomplete set (`IncompleteAuthoring`, naming the parts) rather than proceeding; the install holds the set to the pin, which the guest cannot see. A migration reaching another topic's namespace, a route inside the admin namespace, a policy that loosens a global floor: each is refused where it arrives. **Two crates moved to make room, unchanged in behavior.** `proof-rlm-lifecycle` (the transition table and owner hooks, out of `proof-rlm`) and `proof-vm-staging` (the staging layer, out of `proof-vm-guest`) — both were at 1494/1495 of the repository's 1500-line per-crate cap, and the authoring surface needs the room. Both are re-exported under their old paths, so no caller changes. Tests: the guest's whole-set path (complete set travels; another topic's set, a denied migration, and an incomplete set are each refused by name; a rules-only answer stays a fragment), and an end-to-end install against a real Postgres where the operator's section and the RLM's set differ in every part, so a run that quietly applied the bundle would fail every assertion. The bundle guard flipped with the code, as its own doc comment said it would. Docs corrected where they claimed the old boundary: `docs/PROOF.md`, `docs/COMPLETENESS.md`, the bundle crate's header, and the guest's. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…ner run that proves it Rewrites the pack against the tip that closed the authorship gap. **Item 2 is now met in code, and the pack says where it is not yet live.** The gap pack v2 stated exactly (§2f: no job or wire message could carry a migration or a route) is closed: `VmJobOutput::Authored(TopicAuthoring)` carries all five parts, the install applies the RLM's set rather than the bundle's section, and the journal records `binding.authorship` per part with a digest each. §2d keeps Dev's live staging dig as it stands and says plainly that install #11 predates the change — its `migrations`/`apis` came from the bundle and its binding carries no `authorship` entry. §2g is the Owner LIVE run that moves the host onto the new shape, with the exact journal entry that proves it. **Item 3's distinction is stated rather than rounded up.** The RLM authors the topic's **behavior**; the **document** (statement, metric, floors, pack pin) stays the operator's, because miners need a signed document and only the operator holds the `proof` key. §3e is rewritten as a before/after table so the claim is legible, and the B1 FIXED YAML's status is explicit: it cannot produce an `authorship: rlm` row, because an install from a bundle records `topic_document` provenance and the publish gate refuses to open on it. **Item 4's inventory is measured, not asserted.** Two counts per module, both over production code: `raw` (comments included, what a reader greps) and `logic` (comments stripped, what the guard enforces). The earlier pack's "6 → 0" for `proof-admin` is reproduced as raw 6 → 0 / logic 2 → 0, and the two comment-only survivors in `proof-topic-ops` are now 0 raw as well — this tip made those doc examples generic too. **Two new caveats, both about this container rather than the change.** The `rustls` advisory is confirmed pre-existing (the lockfile's rustls entry is untouched; the diff adds only the three new crates), and six failures that appear only with `DATABASE_URL` set are in crates this branch does not touch (`git diff c842598 -- crates/db crates/gateway-store-pg` is empty). **New runbook:** `docs/runbooks/proof-rlm-authorship-install.md` is the Owner ceremony — preconditions, the `authoring.json` contract with every refusal it can produce, the four commands, the journal entry that proves it worked, and the rollback notes. It says explicitly that it is not a re-run of the B1 YAML and why that ceremony cannot satisfy it. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
@greptileai review Two new commits on top of
What to look at hardest:
Known and stated rather than hidden: |
…guard Both are P1, both reproduced before fixing, and both are in the same check — the one place that can see the whole registry. **1. Function bodies bypassed the collision check.** The check scanned `statement.blanked`, which blanks dollar-quoted bodies *along with* string literals — so object names inside a body were never compared to the registry. Greptile's reproduction: topic `aa` installs `CREATE FUNCTION aa_delete_sibling() … $$ DELETE FROM aa_b_scratch $$ LANGUAGE sql` while sibling `aa-b` is registered, the install completes, and calling the function deletes the sibling's rows. The body is what runs, so the body is what the check reads: `collision_in` scans `blanked` **and** `bodies`. **2. Unpublished installs were invisible, and the check was not atomic.** The check enumerated `proof_topic_version` only, so a topic that had installed but not yet published did not exist as far as it was concerned: `aa` and `aa-b` could both install and both create `aa_b_scratch`. A topic claims a namespace by installing into it, so the claim is read from the **journal** too. And under READ COMMITTED two concurrent installs each see no row from the other and both commit — the same defect with a race on top. Both close in one place. `Installer::claim_namespace` is a single transaction that takes `pg_advisory_xact_lock` (the pattern this repo already uses for the alias slug race), reads the union of `proof_topic_version` and `proof_topic_install`, and writes the `pending` row that records the claim **inside the same transaction** — so the next install to take the lock sees it. A refusal rolls back and writes nothing: no row, no rule, no table. The advisory lock is deliberately coarse — one lock for all installs — because installs are rare operator actions and a per-namespace lock would have to reason about prefix overlap, which is the very thing that is hard here. It is a transaction lock, so a crashed install cannot wedge it. **Tests, each verified non-vacuous.** Neutering the body scan fails `a_function_body_that_reaches_a_sibling_is_refused`; narrowing the registry read back to `proof_topic_version` fails `two_unpublished_installs_cannot_claim_the_same_object`. A third test races two installs and asserts exactly one is refused. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
@greptileai review Both P1s you found in the cross-topic SQL guard are fixed in 1. Function bodies. The check scanned 2. Unpublished installs + atomicity. The registry read enumerated Both close in one place:
A refusal rolls back, so it writes nothing at all — no row, no rule, no table. The lock is deliberately coarse (one for all installs) because installs are rare operator actions and a per-namespace lock would have to reason about prefix overlap, which is the very thing that is hard here. It is a transaction lock, so a crashed install cannot wedge it. Narrowing the registry read back to |
| let job = VmJob::ProposeRules { | ||
| topic: Box::new(topic.clone()), | ||
| current_version: current.as_ref().map(|r| r.version), | ||
| current: None, |
There was a problem hiding this comment.
Re-authoring loses authored behavior
After a topic already has a complete authored set, setup sends its prior rule version but always sets current to None. The guest then discards that field and does not provide any equivalent prior set to the adaptor. The RLM cannot preserve unchanged migrations, APIs, submission format, or pin policy, so a re-authoring run can unintentionally replace installed behavior while rebuilding the full set from scratch.
Artifacts
- Creates a temporary two-run integration test, executes it with Cargo, captures the full output, and removes the temporary test; it directly inspects the second dispatched proposal job.
- Cargo executed the focused two-run test successfully and printed that the second proposal carries version 1 while its current authored set is absent, confirming the protocol gap.
| "pin_policy" => { | ||
| if !value.is_object() { | ||
| return Err(bad( | ||
| "pin_policy", | ||
| format!("must be an object, got {}", kind(value)), | ||
| )); | ||
| } | ||
| plan.pin_policy_digest = Some(digest_of(value)); |
There was a problem hiding this comment.
A stricter RLM pin policy is accepted and recorded as a digest, but it is not carried into the effective scoring configuration. For example, an RLM can tighten the throughput improvement floor to 10%, yet promotion continues to use the signed document's 2% epsilon and promotes a 3% improvement. The journal can consequently represent a stricter authored policy that miners were not actually evaluated under. Persist and use the accepted policy for scoring, or stop recording it as an enforced tightening.
Greptile's P1 on the whole-set change, and it was right: the wire carried `VmJob::ProposeRules.current` but the driver always sent `None`. An adaptor that cannot read its previous set cannot **retain** the parts it is not changing, so a second authoring run was a rewrite from nothing — and the install would apply that lossy set, silently dropping a migration the topic still needs. Three places, because the set has to be stored, carried, and reachable: 1. **Stored.** `proof_topic_authoring` (migration 0027) keeps every set an RLM authored, versioned and append-only, with the canonical digest beside it. Read from the store rather than held in memory, so a restart — or a different operator process — is handed the same set. The version advances under an advisory lock, so two concurrent authoring runs see each other and report a `VersionGap` rather than racing into a duplicate-key error. 2. **Carried.** `TopicSetup::current_authoring` populates the job's `current`, and a topic with no prior set carries `None` — the first run is unchanged. 3. **Reachable by the adaptor.** The guest writes the previous set to `$PROOF_WORK_DIR/current-authoring.json` and exports its path as `PROOF_CURRENT_AUTHORING_FILE`: always set, empty when there is none, so an adaptor branches on one variable rather than on a variable's presence. **A fragment is not stored as the set in force.** A rules-only answer is persisted as *rules* (honest `rlm` provenance) and **not** as the authored set — storing it would hand the next run a set that was never authored, and the re-authoring path would treat a partial answer as the retention baseline. Tests, each verified non-vacuous: - `a_re_authoring_run_is_handed_the_set_the_first_one_wrote` — first run carries `None`, the set is persisted, the second carries **that** set complete, the store advances to v2. Neutering `current` back to `None` fails it. - `a_rules_only_answer_is_not_persisted_as_the_authored_set` — the fragment path: `IncompleteAuthoring`, no stored set, rules still stored as `rlm`. - `the_previous_set_survives_a_new_driver` — a different `TopicSetup` over the same store is handed the same set, so nothing depends on in-memory state. Docs: the Owner runbook documents the new adaptor input, and the evidence pack and completeness table say what re-authoring now guarantees. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
@greptileai review Your re-authoring P1 is fixed in The fix is in three places, because the set has to be stored, carried, and reachable by the adaptor:
One deliberate asymmetry: a rules-only answer is persisted as rules (honest Three tests, each verified non-vacuous:
|
… the document Two more Greptile P1s on the whole-set change, both reproduced before fixing. **1. A re-install did not remove the routes of the set it replaced.** `register_apis` was insert-only and `proof_topic_api` was `SELECT, INSERT` for the application role, so the table could only grow. When an RLM-authored install superseded a bundle-authored one, the bundle's routes were still there — and the mux loads **every** row for a topic, so a miner could still reach an endpoint the topic's current install does not declare while the journal said the newer set was in force. The install now **reconciles**, in one transaction: it deletes the topic's rows the new set does not claim, upserts the set's rows, and bumps the topic's route revision (migration 0028, which grants the DELETE this needs). The mux's change signal had to change with it: the generation probe was `count(*) FROM proof_topic_api`, and a count cannot see a replacement — delete one, insert one, and it is unchanged while the routes are not. It reads the sum of `proof_topic_route_revision` now: monotonic per topic, bumped in the same transaction as the reconciliation. **2. An accepted pin policy had no effect on scoring.** The check accepted a policy *tighter* than the signed document and the install recorded it, but scoring reads the document (`proof-score::nll_gates` takes `topic.epsilon_nll`; the split-regression gate takes `topic.epsilon_topic_max_regress`). So the host recorded the RLM's tighter number while challengers were judged by the document's — a threshold nobody is scored against, in either direction. `PinPolicy::agrees_with_document` (renamed from `tightens_document`) makes it an **equality**: a policy restates the document — proving its RLM considered the knob — and cannot choose for it. A topic that wants a tighter floor puts it in the document, where it is signed and verifiable. Tests, each verified non-vacuous: - `a_re_install_replaces_the_route_set` — bundle routes, then an RLM set with one different route: only the new route is in the table and the mux answers `NotRegistered` for both old paths. Neutering the delete shows all three, which is exactly the reported bug. - `a_same_count_replacement_still_moves_the_generation` — one route replaced by one route: the count is unchanged, the generation must still move. - `a_pin_policy_restates_the_signed_document_and_cannot_diverge` — restating accepted, diverging refused by name in both directions. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
@greptileai review Both new P1s are fixed in 1. Route replacement. The install now reconciles in one transaction: it deletes the topic's rows the new set does not claim, upserts the set's rows, and bumps the topic's route revision (migration Your point about the cache is the other half, and it needed fixing too: the generation probe was
2. Pin policy. You were right that an accepted policy had no effect: scoring reads the document (
|
| // a set that was never authored. The fragment's rules are already in | ||
| // the store above, with honest provenance. | ||
| if let Some(set) = authored.as_ref() { | ||
| self.store.put_authoring(&topic.id, set).await?; |
There was a problem hiding this comment.
When two re-authoring runs overlap, both can receive the same previously authored set. The later write is accepted after the first run has already stored a newer set, because the append operation assigns a new version without checking which version the RLM received. Readers then treat the stale later result as the current authored set, so migrations, APIs, or policy changes introduced by the earlier result can be absent from the next re-authoring run. Pass the delivered authoring version into a conditional append and reject it when it is no longer current, or serialize the complete read/run/write cycle.
Artifacts
- The authored Rust runner creates two stale re-authoring inputs, concurrently appends them through PgRlmStore, and reads the current result, demonstrating the missing base-version check.
- The authored shell command starts a disposable PostgreSQL 16 instance, primes the workspace migrations for SQLx compilation, and executes the isolated Rust reproduction.
- The executed PostgreSQL reproduction reports both jobs read version 1, their concurrent appends received versions 2 and 3, and version 3 became current, confirming the race is reachable.
- The authored Rust runner exercises the same read-then-append sequence against MemoryRlmStore and asserts that the later stale set is current.
- The authored shell command builds and runs the isolated in-memory reproduction without modifying repository source.
- The executed in-memory reproduction shows both reads at version 1, stored versions 2 and 3, and that the first result's migration is not retained in the current set.
- The executed existing proof-rlm-store contract suite passed for the memory store; its PostgreSQL portions were skipped because DATABASE_URL was unset, so it did not cover this authoring race.
- The first executed PostgreSQL attempt pulled the disposable image but failed SQLx macro compilation because the fresh database had not yet been migrated; the final command was corrected to prime migrations and then succeeded.
| if let Some(set) = authored.as_ref() { | ||
| self.store.put_authoring(&topic.id, set).await?; |
There was a problem hiding this comment.
If put_authoring fails after put_rules succeeds, the rules advance while the previous authored set remains current. The next retry then receives stale migrations, APIs, submission format, and pin policy alongside the newer rules. Persist and version-bind both records atomically, or prevent retries from treating an older authored set as current after this partial failure.
Greptile's follow-up on the re-authoring fix: the rules went through
`put_rules` and the set through `put_authoring`, two separate store
operations, so a failure between them would leave newer rules with the
**previous** set. A retry would then be handed a set whose rules are not the
ones in force — the two halves of one fact ("this topic's RLM authored *this*
at rule version *N*") disagreeing.
`RlmStore::put_authoring` now takes both and lands them in **one
transaction**:
- Postgres: one `BEGIN`, one advisory lock, the set insert, the rule insert
(with the same version check `put_rules` runs), one `COMMIT`. A vector that
does not advance rolls the set back with it.
- Memory: one lock, the same check, both pushes or neither.
`the_rules_and_the_set_land_in_one_write` pins it: a first run lands both at
v1; a second whose rules do not advance is refused and appends **no** set, so
the set in force stays the one that landed with the rules in force; the next
valid run advances both. `the_atomic_write_still_reports_rlm_provenance`
confirms the provenance read is unchanged.
The fragment path still uses `put_rules`: a rules-only answer is not a set, so
there is nothing to pair it with.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
@greptileai review Your follow-up is fixed in
The two halves are one fact — "this topic's RLM authored this at rule version N" — and the store can no longer hold one without the other.
The fragment path still uses
|
| sqlx::query( | ||
| "INSERT INTO proof_rule_version (topic_id, version, source, rules) \ | ||
| VALUES ($1, $2, $3, $4)", | ||
| ) | ||
| .bind(topic_id) | ||
| .bind(i32::try_from(rules.version).map_err(malformed)?) | ||
| .bind(source_str(rules.source)) | ||
| .bind(rules_json) | ||
| .execute(&mut *tx) | ||
| .await?; |
There was a problem hiding this comment.
A complete RLM authoring run on PostgreSQL inserts its rule version without the required digest column. PostgreSQL rejects the insert because digest is non-nullable and has no default, then rolls back the paired transaction. As a result, --drive-rlm cannot complete whole-topic authorship on the production backend.
| sqlx::query( | |
| "INSERT INTO proof_rule_version (topic_id, version, source, rules) \ | |
| VALUES ($1, $2, $3, $4)", | |
| ) | |
| .bind(topic_id) | |
| .bind(i32::try_from(rules.version).map_err(malformed)?) | |
| .bind(source_str(rules.source)) | |
| .bind(rules_json) | |
| .execute(&mut *tx) | |
| .await?; | |
| sqlx::query( | |
| "INSERT INTO proof_rule_version (topic_id, version, source, rules, digest) \ | |
| VALUES ($1, $2, $3, $4, $5)", | |
| ) | |
| .bind(topic_id) | |
| .bind(i32::try_from(rules.version).map_err(malformed)?) | |
| .bind(source_str(rules.source)) | |
| .bind(rules_json) | |
| .bind(rules.digest()) | |
| .execute(&mut *tx) | |
| .await?; |
Artifacts
- This authored shell script starts a fresh PostgreSQL 16 instance, applies the migrations, captures column metadata, generates and runs the focused integration test, and removes all temporary resources; it is the executable source for the validation.
- This executed PostgreSQL metadata query reports `digest|NO|<none>` for `proof_rule_version.digest`, showing it is NOT NULL and has no default.
- This executed integration-test output shows the missing-digest not-null violation and zero rows in both paired tables after rollback, confirming complete authoring is prevented.
Greptile caught a real bug in the previous commit's Postgres path: `proof_rule_version.digest` is `NOT NULL` and CHECK'd as 64 hex, and the new paired insert omitted it — so **every** complete authoring would have been rejected by the database. The memory store did not, because its `RuleSet` is the object itself; only the real database could say so. The insert now binds `rules.digest()`, exactly as `put_rules` does. **And the contract test now covers it.** `store_contract.rs` runs one contract against both stores, so the paired write is exercised against a real Postgres in CI — which is what makes this class of defect (a column the memory store does not have) a test failure rather than a production surprise. Reverting the digest bind fails it with the not-null violation, verbatim. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
@greptileai review Caught, fixed, and thank you — that was a real bug in code I had just written: The more useful fix is the test.
|
Greptile's last review (of `dc6ca1a4`) scores **5/5**: "Safe to merge; there are no outstanding blocking issues." The pack now carries the finding-to-fix table, so a reader does not have to reconstruct the review history: nine findings across this branch, each fixed in a named commit, each with a regression test that was verified non-vacuous by neutering the fix and watching the test fail. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Mathis LOCK OVERRIDE + Arch PIN — 100% RLM autonomous (2026-09-15)
DRAFT. DO NOT MERGE. No squash. No auto-merge.
Merge HOLD on #297 / #298 / #299 / #300 until Owner staging A→Z + Mathis GO.
Base: #300 HEAD
870a3b87553337a6c9a94d8bcecaf236a224c602(verified equal toorigin/droid/933f76bf-b1-raise-max-proof-deadline).Removes the three remaining places a topic's identity or behavior could come
from compiled code or an operator-cloned document instead of from the topic's
own RLM. Data-driven
topic_id/ install / pin only; the results contractis the generic
harbor-trials-v1.(1) Residual
tbench/TB4hardcode in challenge / gw / orch / guestCONTRACT_TBENCH_HARBORnamed a topicCONTRACT_HARBOR_TRIALS_LEGACY— the value stays (it is signed wire data a live document still pins; renaming the string would break a signed doc), the name stops naming a topic. New topics pinharbor-trials-v1.tb4.install-bundle.json/tb4.pin.tomltopic.install-bundle.json/topic.pin.toml, regenerated with a neutral slug (fixture-topic-v0), custom id (fixture_metric_v0), alias (fixture-alias), and display name.topic validate --bundle topic.json,ctx-clienttimeout rationale, the0024migration comment, and the bundle crate's alias docs no longer name a default topic.No product branch, default, or logic condition names a topic id — asserted by a
new repo-wide guard test (below).
(2) Operator-cloned
tb4TopicDocument as install SoT → RLM authors itThe defect. The guest
propose_ruleshad a fallback that echoed theoperator's signed
checklistback as a rule proposal, whileTopicSetup::propose_rulesstamped the resultRuleSource::Rlm. That is anoperator-cloned document presented to the store as RLM authorship: rule
version 1 would read
rlmfor a topic whose behavior nobody authored.The fix, fail-closed at four layers.
propose_rulesentrypoint is
Failed(503, no row, nothing scored); the signedchecklistkeeps its honest
topic_documentprovenance as version 1.RlmStore::current_rules_source+rlm_authored_rules(Pg and Memory) read
proof_rule_version.sourcedirectly, so the answerdoes not depend on rule bodies deserializing.
TopicSetup::propose_rulesre-reads the store after writingand refuses with
RulesNotRlmAuthored(naming the provenance) unless theversion in force is
rlm-sourced.PgInstallJournal::appliednow requires both anappliedinstall row and RLM-authored rules. Anopendocument whosebehavior nobody authored is a 409, not a live topic.
proof_topic_install::install_rulesdocuments that what it seeds istopic_documentprovenance — honest, and explicitly not a substitute.(3) Parallel hardwired admission → DB / install-driven open + scorable
load_topicsadmitted anopendocument straight from the operator'sPROOF_TOPICS_FILEinto the store, so it landed inopen_topics/scorable_topicswith no install and no RLM involvement — a file-driven pathparallel to the install journal.
On the live backend it now skips an
opendocument it cannot prove wasinstalled and RLM-authored, logging the provenance that blocked it. The host
keeps serving whatever else is installed; a topic whose behavior nobody
authored never becomes submitable. Sim is exempt (CI/local opt-in backend with
no install, no RLM, no topic VM) — applying the gate there would test the
fixture rather than the boundary.
scorable_topicsitself was already derived from the store + the family'sscorer; there is no compiled-in
scorable = [tbench]list anywhere in thetree (verified), and none is introduced.
Kept intact
VMS_PER_SUBMISSION = 1, untouched.custom_ids/ready_for_topic/FamilyMuxfamily wiring, thePROOF_VM_RUNNER_CUSTOM_IDSregistry (still empty by default), and thesigned-topic-as-source-of-truth model.
Guard test, proven non-vacuous
no_topic_id_is_compiled_into_the_product_branches(incrates/proof-topic-bundle) reads the challenge / gateway / orchestrator /guest product sources at compile time and asserts no topic id appears in
non-test logic. Proven to catch a real violation: injecting
pub const PROBE_TOPIC: &str = "tbench";intoproof-rlm/src/runner.rsnon-test code fails the test with a message naming the file and literal.
B1 first-5 is plumbing evidence only
No live-run claim is made here. Nothing in this branch asserts a scored
submission, a sealed baseline, or end-to-end payment.
Test plan
cargo fmt --all -- --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspace870a3b87(see below)cargo run -p xtask -- loc-capcargo run -p xtask -- consensus-lintcargo run -p xtask -- spec-checkcargo run -p xtask -- design-checkcargo run -p xtask -- external-docs-checkPre-existing failures on base, unchanged by this branch (all are
root-permission filesystem tests that cannot fail as root in this
environment):
agent_tests::deadline_cut_still_persists_work_tree,agent_tests::paid_run_fails_closed_when_work_tree_cannot_be_synced,artefact::tests::max_zip_numeric_id_fails_closed_when_a_topic_dir_cannot_be_read,tests::seed_pf_allocator_refuses_boot_when_a_topic_dir_cannot_be_read.Verified by running the suite on base
870a3b87and diffing failing testnames — identical sets.
cargo deny checkadvisories fails identically onbase (
yanked-not-detectedon the schnorrkel/arrayref waiver).New / updated tests
inspection_and_rule_proposals_go_through_the_adaptor_or_fail_closed— asserts the refusal instead of the removed echo.
a_live_host_admits_no_open_topic_it_cannot_prove_was_installedand
the_rlm_authorship_read_refuses_rather_than_guessing.EmptyJournalcase now asserts the refusal names bothhalves (install applied and RLM-authored rules).
proof-adminCLI: 22/22 green against the regenerated neutral fixture.Not verified here (needs Owner staging A→Z)
No live orchestrator, no KVM host, no Firecracker boot, no real RLM run, no
sealed baseline, no Lium rent. The RLM-authorship path is exercised against
fakes and the install journal; a real
--drive-rlminstall on staging is thenext step and is not claimed by this PR.
Update —
945e143f+1f47297e: the RLM authors the whole topicThe sections above closed the operator-cloned-document path and made the
rules the RLM's. This update closes the rest of the authorship pin: the
RLM now authors rules + SQL migrations + dynamic APIs + submission_format +
pin_policy, as one document, and the install applies that rather than
the operator's bundle section.
What changed
VmJobOutput::Rules(Vec<ChecklistRule>)— the only thing a job could answer about behaviorVmJobOutput::Authored(Box<TopicAuthoring>)— the whole set, five parts,deny_unknown_fieldsmigrations/apis/submission_formatcame from the operator's bundle and were applied verbatimauthoring.jsoninside the topic VM); the install applies the RLM's set when it exists, and the bundle's section only when it does not — with honesttopic_documentprovenance, which the publish gate refuses to open onbindingrecorded the executor onlybinding.authorshiprecords per-part provenance (rlm/topic_document) with each part's digest, plus the names that landed for migrations and routespin_policydid not existeval_image_digest,gpu_class,holdout_size)rlmprovenance, andSetupError::IncompleteAuthoringnames the parts that have no author. It is never widened from the bundleWhere the new checks run
against the document's own knobs — before the answer becomes a job
output.
pin (
set.validate_against_pin) — which the guest cannot see.Both link the new
proof-topic-authoringcrate, which is why they cannotdisagree. That crate also took over the section reader and the handler
allow-list (from
proof-topic-install), so the guest can link them too.Two crates moved for LOC room (behavior unchanged)
proof-rlmwas at 1494/1500 andproof-vm-guestat 1495/1500 of therepository's per-crate cap. The lifecycle (
proof-rlm-lifecycle) and thestaging layer (
proof-vm-staging) moved out; both are re-exported under theirold paths, so no caller changes.
New tests
agent_tests::the_rlm_authors_its_whole_set_and_a_fragment_is_named_as_one):a complete set travels as
Authored; a set for another topic, a deniedmigration, and an incomplete set are each refused by name; a rules-only
answer stays
Rules.(
install_engine::the_rlm_authored_set_is_what_an_install_applies): theoperator's section and the RLM's set differ in every part (different
table, route, and submission format), so a run that quietly applied the
bundle would fail every assertion; the journal is then read back per part,
and a re-install with no authored set is asserted to record
topic_document.the_rlm_authors_the_whole_set_not_just_rules— theprevious guard asserted the old boundary and its own doc comment said it
would fail when the RLM could emit more. It flipped with the code, as
designed.
Docs
docs/evidence/rlm-authorship-evidence.md— pack v3: item 2 rewritten from"the gap, stated exactly" to "the boundary, as it now stands", with the live
staging dig kept as it is and labelled as predating this change; item 4's
inventory measured two ways (
raw/logic); item 3's distinction (RLM ownsthe behavior, the operator signs the document) stated rather than
rounded up.
docs/runbooks/proof-rlm-authorship-install.md— the Owner LIVE ceremony:the
authoring.jsoncontract with every refusal it can produce, the fourcommands, the journal entry that proves it worked, and rollback notes. It
says explicitly that it is not a re-run of the B1 FIXED YAML, and why
that ceremony cannot satisfy it.
docs/PROOF.md,docs/COMPLETENESS.md, the bundle crate's header, and theguest's corrected where they claimed the old boundary.
Still not verified here (unchanged)
No live orchestrator, no KVM host, no Firecracker boot, no real RLM run, no
sealed baseline, no Lium rent. The authorship path is exercised against fakes
and a real Postgres running the real install path. Moving the live host
onto it is the Owner ceremony in the new runbook, and it is not claimed by
this PR.