<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://elite.mistflux.co.uk/w/index.php?action=history&amp;feed=atom&amp;title=User%3AEliteMaintenanceBot%2FGovernance_source</id>
	<title>User:EliteMaintenanceBot/Governance source - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://elite.mistflux.co.uk/w/index.php?action=history&amp;feed=atom&amp;title=User%3AEliteMaintenanceBot%2FGovernance_source"/>
	<link rel="alternate" type="text/html" href="https://elite.mistflux.co.uk/w/index.php?title=User:EliteMaintenanceBot/Governance_source&amp;action=history"/>
	<updated>2026-11-01T22:46:44Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.46.2</generator>
	<entry>
		<id>https://elite.mistflux.co.uk/w/index.php?title=User:EliteMaintenanceBot/Governance_source&amp;diff=138319&amp;oldid=prev</id>
		<title>Talondri: Protected &quot;User:EliteMaintenanceBot/Governance source&quot;: Preserve reviewed operational bot source ([Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite))</title>
		<link rel="alternate" type="text/html" href="https://elite.mistflux.co.uk/w/index.php?title=User:EliteMaintenanceBot/Governance_source&amp;diff=138319&amp;oldid=prev"/>
		<updated>2026-11-01T17:08:39Z</updated>

		<summary type="html">&lt;p&gt;Protected &amp;quot;&lt;a href=&quot;/wiki/User:EliteMaintenanceBot/Governance_source&quot; title=&quot;User:EliteMaintenanceBot/Governance source&quot;&gt;User:EliteMaintenanceBot/Governance source&lt;/a&gt;&amp;quot;: Preserve reviewed operational bot source ([Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite))&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw-interface=&quot;&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en-GB&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 17:08, 1 November 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;4&quot; class=&quot;diff-notice&quot; lang=&quot;en-GB&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;!-- diff cache key elitewiki:diff:1.41:old-138318:rev-138319 --&gt;
&lt;/table&gt;</summary>
		<author><name>Talondri</name></author>
	</entry>
	<entry>
		<id>https://elite.mistflux.co.uk/w/index.php?title=User:EliteMaintenanceBot/Governance_source&amp;diff=138318&amp;oldid=prev</id>
		<title>Talondri: Publish reviewed secret-free governance worker and authorised task scope</title>
		<link rel="alternate" type="text/html" href="https://elite.mistflux.co.uk/w/index.php?title=User:EliteMaintenanceBot/Governance_source&amp;diff=138318&amp;oldid=prev"/>
		<updated>2026-11-01T17:08:35Z</updated>

		<summary type="html">&lt;p&gt;Publish reviewed secret-free governance worker and authorised task scope&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Operator: [[User:Talondri|Talondri]] (site operator). This bounded task was explicitly authorised by the site operator. It uses the existing EliteMaintenanceBot account and existing restricted credentials. No new human account access was granted.&lt;br /&gt;
&lt;br /&gt;
The worker transfers complete, explicitly closed level-two discussions after at least 30 days, only with a dated recorded outcome and activity checks. It reports election deadlines from the two open-election categories. It never closes discussions, counts votes, declares results or grants rights. It honours bot exclusions and administrator-only JSON controls.&lt;br /&gt;
&lt;br /&gt;
Code review identified an archive-change race during edit pacing. The corrected worker rechecks the archive after pacing, immediately before source removal. Offline regression tests and isolated live user-subpage lifecycle tests were run. This records the authorised trial and review evidence, not a claim of wider community consensus.&lt;br /&gt;
&lt;br /&gt;
* [[User:EliteMaintenanceBot/Governance control|Administrator control]]&lt;br /&gt;
* [[User:EliteMaintenanceBot/Governance report|Deadline report]]&lt;br /&gt;
* [[Project:Bots|Local bot policy]]&lt;br /&gt;
&lt;br /&gt;
== Published worker ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;Bounded governance housekeeping. Authenticated dry-run unless --apply.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
from __future__ import annotations&lt;br /&gt;
&lt;br /&gt;
import argparse&lt;br /&gt;
from contextlib import contextmanager&lt;br /&gt;
from datetime import date, datetime, timezone, timedelta&lt;br /&gt;
import hashlib&lt;br /&gt;
import json&lt;br /&gt;
import os&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
import re&lt;br /&gt;
import sys&lt;br /&gt;
import tempfile&lt;br /&gt;
import time&lt;br /&gt;
&lt;br /&gt;
import mwparserfromhell as mw&lt;br /&gt;
&lt;br /&gt;
BOT = &amp;#039;EliteMaintenanceBot&amp;#039;&lt;br /&gt;
CONTROL = &amp;#039;User:EliteMaintenanceBot/Governance control&amp;#039;&lt;br /&gt;
REPORT = &amp;#039;User:EliteMaintenanceBot/Governance report&amp;#039;&lt;br /&gt;
TEST = &amp;#039;User:EliteMaintenanceBot/Governance tests/&amp;#039;&lt;br /&gt;
BOARDS = {&amp;quot;Project:&amp;quot; + s for s in (&lt;br /&gt;
    &amp;quot;Administrators&amp;#039; noticeboard&amp;quot;, &amp;quot;Bureaucrats&amp;#039; noticeboard&amp;quot;, &amp;#039;Requests for permissions&amp;#039;,&lt;br /&gt;
    &amp;#039;Requests for page protection&amp;#039;, &amp;#039;Bot requests&amp;#039;, &amp;#039;Village pump&amp;#039;, &amp;#039;Help desk&amp;#039;)}&lt;br /&gt;
CATEGORIES = (&amp;#039;Category:Open administrator elections&amp;#039;, &amp;#039;Category:Open bureaucrat elections&amp;#039;)&lt;br /&gt;
START, END = &amp;#039;&amp;lt;!-- EliteGovernance:report:start --&amp;gt;&amp;#039;, &amp;#039;&amp;lt;!-- EliteGovernance:report:end --&amp;gt;&amp;#039;&lt;br /&gt;
MAX_BYTES = 500_000&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class Halt(RuntimeError):&lt;br /&gt;
    pass&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def name(value):&lt;br /&gt;
    return str(value).strip().replace(&amp;#039;_&amp;#039;, &amp;#039; &amp;#039;).casefold().removeprefix(&amp;#039;template:&amp;#039;).strip()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def params(template):&lt;br /&gt;
    result = {}&lt;br /&gt;
    for p in template.params:&lt;br /&gt;
        key = name(p.name)&lt;br /&gt;
        if key in result:&lt;br /&gt;
            raise Halt(&amp;#039;Duplicate template parameters&amp;#039;)&lt;br /&gt;
        result[key] = str(p.value).strip()&lt;br /&gt;
    return result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def iso(value):&lt;br /&gt;
    if not isinstance(value, str) or not re.fullmatch(r&amp;#039;\d{4}-\d{2}-\d{2}&amp;#039;, value):&lt;br /&gt;
        raise ValueError(&amp;#039;Date requires YYYY-MM-DD&amp;#039;)&lt;br /&gt;
    return date.fromisoformat(value)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def optout(text):&lt;br /&gt;
    for t in mw.parse(text).filter_templates(recursive=True):&lt;br /&gt;
        n = name(t.name)&lt;br /&gt;
        if n == &amp;#039;nobots&amp;#039;:&lt;br /&gt;
            raise Halt(&amp;#039;Bot exclusion&amp;#039;)&lt;br /&gt;
        if n == &amp;#039;bots&amp;#039;:&lt;br /&gt;
            p = params(t)&lt;br /&gt;
            for key in (&amp;#039;deny&amp;#039;, &amp;#039;allow&amp;#039;, &amp;#039;optout&amp;#039;):&lt;br /&gt;
                if key not in p:&lt;br /&gt;
                    continue&lt;br /&gt;
                values = {name(v) for v in p[key].split(&amp;#039;,&amp;#039;)}&lt;br /&gt;
                if ((key == &amp;#039;deny&amp;#039; and values &amp;amp; {&amp;#039;all&amp;#039;, BOT.casefold()})&lt;br /&gt;
                        or (key == &amp;#039;allow&amp;#039; and not values &amp;amp; {&amp;#039;all&amp;#039;, BOT.casefold()})&lt;br /&gt;
                        or (key == &amp;#039;optout&amp;#039; and values &amp;amp; {&amp;#039;all&amp;#039;, &amp;#039;governance&amp;#039;, &amp;#039;archive&amp;#039;, &amp;#039;archiving&amp;#039;})):&lt;br /&gt;
                    raise Halt(&amp;#039;Bot exclusion&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def sections(text, today):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Only genuine top-level level-2 sections, terminated by level &amp;lt;=2 headings.&lt;br /&gt;
&lt;br /&gt;
    Parser nodes avoid headings/markers hidden in comments, nowiki, tags or templates.&lt;br /&gt;
    The marker must belong directly to the level-2 section, before any subheading.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nodes = mw.parse(text).nodes&lt;br /&gt;
    positions, offset = [], 0&lt;br /&gt;
    for i, node in enumerate(nodes):&lt;br /&gt;
        if isinstance(node, mw.nodes.Heading) and node.level &amp;lt;= 2:&lt;br /&gt;
            positions.append((i, offset, node.level))&lt;br /&gt;
        offset += len(str(node))&lt;br /&gt;
    positions.append((len(nodes), len(text), 0))&lt;br /&gt;
    eligible = []&lt;br /&gt;
    for current, following in zip(positions, positions[1:]):&lt;br /&gt;
        i, begin, level = current&lt;br /&gt;
        j, end, _ = following&lt;br /&gt;
        if level != 2:&lt;br /&gt;
            continue&lt;br /&gt;
        direct = []&lt;br /&gt;
        for node in nodes[i+1:j]:&lt;br /&gt;
            if isinstance(node, mw.nodes.Heading):&lt;br /&gt;
                break&lt;br /&gt;
            if isinstance(node, mw.nodes.Template) and name(node.name) == &amp;#039;governance closed&amp;#039;:&lt;br /&gt;
                direct.append(node)&lt;br /&gt;
        if len(direct) != 1:&lt;br /&gt;
            continue&lt;br /&gt;
        try:&lt;br /&gt;
            p = params(direct[0])&lt;br /&gt;
            if (not {&amp;#039;date&amp;#039;, &amp;#039;outcome&amp;#039;} &amp;lt;= set(p) or set(p) - {&amp;#039;date&amp;#039;, &amp;#039;outcome&amp;#039;, &amp;#039;by&amp;#039;}&lt;br /&gt;
                    or not p[&amp;#039;outcome&amp;#039;] or len(p[&amp;#039;outcome&amp;#039;]) &amp;gt; 1000):&lt;br /&gt;
                continue&lt;br /&gt;
            if (p[&amp;#039;outcome&amp;#039;].casefold() in {&amp;#039;unclear&amp;#039;, &amp;#039;reopened&amp;#039;, &amp;#039;open&amp;#039;, &amp;#039;pending&amp;#039;, &amp;#039;undecided&amp;#039;, &amp;#039;tbd&amp;#039;, &amp;#039;unknown&amp;#039;}&lt;br /&gt;
                    or re.search(r&amp;#039;[{}&amp;lt;&amp;gt;\n\r]&amp;#039;, p[&amp;#039;outcome&amp;#039;])):&lt;br /&gt;
                continue&lt;br /&gt;
            closed = iso(p[&amp;#039;date&amp;#039;])&lt;br /&gt;
        except (ValueError, Halt):&lt;br /&gt;
            continue&lt;br /&gt;
        content = text[begin:end]&lt;br /&gt;
        if any(name(t.name) in (&amp;#039;governance reopened&amp;#039;, &amp;#039;reopened&amp;#039;)&lt;br /&gt;
               for t in mw.parse(content).filter_templates(recursive=True)):&lt;br /&gt;
            continue&lt;br /&gt;
        if (today - closed).days &amp;gt;= 30 and not newer_signature(content, closed):&lt;br /&gt;
            eligible.append(dict(start=begin, end=end, text=content, closed=closed,&lt;br /&gt;
                                 month=today.strftime(&amp;#039;%Y-%m&amp;#039;)))&lt;br /&gt;
    return eligible&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def newer_signature(text, closed):&lt;br /&gt;
    # English wiki signatures plus ISO datetimes. Revision checks also cover unsigned edits.&lt;br /&gt;
    months = {m: i for i, m in enumerate((&amp;#039;January&amp;#039;, &amp;#039;February&amp;#039;, &amp;#039;March&amp;#039;, &amp;#039;April&amp;#039;, &amp;#039;May&amp;#039;,&lt;br /&gt;
              &amp;#039;June&amp;#039;, &amp;#039;July&amp;#039;, &amp;#039;August&amp;#039;, &amp;#039;September&amp;#039;, &amp;#039;October&amp;#039;, &amp;#039;November&amp;#039;, &amp;#039;December&amp;#039;), 1)}&lt;br /&gt;
    for match in re.finditer(r&amp;#039;\b\d{1,2}:\d{2}(?::\d{2})?,?\s+(\d{1,2})\s+(&amp;#039;&lt;br /&gt;
                             + &amp;#039;|&amp;#039;.join(months) + r&amp;#039;)\s+(\d{4})\s+\([^)]*\)&amp;#039;, text):&lt;br /&gt;
        try:&lt;br /&gt;
            d = date(int(match[3]), months[match[2]], int(match[1]))&lt;br /&gt;
        except ValueError:&lt;br /&gt;
            return True&lt;br /&gt;
        if d &amp;gt; closed:&lt;br /&gt;
            return True&lt;br /&gt;
    for match in re.finditer(r&amp;#039;\b(\d{4}-\d{2}-\d{2})[T ]\d{2}:\d{2}&amp;#039;, text):&lt;br /&gt;
        try:&lt;br /&gt;
            if iso(match[1]) &amp;gt; closed:&lt;br /&gt;
                return True&lt;br /&gt;
        except ValueError:&lt;br /&gt;
            return True&lt;br /&gt;
    return False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def election(text, today):&lt;br /&gt;
    ts = [t for t in mw.parse(text).filter_templates(recursive=False)&lt;br /&gt;
          if name(t.name) == &amp;#039;governance election&amp;#039;]&lt;br /&gt;
    if len(ts) != 1:&lt;br /&gt;
        return &amp;#039;needsdate&amp;#039;&lt;br /&gt;
    try:&lt;br /&gt;
        p = params(ts[0])&lt;br /&gt;
    except Halt:&lt;br /&gt;
        return &amp;#039;needsdate&amp;#039;&lt;br /&gt;
    if p.get(&amp;#039;status&amp;#039;, &amp;#039;&amp;#039;).casefold() == &amp;#039;closed&amp;#039;:&lt;br /&gt;
        return &amp;#039;closed&amp;#039;&lt;br /&gt;
    try:&lt;br /&gt;
        closes = iso(p.get(&amp;#039;closes&amp;#039;))&lt;br /&gt;
    except (ValueError, TypeError):&lt;br /&gt;
        return &amp;#039;needsdate&amp;#039;&lt;br /&gt;
    if p.get(&amp;#039;status&amp;#039;, &amp;#039;&amp;#039;).casefold() != &amp;#039;open&amp;#039;:&lt;br /&gt;
        return &amp;#039;needsreview&amp;#039;&lt;br /&gt;
    if p.get(&amp;#039;role&amp;#039;, &amp;#039;&amp;#039;).casefold() not in (&amp;#039;administrator&amp;#039;, &amp;#039;bureaucrat&amp;#039;) or not p.get(&amp;#039;candidate&amp;#039;):&lt;br /&gt;
        return &amp;#039;needsreview&amp;#039;&lt;br /&gt;
    return &amp;#039;overdue&amp;#039; if closes &amp;lt; today else &amp;#039;notdue&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def nomination_title(title):&lt;br /&gt;
    return &amp;#039;/&amp;#039; in title and &amp;#039;preload&amp;#039; not in title.casefold().split(&amp;#039;/&amp;#039;)[1:]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def validate(c, test=False):&lt;br /&gt;
    if not isinstance(c, dict) or set(c) != {&amp;#039;version&amp;#039;, &amp;#039;enabled&amp;#039;, &amp;#039;boards&amp;#039;, &amp;#039;report_page&amp;#039;}:&lt;br /&gt;
        raise Halt(&amp;#039;Control schema requires version, enabled, boards, report_page&amp;#039;)&lt;br /&gt;
    if type(c[&amp;#039;version&amp;#039;]) is not int or c[&amp;#039;version&amp;#039;] != 1 or type(c[&amp;#039;enabled&amp;#039;]) is not bool:&lt;br /&gt;
        raise Halt(&amp;#039;Invalid control version/enabled&amp;#039;)&lt;br /&gt;
    if not isinstance(c[&amp;#039;boards&amp;#039;], list) or len(c[&amp;#039;boards&amp;#039;]) &amp;gt; 7:&lt;br /&gt;
        raise Halt(&amp;#039;At most seven explicit boards&amp;#039;)&lt;br /&gt;
    seen = set()&lt;br /&gt;
    for b in c[&amp;#039;boards&amp;#039;]:&lt;br /&gt;
        if not isinstance(b, dict) or set(b) != {&amp;#039;title&amp;#039;, &amp;#039;archive_prefix&amp;#039;}:&lt;br /&gt;
            raise Halt(&amp;#039;Invalid board schema&amp;#039;)&lt;br /&gt;
        t = b[&amp;#039;title&amp;#039;]&lt;br /&gt;
        allowed = isinstance(t, str) and (t in BOARDS if not test else&lt;br /&gt;
                    t.startswith(TEST) and bool(re.fullmatch(r&amp;#039;[A-Za-z0-9 /-]+&amp;#039;, t[len(TEST):])))&lt;br /&gt;
        if not allowed or t in seen or b[&amp;#039;archive_prefix&amp;#039;] != t + &amp;#039;/Archive&amp;#039;:&lt;br /&gt;
            raise Halt(&amp;#039;Board not allowed, duplicate or incorrect archive prefix&amp;#039;)&lt;br /&gt;
        seen.add(t)&lt;br /&gt;
    if (not test and c[&amp;#039;report_page&amp;#039;] != REPORT) or (test and c[&amp;#039;report_page&amp;#039;] != TEST + &amp;#039;Report&amp;#039;):&lt;br /&gt;
        raise Halt(&amp;#039;Report outside allowed output&amp;#039;)&lt;br /&gt;
    return c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def identity(title, section):&lt;br /&gt;
    # Content-addressed receipt survives a source-revision change and uncertain saves.&lt;br /&gt;
    return hashlib.sha256((title + &amp;#039;\0&amp;#039; + section).encode()).hexdigest()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def block(title, section):&lt;br /&gt;
    key = identity(title, section)&lt;br /&gt;
    return (f&amp;#039;&amp;lt;!-- EliteGovernance:archive:{key}:start --&amp;gt;\n&amp;#039;&lt;br /&gt;
            f&amp;#039;From [[{title}]], closure explicitly marked by an editor.\n&amp;#039;&lt;br /&gt;
            + section + (&amp;#039;&amp;#039; if section.endswith(&amp;#039;\n&amp;#039;) else &amp;#039;\n&amp;#039;)&lt;br /&gt;
            + f&amp;#039;&amp;lt;!-- EliteGovernance:archive:{key}:end --&amp;gt;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class Worker:&lt;br /&gt;
    def __init__(self, site, apply=False, test=False):&lt;br /&gt;
        self.site, self.apply, self.test = site, apply, test&lt;br /&gt;
        self.control_title = TEST + &amp;#039;Control&amp;#039; if test else CONTROL&lt;br /&gt;
        self.gate = None&lt;br /&gt;
        self.edits, self.last_write = 0, 0.0&lt;br /&gt;
&lt;br /&gt;
    def api(self, **kw):&lt;br /&gt;
        return self.site.simple_request(maxlag=5, **kw).submit()&lt;br /&gt;
&lt;br /&gt;
    def snapshot(self, title):&lt;br /&gt;
        d = self.api(action=&amp;#039;query&amp;#039;, titles=title, prop=&amp;#039;info|revisions&amp;#039;, inprop=&amp;#039;protection&amp;#039;,&lt;br /&gt;
                     rvslots=&amp;#039;main&amp;#039;, rvprop=&amp;#039;ids|timestamp|content&amp;#039;, curtimestamp=1)&lt;br /&gt;
        q = d[&amp;#039;query&amp;#039;]&lt;br /&gt;
        pages = list(q.get(&amp;#039;pages&amp;#039;, {}).values())&lt;br /&gt;
        if q.get(&amp;#039;interwiki&amp;#039;) or len(pages) != 1:&lt;br /&gt;
            raise Halt(&amp;#039;Ambiguous or nonlocal page&amp;#039;)&lt;br /&gt;
        p = pages[0]&lt;br /&gt;
        if any(k in p for k in (&amp;#039;invalid&amp;#039;, &amp;#039;redirect&amp;#039;)):&lt;br /&gt;
            raise Halt(&amp;#039;Redirect or invalid page&amp;#039;)&lt;br /&gt;
        r = p.get(&amp;#039;revisions&amp;#039;, [{}])[0]&lt;br /&gt;
        text = r.get(&amp;#039;slots&amp;#039;, {}).get(&amp;#039;main&amp;#039;, {}).get(&amp;#039;*&amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
        if not isinstance(text, str) or len(text.encode()) &amp;gt; MAX_BYTES:&lt;br /&gt;
            raise Halt(&amp;#039;Page exceeds 500 KB bound&amp;#039;)&lt;br /&gt;
        return dict(title=p[&amp;#039;title&amp;#039;], text=text, revid=r.get(&amp;#039;revid&amp;#039;, 0),&lt;br /&gt;
                    timestamp=r.get(&amp;#039;timestamp&amp;#039;), start=d[&amp;#039;curtimestamp&amp;#039;],&lt;br /&gt;
                    protection=p.get(&amp;#039;protection&amp;#039;, []), missing=&amp;#039;missing&amp;#039; in p,&lt;br /&gt;
                    model=p.get(&amp;#039;contentmodel&amp;#039;))&lt;br /&gt;
&lt;br /&gt;
    def control(self):&lt;br /&gt;
        s = self.snapshot(self.control_title)&lt;br /&gt;
        for kind in (&amp;#039;edit&amp;#039;, &amp;#039;move&amp;#039;):&lt;br /&gt;
            if not any(p.get(&amp;#039;type&amp;#039;) == kind and p.get(&amp;#039;level&amp;#039;) == &amp;#039;sysop&amp;#039;&lt;br /&gt;
                       and p.get(&amp;#039;expiry&amp;#039;) in (&amp;#039;infinity&amp;#039;, &amp;#039;infinite&amp;#039;, &amp;#039;indefinite&amp;#039;)&lt;br /&gt;
                       for p in s[&amp;#039;protection&amp;#039;]):&lt;br /&gt;
                raise Halt(&amp;#039;Control requires permanent sysop edit and move protection&amp;#039;)&lt;br /&gt;
        if s[&amp;#039;model&amp;#039;] != &amp;#039;json&amp;#039;:&lt;br /&gt;
            raise Halt(&amp;#039;Control requires JSON content model&amp;#039;)&lt;br /&gt;
        try:&lt;br /&gt;
            c = validate(json.loads(s[&amp;#039;text&amp;#039;]), self.test)&lt;br /&gt;
        except (ValueError, TypeError):&lt;br /&gt;
            raise Halt(&amp;#039;Invalid control JSON&amp;#039;) from None&lt;br /&gt;
        if not c[&amp;#039;enabled&amp;#039;]:&lt;br /&gt;
            raise Halt(&amp;#039;Control disabled&amp;#039;)&lt;br /&gt;
        if self.gate is not None and any(s[k] != self.gate[k] for k in (&amp;#039;text&amp;#039;, &amp;#039;revid&amp;#039;, &amp;#039;protection&amp;#039;)):&lt;br /&gt;
            raise Halt(&amp;#039;Control changed during cycle&amp;#039;)&lt;br /&gt;
        self.gate = s&lt;br /&gt;
        return c&lt;br /&gt;
&lt;br /&gt;
    def save(self, old, text, summary, prerequisite=None):&lt;br /&gt;
        # MediaWiki strips trailing page whitespace during its pre-save transform.&lt;br /&gt;
        # Canonicalise before comparison; receipt-delimited section bytes stay intact.&lt;br /&gt;
        text = text.rstrip()&lt;br /&gt;
        if not self.apply:&lt;br /&gt;
            raise Halt(&amp;#039;Write attempted during dry-run&amp;#039;)&lt;br /&gt;
        if text == old[&amp;#039;text&amp;#039;]:&lt;br /&gt;
            return old&lt;br /&gt;
        if self.edits &amp;gt;= 12 or len(text.encode()) &amp;gt; MAX_BYTES:&lt;br /&gt;
            raise Halt(&amp;#039;Edit or page-size bound reached&amp;#039;)&lt;br /&gt;
        optout(old[&amp;#039;text&amp;#039;])&lt;br /&gt;
        # Pace before the final observations, never sleep with a stale guard.&lt;br /&gt;
        time.sleep(max(0, 15 - (time.monotonic() - self.last_write)))&lt;br /&gt;
        token = self.site.tokens[&amp;#039;csrf&amp;#039;]&lt;br /&gt;
        self.control()&lt;br /&gt;
        fresh = self.snapshot(old[&amp;#039;title&amp;#039;])&lt;br /&gt;
        if any(fresh[k] != old[k] for k in (&amp;#039;text&amp;#039;, &amp;#039;revid&amp;#039;, &amp;#039;protection&amp;#039;, &amp;#039;missing&amp;#039;)):&lt;br /&gt;
            raise Halt(&amp;#039;Target changed before save&amp;#039;)&lt;br /&gt;
        optout(fresh[&amp;#039;text&amp;#039;])&lt;br /&gt;
        # Cross-page prerequisite must run AFTER pacing and final source observation.&lt;br /&gt;
        # The edit request immediately follows; never sleep after this check.&lt;br /&gt;
        if prerequisite is not None:&lt;br /&gt;
            prerequisite()&lt;br /&gt;
        kw = dict(action=&amp;#039;edit&amp;#039;, title=old[&amp;#039;title&amp;#039;], text=text, summary=summary,&lt;br /&gt;
                  token=token, bot=1, assertuser=BOT, starttimestamp=old[&amp;#039;start&amp;#039;])&lt;br /&gt;
        if old[&amp;#039;missing&amp;#039;]:&lt;br /&gt;
            kw[&amp;#039;createonly&amp;#039;] = 1&lt;br /&gt;
        else:&lt;br /&gt;
            # Use revid alone: basetimestamp permits self-conflicts on some MW versions.&lt;br /&gt;
            kw.update(nocreate=1, baserevid=old[&amp;#039;revid&amp;#039;])&lt;br /&gt;
        self.edits += 1&lt;br /&gt;
        self.last_write = time.monotonic()&lt;br /&gt;
        try:&lt;br /&gt;
            result = self.api(**kw)[&amp;#039;edit&amp;#039;]&lt;br /&gt;
        except Exception:&lt;br /&gt;
            raise Halt(&amp;#039;Save conflict or uncertain result; inspect pages before retry; no further edits&amp;#039;) from None&lt;br /&gt;
        if result.get(&amp;#039;result&amp;#039;) != &amp;#039;Success&amp;#039;:&lt;br /&gt;
            raise Halt(&amp;#039;Save not confirmed&amp;#039;)&lt;br /&gt;
        check = self.snapshot(old[&amp;#039;title&amp;#039;])&lt;br /&gt;
        if check[&amp;#039;text&amp;#039;] != text or check[&amp;#039;revid&amp;#039;] != result.get(&amp;#039;newrevid&amp;#039;):&lt;br /&gt;
            raise Halt(&amp;#039;Save verification failed; no further edits&amp;#039;)&lt;br /&gt;
        return check&lt;br /&gt;
&lt;br /&gt;
    def archive_one(self, title, prefix, today):&lt;br /&gt;
        src = self.snapshot(title)&lt;br /&gt;
        if src[&amp;#039;missing&amp;#039;]:&lt;br /&gt;
            return &amp;#039;missing&amp;#039;&lt;br /&gt;
        optout(src[&amp;#039;text&amp;#039;])&lt;br /&gt;
        eligible = sections(src[&amp;#039;text&amp;#039;], today)&lt;br /&gt;
        if not eligible:&lt;br /&gt;
            return &amp;#039;unchanged&amp;#039;&lt;br /&gt;
        section = eligible[0]&lt;br /&gt;
        if not self.quiet_since(src, section):&lt;br /&gt;
            return &amp;#039;recent-or-unverifiable-activity&amp;#039;&lt;br /&gt;
        # Identical repeated sections cannot be safely distinguished by content receipts.&lt;br /&gt;
        if src[&amp;#039;text&amp;#039;].count(section[&amp;#039;text&amp;#039;]) != 1:&lt;br /&gt;
            raise Halt(&amp;#039;Ambiguous duplicate source section&amp;#039;)&lt;br /&gt;
        target = self.intent(src, section, src[&amp;#039;title&amp;#039;] + &amp;#039;/Archive &amp;#039; + section[&amp;#039;month&amp;#039;])&lt;br /&gt;
        dest = self.snapshot(target)&lt;br /&gt;
        optout(dest[&amp;#039;text&amp;#039;])&lt;br /&gt;
        payload = block(src[&amp;#039;title&amp;#039;], section[&amp;#039;text&amp;#039;])&lt;br /&gt;
        receipt = &amp;#039;EliteGovernance:archive:&amp;#039; + identity(src[&amp;#039;title&amp;#039;], section[&amp;#039;text&amp;#039;])&lt;br /&gt;
        if receipt in dest[&amp;#039;text&amp;#039;] and (dest[&amp;#039;text&amp;#039;].count(payload) != 1&lt;br /&gt;
                                       or dest[&amp;#039;text&amp;#039;].count(receipt) != 2):&lt;br /&gt;
            raise Halt(&amp;#039;Archive receipt exists but differs; source retained&amp;#039;)&lt;br /&gt;
        if not self.apply:&lt;br /&gt;
            return &amp;#039;would-archive&amp;#039;&lt;br /&gt;
        if payload not in dest[&amp;#039;text&amp;#039;]:&lt;br /&gt;
            # Observe source again before making an archive copy.&lt;br /&gt;
            current = self.snapshot(title)&lt;br /&gt;
            if current[&amp;#039;revid&amp;#039;] != src[&amp;#039;revid&amp;#039;] or current[&amp;#039;text&amp;#039;] != src[&amp;#039;text&amp;#039;]:&lt;br /&gt;
                raise Halt(&amp;#039;Source changed before archive; retained&amp;#039;)&lt;br /&gt;
            dest = self.save(dest, dest[&amp;#039;text&amp;#039;] + (&amp;#039;\n&amp;#039; if dest[&amp;#039;text&amp;#039;] else &amp;#039;&amp;#039;) + payload,&lt;br /&gt;
                             &amp;#039;Archive from [[&amp;#039; + src[&amp;#039;title&amp;#039;] + &amp;#039;]] oldid=&amp;#039; + str(src[&amp;#039;revid&amp;#039;])&lt;br /&gt;
                             + &amp;#039;; author/timestamp attribution in source history&amp;#039;)&lt;br /&gt;
        # Critical ordering: archive content is reread and verified before source removal.&lt;br /&gt;
        verified = self.snapshot(dest[&amp;#039;title&amp;#039;])&lt;br /&gt;
        optout(verified[&amp;#039;text&amp;#039;])&lt;br /&gt;
        if verified[&amp;#039;text&amp;#039;].count(payload) != 1:&lt;br /&gt;
            raise Halt(&amp;#039;Archive copy not verified; source retained&amp;#039;)&lt;br /&gt;
        def still_archived():&lt;br /&gt;
            final = self.snapshot(dest[&amp;#039;title&amp;#039;])&lt;br /&gt;
            optout(final[&amp;#039;text&amp;#039;])&lt;br /&gt;
            if (final[&amp;#039;missing&amp;#039;] or final[&amp;#039;revid&amp;#039;] != verified[&amp;#039;revid&amp;#039;]&lt;br /&gt;
                    or final[&amp;#039;text&amp;#039;] != verified[&amp;#039;text&amp;#039;] or final[&amp;#039;text&amp;#039;].count(payload) != 1):&lt;br /&gt;
                raise Halt(&amp;#039;Archive changed before source removal; source retained&amp;#039;)&lt;br /&gt;
        self.save(src, src[&amp;#039;text&amp;#039;][:section[&amp;#039;start&amp;#039;]] + src[&amp;#039;text&amp;#039;][section[&amp;#039;end&amp;#039;]:],&lt;br /&gt;
                  &amp;#039;Move verified closed discussion to [[&amp;#039; + dest[&amp;#039;title&amp;#039;] + &amp;#039;]]&amp;#039;,&lt;br /&gt;
                  prerequisite=still_archived)&lt;br /&gt;
        self.intent(src, section, target, complete=True)&lt;br /&gt;
        return &amp;#039;archived&amp;#039;&lt;br /&gt;
&lt;br /&gt;
    def intent(self, src, section, target, complete=False):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;Persist before archive write; retries across month boundaries use that copy.&lt;br /&gt;
&lt;br /&gt;
        Separate test journal, &amp;lt;=7 pending boards. If content changes after a partial&lt;br /&gt;
        transfer, retain both copies and require human inspection rather than guessing.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        path = Path(os.environ.get(&amp;#039;GOVERNANCE_STATE_DIR&amp;#039;, &amp;#039;/var/lib/elitewiki-governance&amp;#039;)) / (&lt;br /&gt;
            &amp;#039;test-intents.json&amp;#039; if self.test else &amp;#039;intents.json&amp;#039;)&lt;br /&gt;
        try:&lt;br /&gt;
            data = json.loads(path.read_text())[&amp;#039;intents&amp;#039;] if path.exists() else {}&lt;br /&gt;
            if not isinstance(data, dict) or len(data) &amp;gt; 7:&lt;br /&gt;
                raise ValueError()&lt;br /&gt;
        except (ValueError, KeyError, TypeError):&lt;br /&gt;
            raise Halt(&amp;#039;Invalid durable intents; inspect state before retry&amp;#039;) from None&lt;br /&gt;
        key, digest = src[&amp;#039;title&amp;#039;], identity(src[&amp;#039;title&amp;#039;], section[&amp;#039;text&amp;#039;])&lt;br /&gt;
        previous = data.get(key)&lt;br /&gt;
        if previous:&lt;br /&gt;
            if not re.fullmatch(re.escape(src[&amp;#039;title&amp;#039;] + &amp;#039;/Archive &amp;#039;) + r&amp;#039;\d{4}-\d{2}&amp;#039;, previous.get(&amp;#039;target&amp;#039;, &amp;#039;&amp;#039;)):&lt;br /&gt;
                raise Halt(&amp;#039;Pending archive content differs; human inspection required&amp;#039;)&lt;br /&gt;
            if previous.get(&amp;#039;hash&amp;#039;) != digest:&lt;br /&gt;
                # Recover a crash after successful source removal but before clearing&lt;br /&gt;
                # the intent. Never clear an intent while its original section remains.&lt;br /&gt;
                prior_text = previous.get(&amp;#039;section&amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
                if (not prior_text or prior_text in src[&amp;#039;text&amp;#039;]&lt;br /&gt;
                        or identity(key, prior_text) != previous.get(&amp;#039;hash&amp;#039;)):&lt;br /&gt;
                    raise Halt(&amp;#039;Pending archive content differs; human inspection required&amp;#039;)&lt;br /&gt;
                archived = self.snapshot(previous[&amp;#039;target&amp;#039;])&lt;br /&gt;
                optout(archived[&amp;#039;text&amp;#039;])&lt;br /&gt;
                if archived[&amp;#039;text&amp;#039;].count(block(key, prior_text)) != 1:&lt;br /&gt;
                    raise Halt(&amp;#039;Pending archive not verified; human inspection required&amp;#039;)&lt;br /&gt;
                data.pop(key)&lt;br /&gt;
                previous = None&lt;br /&gt;
            else:&lt;br /&gt;
                target = previous[&amp;#039;target&amp;#039;]&lt;br /&gt;
        if self.apply:&lt;br /&gt;
            if complete:&lt;br /&gt;
                data.pop(key, None)&lt;br /&gt;
            elif not previous:&lt;br /&gt;
                if len(data) &amp;gt;= 7:&lt;br /&gt;
                    raise Halt(&amp;#039;Pending archive bound reached; inspect state&amp;#039;)&lt;br /&gt;
                data[key] = dict(hash=digest, target=target, source_revid=src[&amp;#039;revid&amp;#039;], section=section[&amp;#039;text&amp;#039;])&lt;br /&gt;
            else:&lt;br /&gt;
                return target&lt;br /&gt;
            status(path, dict(intents=data))&lt;br /&gt;
        return target&lt;br /&gt;
&lt;br /&gt;
    def quiet_since(self, src, section):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;Prove identical complete section since end of closure date, fail closed.&lt;br /&gt;
&lt;br /&gt;
        Up to 20 newer board revisions plus one boundary revision. No continuation,&lt;br /&gt;
        full-history scans or stored history database. Busy boards defer to humans.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        boundary = section[&amp;#039;closed&amp;#039;].isoformat() + &amp;#039;T23:59:59Z&amp;#039;&lt;br /&gt;
        if src[&amp;#039;timestamp&amp;#039;] &amp;lt;= boundary:&lt;br /&gt;
            return True&lt;br /&gt;
        common = dict(action=&amp;#039;query&amp;#039;, titles=src[&amp;#039;title&amp;#039;], prop=&amp;#039;revisions&amp;#039;, rvslots=&amp;#039;main&amp;#039;,&lt;br /&gt;
                      rvprop=&amp;#039;ids|timestamp|content&amp;#039;)&lt;br /&gt;
        older = self.api(**common, rvstart=boundary, rvlimit=1)&lt;br /&gt;
        recent = self.api(**common, rvend=boundary, rvlimit=20)&lt;br /&gt;
        if &amp;#039;continue&amp;#039; in recent:&lt;br /&gt;
            return False&lt;br /&gt;
        pages = list(older[&amp;#039;query&amp;#039;][&amp;#039;pages&amp;#039;].values()) + list(recent[&amp;#039;query&amp;#039;][&amp;#039;pages&amp;#039;].values())&lt;br /&gt;
        if any(not p.get(&amp;#039;revisions&amp;#039;) for p in pages):&lt;br /&gt;
            return False&lt;br /&gt;
        revisions = [r for p in pages for r in p[&amp;#039;revisions&amp;#039;]]&lt;br /&gt;
        for rev in revisions:&lt;br /&gt;
            text = rev.get(&amp;#039;slots&amp;#039;, {}).get(&amp;#039;main&amp;#039;, {}).get(&amp;#039;*&amp;#039;)&lt;br /&gt;
            if not isinstance(text, str) or len(text.encode()) &amp;gt; MAX_BYTES:&lt;br /&gt;
                return False&lt;br /&gt;
            # Exact parsed section boundary, not a matching substring in another section.&lt;br /&gt;
            matches = [s for s in sections(text, section[&amp;#039;closed&amp;#039;] + timedelta(days=31))&lt;br /&gt;
                       if s[&amp;#039;text&amp;#039;] == section[&amp;#039;text&amp;#039;]]&lt;br /&gt;
            if len(matches) != 1:&lt;br /&gt;
                return False&lt;br /&gt;
        return True&lt;br /&gt;
&lt;br /&gt;
    def discover(self):&lt;br /&gt;
        titles, truncated = [], False&lt;br /&gt;
        # Two fixed categories, at most 100 unique election reads, no category recursion.&lt;br /&gt;
        for category in CATEGORIES:&lt;br /&gt;
            d = self.api(action=&amp;#039;query&amp;#039;, list=&amp;#039;categorymembers&amp;#039;, cmtitle=category,&lt;br /&gt;
                         cmtype=&amp;#039;page&amp;#039;, cmnamespace=4, cmlimit=100, cmprop=&amp;#039;title&amp;#039;)&lt;br /&gt;
            truncated |= &amp;#039;continue&amp;#039; in d&lt;br /&gt;
            for p in d[&amp;#039;query&amp;#039;][&amp;#039;categorymembers&amp;#039;]:&lt;br /&gt;
                t = p[&amp;#039;title&amp;#039;]&lt;br /&gt;
                if not nomination_title(t):&lt;br /&gt;
                    continue&lt;br /&gt;
                if t not in titles:&lt;br /&gt;
                    if len(titles) == 100:&lt;br /&gt;
                        truncated = True&lt;br /&gt;
                    else:&lt;br /&gt;
                        titles.append(t)&lt;br /&gt;
        return titles, truncated&lt;br /&gt;
&lt;br /&gt;
    def report(self, title, titles, truncated, today):&lt;br /&gt;
        rows = []&lt;br /&gt;
        for t in titles:&lt;br /&gt;
            if not nomination_title(t):&lt;br /&gt;
                continue&lt;br /&gt;
            # Election pages must be subpages, even when categories contain bad entries.&lt;br /&gt;
            if &amp;#039;/&amp;#039; not in t or (self.test and not t.startswith(TEST)):&lt;br /&gt;
                rows.append((t, &amp;#039;needsreview&amp;#039;))&lt;br /&gt;
                continue&lt;br /&gt;
            s = self.snapshot(t)&lt;br /&gt;
            state = &amp;#039;needsdate&amp;#039; if s[&amp;#039;missing&amp;#039;] else election(s[&amp;#039;text&amp;#039;], today)&lt;br /&gt;
            if state not in (&amp;#039;closed&amp;#039;, &amp;#039;notdue&amp;#039;):&lt;br /&gt;
                rows.append((t, state))&lt;br /&gt;
        # Human text is outside a single managed region. No hourly timestamp churn.&lt;br /&gt;
        lines = [&amp;#039;Election deadline checks only. No vote counts, closures or results.&amp;#039;,&lt;br /&gt;
                 &amp;#039;Operator: [[User:Talondri|Talondri]] (site operator). &amp;#039;&lt;br /&gt;
                 &amp;#039;[[User:EliteMaintenanceBot/Governance source|Public source and scope]].&amp;#039;]&lt;br /&gt;
        if truncated:&lt;br /&gt;
            lines.append(&amp;#039;Discovery limit reached: at most 100 pages checked; human review needed.&amp;#039;)&lt;br /&gt;
        lines += [f&amp;#039;* [[{t}]]: {state}&amp;#039; for t, state in sorted(rows)]&lt;br /&gt;
        if not rows:&lt;br /&gt;
            lines.append(&amp;#039;No overdue or undated elections in the bounded category check.&amp;#039;)&lt;br /&gt;
        body = &amp;#039;\n&amp;#039; + &amp;#039;\n&amp;#039;.join(lines) + &amp;#039;\n&amp;#039;&lt;br /&gt;
        s = self.snapshot(title)&lt;br /&gt;
        optout(s[&amp;#039;text&amp;#039;])&lt;br /&gt;
        if s[&amp;#039;missing&amp;#039;]:&lt;br /&gt;
            text = &amp;#039;{{bots|allow=EliteMaintenanceBot}}\n&amp;#039; + START + body + END&lt;br /&gt;
        else:&lt;br /&gt;
            if s[&amp;#039;text&amp;#039;].count(START) != 1 or s[&amp;#039;text&amp;#039;].count(END) != 1:&lt;br /&gt;
                raise Halt(&amp;#039;Report needs exactly one managed marker pair&amp;#039;)&lt;br /&gt;
            a, b = s[&amp;#039;text&amp;#039;].index(START) + len(START), s[&amp;#039;text&amp;#039;].index(END)&lt;br /&gt;
            if a &amp;gt; b:&lt;br /&gt;
                raise Halt(&amp;#039;Report markers reversed&amp;#039;)&lt;br /&gt;
            text = s[&amp;#039;text&amp;#039;][:a] + body + s[&amp;#039;text&amp;#039;][b:]&lt;br /&gt;
        if self.apply and text != s[&amp;#039;text&amp;#039;]:&lt;br /&gt;
            self.save(s, text, &amp;#039;Report election deadlines for human review only&amp;#039;)&lt;br /&gt;
        return dict(findings=rows, discovery_truncated=truncated, checked=len(titles))&lt;br /&gt;
&lt;br /&gt;
    def run(self, today):&lt;br /&gt;
        c = self.control()&lt;br /&gt;
        results = []&lt;br /&gt;
        # One section per board per cycle, &amp;lt;=7 archive pairs, total hard cap 12 edits.&lt;br /&gt;
        # Reserve one edit for the report; remaining backlog waits for next hour.&lt;br /&gt;
        for b in c[&amp;#039;boards&amp;#039;]:&lt;br /&gt;
            if self.edits &amp;gt; 9:&lt;br /&gt;
                break&lt;br /&gt;
            try:&lt;br /&gt;
                state = self.archive_one(b[&amp;#039;title&amp;#039;], b[&amp;#039;archive_prefix&amp;#039;], today)&lt;br /&gt;
            except Halt as e:&lt;br /&gt;
                if str(e) == &amp;#039;Bot exclusion&amp;#039;:&lt;br /&gt;
                    state = &amp;#039;excluded&amp;#039;&lt;br /&gt;
                else:&lt;br /&gt;
                    raise&lt;br /&gt;
            results.append(dict(title=b[&amp;#039;title&amp;#039;], status=state))&lt;br /&gt;
        titles, truncated = ([], False) if self.test else self.discover()&lt;br /&gt;
        report = self.report(c[&amp;#039;report_page&amp;#039;], titles, truncated, today)&lt;br /&gt;
        return dict(status=&amp;#039;applied&amp;#039; if self.apply else &amp;#039;dry-run&amp;#039;, boards=results,&lt;br /&gt;
                    elections=report, edits=self.edits)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@contextmanager&lt;br /&gt;
def lock(path):&lt;br /&gt;
    import fcntl&lt;br /&gt;
    with open(path, &amp;#039;a&amp;#039;) as f:&lt;br /&gt;
        try:&lt;br /&gt;
            fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)&lt;br /&gt;
        except BlockingIOError:&lt;br /&gt;
            raise Halt(&amp;#039;Governance worker already active&amp;#039;) from None&lt;br /&gt;
        yield&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def status(path, result):&lt;br /&gt;
    fd, tmp = tempfile.mkstemp(dir=path.parent, prefix=&amp;#039;.status-&amp;#039;)&lt;br /&gt;
    try:&lt;br /&gt;
        with os.fdopen(fd, &amp;#039;w&amp;#039;) as stream:&lt;br /&gt;
            json.dump(dict(result, at=datetime.now(timezone.utc).isoformat()), stream, indent=2)&lt;br /&gt;
            stream.flush()&lt;br /&gt;
            os.fsync(stream.fileno())&lt;br /&gt;
        os.replace(tmp, path)&lt;br /&gt;
    finally:&lt;br /&gt;
        if os.path.exists(tmp):&lt;br /&gt;
            os.unlink(tmp)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def connect():&lt;br /&gt;
    import pywikibot&lt;br /&gt;
    pywikibot.config.max_retries = 1&lt;br /&gt;
    pywikibot.config.retry_wait = 5&lt;br /&gt;
    pywikibot.config.retry_max = 10&lt;br /&gt;
    pywikibot.config.socket_timeout = (10, 30)&lt;br /&gt;
    pywikibot.config.log = []&lt;br /&gt;
    site = pywikibot.Site()&lt;br /&gt;
    if site.hostname() != &amp;#039;elite.mistflux.co.uk&amp;#039; or site.protocol() != &amp;#039;https&amp;#039;:&lt;br /&gt;
        raise Halt(&amp;#039;Unexpected wiki endpoint&amp;#039;)&lt;br /&gt;
    site.login()&lt;br /&gt;
    if site.user() != BOT or &amp;#039;bot&amp;#039; not in site.userinfo.get(&amp;#039;rights&amp;#039;, []):&lt;br /&gt;
        raise Halt(&amp;#039;Expected EliteMaintenanceBot with bot right&amp;#039;)&lt;br /&gt;
    return site&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def main():&lt;br /&gt;
    p = argparse.ArgumentParser(description=__doc__)&lt;br /&gt;
    p.add_argument(&amp;#039;--apply&amp;#039;, action=&amp;#039;store_true&amp;#039;)&lt;br /&gt;
    p.add_argument(&amp;#039;--test&amp;#039;, action=&amp;#039;store_true&amp;#039;, help=&amp;#039;Only isolated user-subpage control and outputs&amp;#039;)&lt;br /&gt;
    args = p.parse_args()&lt;br /&gt;
    directory = Path(os.environ.get(&amp;#039;GOVERNANCE_STATE_DIR&amp;#039;, &amp;#039;/var/lib/elitewiki-governance&amp;#039;))&lt;br /&gt;
    try:&lt;br /&gt;
        with lock(directory / &amp;#039;governance.lock&amp;#039;):&lt;br /&gt;
            try:&lt;br /&gt;
                result = Worker(connect(), args.apply, args.test).run(datetime.now(timezone.utc).date())&lt;br /&gt;
                code = 0&lt;br /&gt;
            except Halt as e:&lt;br /&gt;
                result, code = dict(status=&amp;#039;halted&amp;#039;, reason=str(e)), 2&lt;br /&gt;
            except Exception as e:&lt;br /&gt;
                result, code = dict(status=&amp;#039;error&amp;#039;, error_type=type(e).__name__), 1&lt;br /&gt;
            status(directory / (&amp;#039;test-status.json&amp;#039; if args.test else &amp;#039;status.json&amp;#039;), result)&lt;br /&gt;
            print(json.dumps(result), flush=True)&lt;br /&gt;
            return code&lt;br /&gt;
    except Halt as e:&lt;br /&gt;
        print(str(e))&lt;br /&gt;
        return 2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;#039;__main__&amp;#039;:&lt;br /&gt;
    sys.exit(main())&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Talondri</name></author>
	</entry>
</feed>