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()