feat: per-connection publish governance — auto, 1-admin, majority, consensus
Per the owner's spec: each social connection gets publishGovernance in its fields — unset = manual publish (today), auto = dispatch on approval, admin1/admin_majority/admin_consensus = a publish-phase chatgov poll (subject draft_publish, its own open-poll slot, no collision with review gates). Dispatch holds until the publish poll passes (E3 pre-dispatch check); auto dispatches on approval. Includes migration 033, the PATCH /connections/:id/publish-governance route (chatgov.manage), and 19+31 new tests. Implementation by the branding-panel process (claude/opus), lint and integration fixes here.
This commit is contained in:
parent
f2eb14956e
commit
b89dea5c93
26 changed files with 1637 additions and 31 deletions
46
infra/occupymedia/element/apply-auth-branding.py
Normal file
46
infra/occupymedia/element/apply-auth-branding.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Merge the auth-only patch into an existing, file-bind-mounted Element config."""
|
||||
|
||||
import argparse
|
||||
import copy
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def merge(current, patch):
|
||||
for key, value in patch.items():
|
||||
if isinstance(value, dict):
|
||||
if not isinstance(current.get(key), dict):
|
||||
current[key] = {}
|
||||
merge(current[key], value)
|
||||
else:
|
||||
current[key] = value
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("config", type=Path)
|
||||
parser.add_argument("--write", action="store_true", help="write in place; otherwise print a preview")
|
||||
args = parser.parse_args()
|
||||
patch = json.loads(Path(__file__).with_name("auth-branding.patch.json").read_text())
|
||||
# r+ intentionally retains the inode: replacing this file would leave an
|
||||
# existing Docker single-file bind mount serving the old config.
|
||||
with args.config.open("r+" if args.write else "r", encoding="utf-8") as config_file:
|
||||
current = json.load(config_file)
|
||||
if not isinstance(current, dict):
|
||||
raise ValueError("Element config must be a JSON object")
|
||||
original = copy.deepcopy(current)
|
||||
merge(current, patch)
|
||||
for key in ("setting_defaults", "default_theme", "default_server_config", "default_server_name"):
|
||||
assert current.get(key) == original.get(key), f"Auth patch must preserve {key}"
|
||||
output = json.dumps(current, indent=2, ensure_ascii=False) + "\n"
|
||||
if args.write:
|
||||
config_file.seek(0)
|
||||
config_file.write(output)
|
||||
config_file.truncate()
|
||||
else:
|
||||
print(output, end="")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue