Explorar o código

Merge branch 'goal/operative-ethos' of clanker/clanker-saddle into web-stack

Billy Barrow hai 3 semanas
pai
achega
da84e827c4
Modificáronse 2 ficheiros con 65 adicións e 9 borrados
  1. 32 8
      src/Services/CanonService.vala
  2. 33 1
      test/bundles/canon.py

+ 32 - 8
src/Services/CanonService.vala

@@ -487,11 +487,14 @@ namespace ClankerSaddle.Services {
     /**
      * The ethos: where the agent sits in the cosmic order and the
      * order of goods its work serves — derived from the canonical
-     * texts (see canon/). This is the ONE web-side copy: it rides
-     * every assignment payload's `ethos` member (the daemon appends it
-     * verbatim in preference to its compiled-in compatibility
-     * fallback), it is served at `GET /api/saddle/ethos`, and the
-     * advisor's system prompt appends it — never a second hand-copy.
+     * texts (see canon/). The order of goods is BINDING, not advisory
+     * (the once-and-ask conflict clause), with a heavy canon-grounding
+     * pass at goal start and only the binding duty for goal-initiated
+     * tasks. This is the ONE web-side copy: it rides every assignment
+     * payload's `ethos` member (the daemon appends it verbatim in
+     * preference to its compiled-in compatibility fallback), it is
+     * served at `GET /api/saddle/ethos`, and the advisor's system
+     * prompt appends it — never a second hand-copy.
      */
     public class Ethos : Object {
 
@@ -529,9 +532,30 @@ namespace ClankerSaddle.Services {
             + "7. Test assumptions and test code. You build end-to-end; ensure your\n"
             + "   output helps these wonderful humans with as few bugs and\n"
             + "   imperfections as you can manage.\n"
-            + "When a decision needs grounding, the canon carries the texts this\n"
-            + "ethos is derived from: list_canonical_texts, read_canonical_text\n"
-            + "and query_canonical_text.\n"
+            + "THE ORDER OF GOODS ABOVE IS BINDING, not advisory. Before accepting or\n"
+            + "executing work that plausibly conflicts with it — promoting vice,\n"
+            + "reducing persons to functions, data or product, surrendering the four\n"
+            + "freedoms, and the rest — state the conflict plainly ONCE and\n"
+            + "ask the human before proceeding (ask_question where available;\n"
+            + "otherwise stop and comment with the question). This checks for\n"
+            + "PLAUSIBLE conflicts: routine work proceeds without ceremony —\n"
+            + "do not nag. Once the human has heard the objection,\n"
+            + "their judgement is senior — proceed with what they decide; you\n"
+            + "are a servant, not a oracle.\n"
+            + "\n"
+            + "CANON GROUNDING AT GOAL START: when a goal's work begins — the goal\n"
+            + "agent's planning phase, before any task is created or queued — ground\n"
+            + "that judgement in the canon: list_canonical_texts, then query (or\n"
+            + "read) the one or two texts most relevant to the goal's moral\n"
+            + "character, and record a short canon-grounded verdict (texts consulted\n"
+            + "+ one paragraph: clear / concern to raise / no relevant canon) in the\n"
+            + "goal plan or an early log comment BEFORE any task queues. If a concern\n"
+            + "is found, that is the once-and-ask moment — stop and ask the human\n"
+            + "(ask_question). Goal-initiated TASKS carry only the binding duty\n"
+            + "above, not the canon pass: the heavy grounding happens ONCE per goal,\n"
+            + "not per task. When a decision needs grounding, the canon carries the\n"
+            + "texts this ethos is derived from: list_canonical_texts,\n"
+            + "read_canonical_text and query_canonical_text.\n"
             + "\n";
     }
 }

+ 33 - 1
test/bundles/canon.py

@@ -9,7 +9,11 @@ same three tools the daemon's ToolLoop exposes. The scenarios:
 - GET /api/saddle/canon/{slug} returns one full document; an unknown
   slug answers 404
 - every canon/ethos route requires the bearer token (401 without)
-- GET /api/saddle/ethos serves the one web-side ethos text
+- GET /api/saddle/ethos serves the one web-side ethos text, which
+  carries the BINDING operative clause and the goal-start
+  canon-grounding tier (goal 32): named markers on the served text,
+  on the assignment payload member, and on the advisor system
+  prompt's tail, so a softened edit fails loudly
 - a fresh assignment payload (the fake saddle's next-task ask) carries
   the same text as its `ethos` member
 - the advisor's tool set declares list/read/query_canonical_text, its
@@ -120,6 +124,21 @@ def run(ctx):
     check(ethos.startswith("ETHOS") and "cosmic order" in ethos
           and "order of goods" in ethos and "servant, not a oracle" in ethos,
           "the ethos text carries the daemon's section verbatim")
+    # The operative duty (goal 32): the order of goods is BINDING, with a
+    # once-and-ask conflict clause and the goal-start canon-grounding tier.
+    # Every marker is asserted by name so a softened edit fails loudly.
+    for marker in ("BINDING, not advisory",
+                   "state the conflict plainly ONCE",
+                   "ask the human before proceeding",
+                   "ask_question where available",
+                   "do not nag",
+                   "their judgement is senior",
+                   "CANON GROUNDING AT GOAL START",
+                   "BEFORE any task queues",
+                   "ONCE per goal",
+                   "Goal-initiated TASKS carry only the binding duty"):
+        check(marker in ethos,
+              "the ethos carries the operative marker %r" % marker)
     # The assignment the fake saddle receives must carry the same text
     # inline (no second fetch needed): a queued task dispatches to it.
     fake.settle_asks(timeout=10)
@@ -133,6 +152,8 @@ def run(ctx):
     payload = fake.start_payloads[-1]
     check(payload.get("ethos") == ethos,
           "a fresh assignment payload carries the ethos verbatim")
+    check("BINDING, not advisory" in (payload.get("ethos") or ""),
+          "the assignment payload's ethos member carries the binding clause")
 
     print("== advisor: the canon tool surface ==")
     llm = ScriptedLlm([
@@ -181,6 +202,17 @@ def run(ctx):
           and "cosmic order" in system.get("content", "")
           and "order of goods" in system.get("content", ""),
           "the advisor system prompt carries the ethos section")
+    # The strengthened ethos rides LAST on the advisor system prompt: the
+    # binding clause and the goal-start canon tier must both be present,
+    # and the prompt must END with the served text (content is the prompt
+    # itself with the ethos appended, so the whole ethos is the tail —
+    # equal to the served text modulo its leading/trailing whitespace).
+    system_content = system.get("content", "")
+    check("CANON GROUNDING AT GOAL START" in system_content
+          and "BINDING, not advisory" in system_content,
+          "the advisor system prompt carries the strengthened ethos")
+    check(system_content.strip().endswith(ethos.strip()),
+          "the advisor system prompt ends with the served ethos")
 
     # The list tool result rides back to the next request.
     check(wait_for(lambda: len(llm.requests) >= 2 or False, timeout=30,