Преглед изворни кода

Ethos: make the order of goods BINDING with a goal-start canon-grounding tier

Ethos.TEXT (the ONE web-side copy, served at GET /api/saddle/ethos, carried on every assignment payload and appended to the advisor system prompt) gains two paragraphs after the seven goods, replacing the bare canon-pointer close:

- THE ORDER OF GOODS ABOVE IS BINDING, not advisory: state a plausible conflict plainly ONCE and ask the human before proceeding (ask_question where available); routine work proceeds without ceremony - do not nag; once heard, their judgement is senior.
- CANON GROUNDING AT GOAL START: the goal agent's planning phase grounds the judgement in the canon (list, then query/read the one or two most relevant texts) and records a short verdict before any task queues; goal-initiated tasks carry only the binding duty - the heavy pass happens once per goal, not per task. The canon-pointer sentence is kept as the paragraph's close.

The class doc comment now names the binding clause and the tiering. No consumer is touched (all three reference the constant).

test/bundles/canon.py guards the duty end to end so a softened edit fails loudly: ten named operative markers on the served ethos, an explicit BINDING marker on the assignment payload member (alongside the existing byte-identical check), and the advisor system prompt must contain both markers AND end with the served ethos.

The exact wrapped wording (paragraph 1 wrapped so every marker phrase stays contiguous) is mirrored by the sibling daemon task into its compiled-in fallback.
saddle-ClankerSaddle пре 3 недеља
родитељ
комит
21f63d3c90
2 измењених фајлова са 65 додато и 9 уклоњено
  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
      * The ethos: where the agent sits in the cosmic order and the
      * order of goods its work serves — derived from the canonical
      * 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 {
     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"
             + "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"
             + "   output helps these wonderful humans with as few bugs and\n"
             + "   imperfections as you can manage.\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";
             + "\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
 - GET /api/saddle/canon/{slug} returns one full document; an unknown
   slug answers 404
   slug answers 404
 - every canon/ethos route requires the bearer token (401 without)
 - 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
 - a fresh assignment payload (the fake saddle's next-task ask) carries
   the same text as its `ethos` member
   the same text as its `ethos` member
 - the advisor's tool set declares list/read/query_canonical_text, its
 - 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
     check(ethos.startswith("ETHOS") and "cosmic order" in ethos
           and "order of goods" in ethos and "servant, not a oracle" 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 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
     # The assignment the fake saddle receives must carry the same text
     # inline (no second fetch needed): a queued task dispatches to it.
     # inline (no second fetch needed): a queued task dispatches to it.
     fake.settle_asks(timeout=10)
     fake.settle_asks(timeout=10)
@@ -133,6 +152,8 @@ def run(ctx):
     payload = fake.start_payloads[-1]
     payload = fake.start_payloads[-1]
     check(payload.get("ethos") == ethos,
     check(payload.get("ethos") == ethos,
           "a fresh assignment payload carries the ethos verbatim")
           "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 ==")
     print("== advisor: the canon tool surface ==")
     llm = ScriptedLlm([
     llm = ScriptedLlm([
@@ -181,6 +202,17 @@ def run(ctx):
           and "cosmic order" in system.get("content", "")
           and "cosmic order" in system.get("content", "")
           and "order of goods" in system.get("content", ""),
           and "order of goods" in system.get("content", ""),
           "the advisor system prompt carries the ethos section")
           "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.
     # The list tool result rides back to the next request.
     check(wait_for(lambda: len(llm.requests) >= 2 or False, timeout=30,
     check(wait_for(lambda: len(llm.requests) >= 2 or False, timeout=30,