fix(kanban): scope the delegated-child write fence to the lineage's board root

HERMES_DELEGATED_CHILD_CONTEXT=1 is deliberately carried into every shell/
execute_code subprocess a delegate_task child spawns (the fence must survive
exec so a grandchild `hermes kanban complete` cannot promote itself). But the
readers treated the bare flag as "fence every Kanban DB": kanban_db_connect
opened ANY board ?mode=ro and write_txn refused ANY mutation. A subagent
running a Kanban reproduction against a scratch HERMES_HOME therefore got a
silently read-only board with a misleading "descendants require an
initialized board" error; only one lane in the retrospective ever discovered
why (deleg_15dac332), every earlier kanban repro ran degraded.

The marker's value is now the fenced board ROOT (kanban_home() at spawn) and
readers deny only paths under that root or the dispatcher-pinned
HERMES_KANBAN_DB (kanban_path_is_fenced). In-process children and a legacy
"1" marker still fence everything; an inherited path marker is never
re-derived, so a grandchild that moved HERMES_HOME cannot unfence the real
board. Owner-gate tests (test_kanban_descendant_scope, cron env isolation,
kanban CLI exit status) are unchanged and green.
This commit is contained in:
teknium1
2026-09-14 22:46:06 -07:00
committed by Teknium
parent 9a49b3c984
commit 8a8c3634e8
8 changed files with 117 additions and 18 deletions
+2 -5
View File
@@ -233,12 +233,9 @@ def _is_delegated_child_cli_mutation(args: argparse.Namespace) -> bool:
return False
elif action not in _DELEGATED_CHILD_DENIED_ACTIONS:
return False
try:
from agent.delegation_context import is_delegated_child_process_context
from agent.delegation_context import kanban_path_is_fenced
return is_delegated_child_process_context()
except Exception:
return bool(os.environ.get("HERMES_DELEGATED_CHILD_CONTEXT"))
return kanban_path_is_fenced(kb.kanban_home()) or kanban_path_is_fenced(kb.kanban_db_path())
def _joined_words(words) -> Optional[str]: