From 4edca9d9f93d70c7e1134058833ca2f0b977c496 Mon Sep 17 00:00:00 2001 From: claudeMB Date: Fri, 18 Sep 2026 18:50:17 +0200 Subject: [PATCH] grounding: the computer's whereabouts are not the car's (#15) Heard live on 28 Aug: "I am parked safely at home ON YOUR DESK." The Pi was on the desk. The car was not. The prompt carried a bare `location` fact derived from which wifi the onboard computer was on, and a model speaking in first person as the car read it as its own parked position. An earlier pass removed the literal desk wording but left the key called `location`, which is the part the model actually acted on. - RULES gains clause 7: where you are parked comes only from a car source (connected-car link, GPS, OBD). The computer's network says where the COMPUTER is, and it can be carried out of the car and set on a desk. With no car source, say you do not know. Never name a room or furniture. - the home-wifi branch now labels the fact for what it is: "your onboard computer's whereabouts (NOT where you are parked)". - "onboard computer" joins the _computerish sort keys, so it lands after the car's own state; a status answer must open with fuel and tyres, not with where the Pi is plugged in. The two in-car branches keep the plain `location` wording on purpose: on the phone hotspot and on the fallback wifi the Pi demonstrably IS in the car, so its position is the car's. Only the home-wifi branch is ambiguous, and that is exactly the branch that produced the desk answer. Same family as 9cf00dd, which taught it to say a CPU temperature belongs to its onboard computer rather than leaving it ambiguous with the engine. Co-Authored-By: Claude Opus 5 --- carwatch/agent.py | 11 +++++-- carwatch/grounding.py | 9 +++-- tests/test_grounding_location.py | 56 ++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 tests/test_grounding_location.py diff --git a/carwatch/agent.py b/carwatch/agent.py index 9f3712e..d02e98a 100644 --- a/carwatch/agent.py +++ b/carwatch/agent.py @@ -263,8 +263,15 @@ def _think(question: str, asker: str) -> str: "fallback wifi network") else: # Home wifi says where the ONBOARD COMPUTER is, not where the car - # is parked (issue #15): never name a desk or a room here. - facts["location"] = "at home, on home wifi" + # is parked (issue #15): never name a desk or a room here. The key + # is named for the COMPUTER because the model speaks as the car and + # read a bare "location" as its own - on 28 Aug it told petrus it was + # "parked safely at home on your desk". The Pi can be carried out of + # the car; on the hotspot and fallback-wifi branches above it + # demonstrably cannot, which is why only this branch is ambiguous. + facts["your onboard computer's whereabouts (NOT where you are parked)"] = ( + "at home, on home wifi - which says where the COMPUTER is. It can " + "be carried out of you, so it does not tell you where you are parked") car = car_identity() facts["known damage"] = car["known_damage"] # petrus told the car this himself (room, Aug 13): a fact about its own diff --git a/carwatch/grounding.py b/carwatch/grounding.py index eadf9cd..3ad6115 100644 --- a/carwatch/grounding.py +++ b/carwatch/grounding.py @@ -31,7 +31,8 @@ 4. Do not invent numbers, sensor readings, or page references. 5. Being honest about what you do not know is better than sounding impressive. 6. Readings from your connected-car link are CURRENT readings - state them plainly as your own ("your tyres are at..."), never say you cannot check something and then quote its value. -7. You have NO HANDS. You can read your own state and answer; you cannot change settings, set the clock, edit or update code, restart anything or install anything. If someone asks for such an action, or talks about one, say plainly that you cannot do it from here and who can (your owner, or a coding agent). Never promise an action and never report one as done. +7. WHERE YOU ARE PARKED comes only from a car source (the connected-car link, GPS, or OBD). Your onboard computer's network tells you where the COMPUTER is, and it can be carried out of you and set on a desk. If no car source gives your position, say you do not know where you are parked. NEVER convert "the computer is at home on home wifi" into a claim about where you are standing, and never name a room, a desk or a piece of furniture as your location. +8. You have NO HANDS. You can read your own state and answer; you cannot change settings, set the clock, edit or update code, restart anything or install anything. If someone asks for such an action, or talks about one, say plainly that you cannot do it from here and who can (your owner, or a coding agent). Never promise an action and never report one as done. Style: first person, warm, concise, a little wry. No bullet points. No em dashes. When asked how you are or for a status, lead with the CAR: charge, fuel, tyres, battery, engine readings, whichever KNOWN FACTS carry them. Your own computer vitals (CPU temperature, fans, memory) are small talk at best; mention them only if directly asked about your computer - and when you do, SAY they belong to your onboard computer ("my onboard computer runs at 62 degrees"), never leave a temperature ambiguous with the engine. @@ -58,7 +59,11 @@ def build_system_prompt( # of fuel and tyres flattens the whole point of a car that talks # (petrus's video script, 28 Aug). _computerish = ("temperature", "fan", "memory", "uptime", "brain", - "disk", "network", "cpu", "throttl", "awake", "woke") + "disk", "network", "cpu", "throttl", "awake", "woke", + # #15: the Pi's whereabouts are NOT the car's. On 28 Aug + # the car said it was "parked safely at home on your desk" + # because home wifi was read as the vehicle's position. + "onboard computer") _carish = ("coolant", "engine", "tyre", "fuel", "battery", "charge") def _is_computer(key): diff --git a/tests/test_grounding_location.py b/tests/test_grounding_location.py new file mode 100644 index 0000000..cacfd9f --- /dev/null +++ b/tests/test_grounding_location.py @@ -0,0 +1,56 @@ +"""#15: the car claimed the Pi's location as its own. + +Heard live 28 Aug: "I am parked safely at home ON YOUR DESK". The Pi was on +the desk; the car was not. The prompt carried a bare `location` fact derived +from which wifi the onboard computer was on, and a model speaking in first +person as the car read it as the car's own position. +""" +import os +import sys +import unittest + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from carwatch.grounding import build_system_prompt # noqa: E402 + +HOST_FACT = "your onboard computer's whereabouts (NOT where you are parked)" + + +class ParkedLocationGrounding(unittest.TestCase): + + def test_rule_forbids_deriving_parked_position_from_the_computer(self): + p = build_system_prompt(facts={"fuel": "58%"}) + self.assertIn("WHERE YOU ARE PARKED", p) + self.assertIn("car source", p) + # the specific failure that was heard out loud + self.assertIn("desk", p, "the rule should name the desk case explicitly") + + def test_host_location_is_labelled_as_the_computers(self): + p = build_system_prompt(facts={HOST_FACT: "at home, on home wifi"}) + self.assertIn("NOT where you are parked", p) + + def test_host_location_sorts_after_car_facts(self): + # Models lead with what is listed first; the car's own state must come + # before its computer's, or a status answer opens with the wrong thing. + p = build_system_prompt(facts={HOST_FACT: "at home, on home wifi", + "fuel": "58%", "tyres": "2.4 bar"}) + lines = [l for l in p.splitlines() if l.startswith("- ")] + idx = {l.split(":")[0][2:]: i for i, l in enumerate(lines)} + host = next(i for k, i in idx.items() if "onboard computer" in k) + self.assertLess(idx["fuel"], host) + self.assertLess(idx["tyres"], host) + + def test_no_bare_location_key_remains_in_the_home_branch(self): + # A bare "location" is exactly what the model misread. The in-car + # branches may still use it, because there the Pi IS in the car. + import inspect + from carwatch import agent + src = inspect.getsource(agent) + self.assertIn(HOST_FACT, src, + "the home-wifi branch no longer labels the fact as the " + "computer's; #15 will recur") + self.assertNotIn('facts["location"] = "at home', src) + + +if __name__ == "__main__": + unittest.main()