✨ feat(workflow): enforce auditable agent rules
This commit is contained in:
+55
-22
@@ -508,6 +508,32 @@ def extract_block_lines(text: str, start: str, end: str) -> list[str]:
|
||||
|
||||
_AGENTS_BLOCK_START = "<!-- playbook:agents:start -->"
|
||||
_AGENTS_BLOCK_END = "<!-- playbook:agents:end -->"
|
||||
_RULES_BLOCK_START = "<!-- playbook:rules:start -->"
|
||||
_RULES_BLOCK_END = "<!-- playbook:rules:end -->"
|
||||
|
||||
|
||||
def replace_marked_block(
|
||||
text: str,
|
||||
block: list[str],
|
||||
start_marker: str,
|
||||
end_marker: str,
|
||||
) -> str:
|
||||
"""Swap the first start..end marked region for ``block``, keeping the rest."""
|
||||
updated: list[str] = []
|
||||
in_block = False
|
||||
replaced = False
|
||||
for line in text.splitlines():
|
||||
if not replaced and line.strip() == start_marker:
|
||||
updated.extend(block)
|
||||
in_block = True
|
||||
replaced = True
|
||||
continue
|
||||
if in_block:
|
||||
if line.strip() == end_marker:
|
||||
in_block = False
|
||||
continue
|
||||
updated.append(line)
|
||||
return "\n".join(updated) + "\n"
|
||||
|
||||
|
||||
def preserve_agents_subblock(block: list[str], agents_text: str) -> list[str]:
|
||||
@@ -559,23 +585,10 @@ def update_agents_section(
|
||||
agents_text = agents_path.read_text(encoding="utf-8")
|
||||
if start_marker in agents_text:
|
||||
block = preserve_agents_subblock(block, agents_text)
|
||||
lines = agents_text.splitlines()
|
||||
updated: list[str] = []
|
||||
in_block = False
|
||||
replaced = False
|
||||
for line in lines:
|
||||
if not replaced and line.strip() == start_marker:
|
||||
updated.extend(block)
|
||||
in_block = True
|
||||
replaced = True
|
||||
continue
|
||||
if in_block:
|
||||
if line.strip() == end_marker:
|
||||
in_block = False
|
||||
continue
|
||||
updated.append(line)
|
||||
agents_path.write_text(
|
||||
"\n".join(updated) + "\n", encoding="utf-8", newline="\n"
|
||||
replace_marked_block(agents_text, block, start_marker, end_marker),
|
||||
encoding="utf-8",
|
||||
newline="\n",
|
||||
)
|
||||
log("Updated: AGENTS.md (section)")
|
||||
else:
|
||||
@@ -741,9 +754,6 @@ def sync_rules_action(config: dict, context: dict) -> int:
|
||||
|
||||
rules_dst = project_root / "AGENT_RULES.md"
|
||||
force = bool(config.get("force", False))
|
||||
if rules_dst.exists() and not force:
|
||||
log("AGENT_RULES.md already exists. Use force to overwrite.")
|
||||
return 0
|
||||
|
||||
project_name = resolve_project_name(context)
|
||||
playbook_scripts = resolve_playbook_scripts(context)
|
||||
@@ -751,13 +761,36 @@ def sync_rules_action(config: dict, context: dict) -> int:
|
||||
date_value = config.get("date") or datetime.now().strftime("%Y-%m-%d")
|
||||
no_backup = bool(config.get("no_backup", False))
|
||||
|
||||
backup_path(rules_dst, no_backup)
|
||||
text = rules_src.read_text(encoding="utf-8")
|
||||
text = replace_placeholders(
|
||||
text, project_name, date_value, playbook_scripts, playbook_root
|
||||
)
|
||||
rules_dst.write_text(text.rstrip("\n") + "\n", encoding="utf-8", newline="\n")
|
||||
log("Synced: AGENT_RULES.md")
|
||||
|
||||
if rules_dst.exists() and not force:
|
||||
# The process itself is Playbook-owned, so keep it upgradable: refresh the
|
||||
# marked block in place and leave anything the project added outside it.
|
||||
# Files predating the markers still need force, as before.
|
||||
block = extract_block_lines(text, _RULES_BLOCK_START, _RULES_BLOCK_END)
|
||||
existing = rules_dst.read_text(encoding="utf-8")
|
||||
if not block:
|
||||
log("Skip: rules markers not found in template")
|
||||
return 0
|
||||
if _RULES_BLOCK_START not in existing:
|
||||
log("AGENT_RULES.md has no playbook:rules block. Use force to overwrite.")
|
||||
return 0
|
||||
updated = replace_marked_block(
|
||||
existing, block, _RULES_BLOCK_START, _RULES_BLOCK_END
|
||||
)
|
||||
if updated == existing:
|
||||
log("Unchanged: AGENT_RULES.md (section)")
|
||||
else:
|
||||
backup_path(rules_dst, no_backup)
|
||||
rules_dst.write_text(updated, encoding="utf-8", newline="\n")
|
||||
log("Updated: AGENT_RULES.md (section)")
|
||||
else:
|
||||
backup_path(rules_dst, no_backup)
|
||||
rules_dst.write_text(text.rstrip("\n") + "\n", encoding="utf-8", newline="\n")
|
||||
log("Synced: AGENT_RULES.md")
|
||||
|
||||
local_rules = project_root / "AGENT_RULES.local.md"
|
||||
if not local_rules.exists():
|
||||
|
||||
Reference in New Issue
Block a user