CHAT_OUTPUT_BEGIN
COMMAND_ID=NEWFI-260907-A-PORTAL200-APPLY4DESIGN6
STATUS=OK
RC=0
HOST=pve01
MODE=read-only
COMPONENT=newfi-portal200-apply4-exact-old-carrier-recovery
REFERENCE_REGISTER_CHECK=OK
REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66
ERROR_REGISTER_CHECK=OK
ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0
COMMAND_SHA256=743ae461d12f19d0da870df76bde76c8a943c1729a9a04890736219912177071
DUPLICATE_FAILED_COMMAND_BLOCKED=false
EXECUTION_STARTED=true
CHANGE_DECLARED=false
RESULT_CONTRACT_VALID=true
RESULT_CONTRACT_STATUS=NOT_APPLICABLE
RESULT_CONTRACT_ERROR=NONE
COMMAND_RC=0
CHANGES_MADE=false
ROLLBACK_STARTED=false
ROLLBACK_RESTORED=null
MUTATION_OUTCOME=NO_MUTATION
SANITIZED=yes
SECRETS_INCLUDED=no
PRIVATE_ADDRESSES_INCLUDED=no
RAW_EVIDENCE_SHA256=26388e4b0b4f7fd3220098a4be02fcc57b475838436c8374cae49aec73fea1ac
SANITIZED_OUTPUT_SHA256=778936e6f9b7e0ce7697c2b9feb8203c185a24c19d7b76e2f58d05caa12ee143
OUTPUT_BEGIN
LOCAL_APPLY3_EXACT_CARRIER_RECOVERY_BEGIN=true
COMMAND_ID=NEWFI-260907-A-PORTAL200-APPLY4DESIGN6
MUTATION_SCOPE=NONE_READ_ONLY
LOCAL_MUTATIONS_PERFORMED=NO
TARGET_BASENAME=newfi-54-portal200-apply3.cmd
TARGET_OLD_COMMAND_ID=NEWFI-260907-PORTAL200-APPLY3
TARGET_MATCH_COUNT=1
TARGET_FILE_SHA256=573c5c94fe59ed072e094281668ce9847dc4a9882019a2b89d8ba5f190d92176
TARGET_FILE_BYTES=17209
TARGET_FILE_CONTAINS_OLD_CID=True
DECODED_LAYER_COUNT=2
CANDIDATE_LAYER_COUNT=2
CANDIDATE_LAYER_1_SOURCE=carrier::b64::zlib
CANDIDATE_LAYER_1_SHA256=97a1807a275e5d309591f7a7e044d7d56ba5b2f4c6a068df6353bae147707184
CANDIDATE_LAYER_1_BYTES=34155
CANDIDATE_LAYER_1_RELEVANT_BEGIN
import subprocess
import sys
import xml.etree.ElementTree as ET

CID = "NEWFI-260907-PORTAL200-APPLY3"
BRANCH = "implementation/newfi-closed-mvp-20260906"
OLD_HEAD = "06bab5acd124e768f70305b3d7eca2f595871890"
WT = pathlib.Path("/root/newfi-portal/.worktrees/implementation-newfi-closed-mvp-20260906")
CAND_ROOT = pathlib.Path("/root/newfi-portal200-candidate/src/addons/Newfi/Portal")
PORTAL_REL = pathlib.Path("src/addons/Newfi/Portal")
GIT_PORTAL = WT / PORTAL_REL
EXPECTED_NEW = {
    "Pub/Controller/Portal.php": (363, "619d489f7cc3b461479c702fa8825097ae22d3c6cfeb911e9f14ea1de9c36d6d"),
    "Service/PortalData.php": (5213, "4da03f26294ab6d163a1a746dee4bb6a82d7d4f39e66b24246bc0eb4c455dd1d"),
    "_data/templates.xml": (10977, "1650c3d868d4bc47703f1b75f1f179cd25309b2d3b20a2b848e81a4423c00861"),
}
EXPECTED_OLD = {
    "Pub/Controller/Portal.php": (247, "53909e753ea5a011550fd280ad1c342a74ca9949be092dfbd95244243cae92f3"),
    "_data/templates.xml": (1461, "c29c04f8b937284e821c87957653e87c2f6ac014fac30648a0f6b18e84392adf"),
EXPECTED_GIT_PATHS = [str(PORTAL_REL / p) for p in sorted(EXPECTED_NEW)]
BACKUP = f"/root/newfi-portal200-backup-{CID}"
class PortalError(RuntimeError):
    pass
def emit(key, value=None):
    if value is None:
function srcMeta(string $path): ?array {
    if (!is_file($path)) { return null; }
    return ['bytes' => filesize($path), 'sha256' => hash_file('sha256', $path)];
$portal = $root . '/src/addons/Newfi/Portal';
$templates = $db->fetchAll(
    "SELECT title, style_id, LENGTH(template) AS bytes, SHA2(template,256) AS sha256, " .
    "LOCATE('data-newfi-portal200', template) > 0 AS html_marker, " .
    "LOCATE('--newfi-portal200', template) > 0 AS css_marker " .
    "FROM xf_template WHERE addon_id='Newfi/Portal' ORDER BY style_id,title"
);
$result = [
 'hostname' => gethostname(),
 'application' => [
  'boardActive' => (string)$db->fetchOne("SELECT option_value FROM xf_option WHERE option_id='boardActive'"),
  'boardUrl' => (string)$db->fetchOne("SELECT option_value FROM xf_option WHERE option_id='boardUrl'"),
  'indexRoute' => (string)$db->fetchOne("SELECT option_value FROM xf_option WHERE option_id='indexRoute'"),
  'defaultStyleId' => (string)$db->fetchOne("SELECT option_value FROM xf_option WHERE option_id='defaultStyleId'"),
  'error_count' => (int)$db->fetchOne("SELECT COUNT(*) FROM xf_error_log"),
  'error_max_id' => (int)$db->fetchOne("SELECT COALESCE(MAX(error_id),0) FROM xf_error_log")
 ],
 'addon' => $db->fetchRow(
  "SELECT addon_id,version_id,version_string,active,is_processing,last_pending_action " .
  "FROM xf_addon WHERE addon_id='Newfi/Portal'"
 ),
 'source' => [
  'Pub/Controller/Portal.php' => srcMeta($portal . '/Pub/Controller/Portal.php'),
  'Service/PortalData.php' => srcMeta($portal . '/Service/PortalData.php'),
  '_data/templates.xml' => srcMeta($portal . '/_data/templates.xml')
 'templates' => $templates
];
echo "PORTAL200_INSPECT_JSON_BEGIN\n";
echo json_encode(
    $result,
    JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_INVALID_UTF8_SUBSTITUTE
echo "\nPORTAL200_INSPECT_JSON_END\n";
?>'''
IMPORT_PHP = r'''<?php
$root = '/var/www/forums/newfi/public';
require $root . '/src/XF.php';
\XF::start($root);
$app = \XF::setupApp('XF\\Pub\\App');
$addOn = $app->addOnManager()->getById('Newfi/Portal');
if (!$addOn || !$addOn->isInstalled()) {
    fwrite(STDERR, "ADDON_NOT_INSTALLED\n");
    exit(30);
$addOn->preRebuild();
$dataManager = $app->addOnDataManager();
$imported = [];
foreach ($dataManager->getDataTypeClasses() as $dataType) {
$addOn->postDataImport();
$repo = $app->repository(\XF\Repository\StyleRepository::class);
$repo->updateAllStylesLastModifiedDateLater();
$addOn->postRebuild();
echo "PORTAL200_IMPORT_JSON=" . json_encode(
    ['result'=>'PASS','containers'=>$imported],
    JSON_UNESCAPED_SLASHES
) . "\n";
ERRORS_PHP_TEMPLATE = r'''<?php
$root='/var/www/forums/newfi/public';
            'hasItems'=>(bool)($section['hasItems'] ?? false),
            'status'=>(string)($section['status'] ?? '')
        ];
    }
    $result['html_marker'] = strpos($html, 'data-newfi-portal200="1"') !== false;
    $result['locked_resources'] = strpos($html, 'data-portal-locked="resources"') !== false;
    $result['locked_media'] = strpos($html, 'data-portal-locked="media"') !== false;
    $result['html_bytes'] = strlen($html);
    if (!$result['html_marker']) { fwrite(STDERR, "HTML_MARKER_MISSING\n"); exit(61); }
    if ($result['sections']['resources']['available'] || $result['sections']['media']['available']) {
        fwrite(STDERR, "GUEST_PERMISSION_CONTRACT_CHANGED\n"); exit(62);
    if (!$result['locked_resources'] || !$result['locked_media']) {
        fwrite(STDERR, "PUBLIC_SECTION_UNAVAILABLE\n"); exit(64);
} else if ($state === 'old') {
    $html = (string)$app->templater()->renderTemplate('public:newfi_portal', []);
    $result['html_marker'] = strpos($html, 'data-newfi-portal200') !== false;
    $result['old_copy'] = strpos($html, 'Закрытое пространство для общения, знаний и материалов сообщества.') !== false;
    if ($result['html_marker'] || !$result['old_copy']) {
        fwrite(STDERR, "OLD_RENDER_CONTRACT\n"); exit(65);
} else {
    exit(66);
echo "PORTAL200_RUNTIME_JSON=" . json_encode(
HTTP_VERIFY_PY = r'''import html,json,re,subprocess,sys,urllib.parse
mode=sys.argv[1]
        out.append((k,v))
    if found:
        forced=urllib.parse.urlunsplit((u.scheme,u.netloc,u.path,urllib.parse.urlencode(out),u.fragment))
        forced_css_status,forced_body=fetch(forced)
        forced_css_marker='--newfi-portal200' in forced_body
result={
 'status':status,
 'bytes':len(body.encode()),
 'html_marker':'data-newfi-portal200="1"' in body,
 'css_marker':'--newfi-portal200' in normal_css_text,
 'old_copy':'Закрытое пространство для общения, знаний и материалов сообщества.' in body,
 'css_count':len(hrefs),
 'css_status':normal_status,
 'forced_css_status':forced_css_status,
 'forced_css_marker':forced_css_marker,
print('PORTAL200_HTTP_JSON='+json.dumps(result,ensure_ascii=False,sort_keys=True,separators=(',',':')))
if status!=expected_status: raise SystemExit(45)
if mode=='prod':
    if not hrefs or any(x!=200 for x in normal_status): raise SystemExit(46)
elif mode=='staging':
    if any(x!=200 for x in normal_status): raise SystemExit(46)
else:
    raise SystemExit(50)
if mode=='prod' and state=='new':
'''
def parse_inspect(text):
    b = "PORTAL200_INSPECT_JSON_BEGIN\n"
    e = "\nPORTAL200_INSPECT_JSON_END"
    if b not in text or e not in text:
        fail("INSPECT_MARKERS")
    try:
        return json.loads(text.split(b, 1)[1].split(e, 1)[0])
    except Exception:
        fail("INSPECT_JSON")
        a.get("boardActive") == expected[0]
        and a.get("boardUrl") == expected[1]
        and a.get("indexRoute") == "portal/"
        and a.get("defaultStyleId") == "4"
        and addon.get("addon_id") == "Newfi/Portal"
        and int(addon.get("version_id", 0)) == 1000010
        and addon.get("version_string") == "1.0.0 Alpha 1"
        and int(addon.get("active", 0)) == 1
        and int(addon.get("is_processing", 1)) == 0
    )
def new_errors(target, baseline):
    raw = qga(
        target,
def assert_no_new_errors(target, baseline, phase):
    rows = new_errors(target, baseline)
    if rows:
        emit(
            "PORTAL200_NEW_XF_ERRORS",
            {
                "target": target,
                "phase": phase,
                "count": len(rows),
                "ids": [r.get("error_id") for r in rows],
            },
        )
        fail("NEW_XF_ERRORS")
def guest_backup(target):
    script = f'''set -eu
B={shlex.quote(BACKUP)}
P=/var/www/forums/newfi/public/src/addons/Newfi/Portal
test ! -e "$B"
install -d -m 700 "$B/Pub/Controller" "$B/_data"
cp -p -- "$P/Pub/Controller/Portal.php" "$B/Pub/Controller/Portal.php"
cp -p -- "$P/_data/templates.xml" "$B/_data/templates.xml"
test ! -e "$P/Service/PortalData.php"
printf 'PORTAL200_BACKUP=PASS\n'
    out = qga(target, ["/bin/bash", "-lc", script], timeout=90)
    if "PORTAL200_BACKUP=PASS" not in out:
        fail("BACKUP_FAILED")
def guest_write(target, rel, data):
    dest = f"/var/www/forums/newfi/public/src/addons/Newfi/Portal/{rel}"
    parent = str(pathlib.PurePosixPath(dest).parent)
    script = (
        "set -eu; umask 022; "
        + "install -d -m 755 "
        + shlex.quote(parent)
        + "; T=$(mktemp "
        + shlex.quote(parent + "/.portal200.XXXXXX")
        + "); cat >\"$T\"; chmod 0644 \"$T\"; chown root:root \"$T\"; "
        + "mv -f -- \"$T\" "
        + shlex.quote(dest)
        + "; printf 'PORTAL200_WRITE=PASS\\n'"
    out = qga(
        ["/bin/bash", "-lc", script],
        input_bytes=data,
        timeout=90,
    if "PORTAL200_WRITE=PASS" not in out:
        fail("WRITE_FAILED")
def install_candidate(target):
    for rel in [
        "Pub/Controller/Portal.php",
        "Service/PortalData.php",
        "_data/templates.xml",
    ]:
        guest_write(target, rel, (CAND_ROOT / rel).read_bytes())
def restore_old(target):
test -f "$B/Pub/Controller/Portal.php"
test -f "$B/_data/templates.xml"
cp -p -- "$B/Pub/Controller/Portal.php" "$P/Pub/Controller/Portal.php"
cp -p -- "$B/_data/templates.xml" "$P/_data/templates.xml"
rm -f -- "$P/Service/PortalData.php"
rmdir -- "$P/Service" 2>/dev/null || true
printf 'PORTAL200_SOURCE_RESTORE=PASS\n'
    if "PORTAL200_SOURCE_RESTORE=PASS" not in out:
        fail("SOURCE_RESTORE_FAILED")
def import_addon(target):
        ["/usr/bin/php8.3"],
        input_bytes=IMPORT_PHP.encode(),
        timeout=240,
    if "PORTAL200_IMPORT_JSON=" not in out:
        fail("IMPORT_FAILED")
def runtime_verify(target, state):
    src = RUNTIME_VERIFY_PHP_TEMPLATE.replace("__STATE__", state)
    out = qga(target, ["/usr/bin/php8.3"], input_bytes=src.encode(), timeout=120)
    line = next(
        (x for x in out.splitlines() if x.startswith("PORTAL200_RUNTIME_JSON=")),
        None,
    if not line:
        fail("RUNTIME_MARKER_MISSING")
        data = json.loads(line.split("=", 1)[1])
        fail("RUNTIME_JSON")
    emit("PORTAL200_RUNTIME", {"target": target, **data})
    return data
def http_verify(target, state):
    argv = ["/usr/bin/python3", "-", target, state]
    if target == "prod":
        try:
            p = subprocess.run(
            fail("HTTP_PROD_TIMEOUT")
        out = p.stdout.decode("utf-8", "replace")
        err = p.stderr.decode("utf-8", "replace")
        line = next(
            (x for x in out.splitlines() if x.startswith("PORTAL200_HTTP_JSON=")),
            None,
        data = None
        if line:
            try:
                data = json.loads(line.split("=", 1)[1])
                emit("PORTAL200_HTTP", {"target": target, "state": state, **data})
            except Exception:
                fail("HTTP_JSON")
        if p.returncode != 0:
            emit(
                "PORTAL200_HTTP_EXEC",
                {
                    "target": target,
                    "state": state,
                    "rc": p.returncode,
                    "stderr": err[:800],
                },
            )
            fail("HTTP_PROD_RC")
        input_bytes=HTTP_VERIFY_PY.encode(),
        timeout=180,
        (x for x in out.splitlines() if x.startswith("PORTAL200_HTTP_JSON=")),
        fail("HTTP_MARKER_MISSING")
        fail("HTTP_JSON")
    emit("PORTAL200_HTTP", {"target": target, "state": state, **data})
def verify_new(target, baseline):
    data = inspect(target)
    if not app_contract(data, target):
        fail("APP_CONTRACT_NEW")
    if not source_is_new(data):
        timeout=40,
    if "status: running" not in p.stdout.decode():
        fail("VM211_NOT_RUNNING")
    emit("PORTAL200_CLUSTER", "PASS")
def result_contract(status, changes, rollback_started, rollback_restored):
    emit(
        "HOMELAB_RESULT_CONTRACT",
        {
            "version": 1,
            "command_id": CID,
emit("NEWFI200_BEGIN")
emit("COMMAND_ID", CID)
emit("PORTAL200_REMAINING_FULL_PRODUCT_COMMANDS_BEFORE", 22)
emit(
    "PORTAL200_FLOW",
    "candidate->staging-apply->rollback-proof->staging-reapply->git-push->production",
)
staging_backup = False
staging_mutated = False
staging_final_new = False
prod_backup = False
prod_mutated = False
git_files_changed = False
s_old_templates = None
p_old_templates = None
try:
    compile(HTTP_VERIFY_PY, "<portal200-http-verify>", "exec")
    for rel, (size, sha) in EXPECTED_NEW.items():
        p = CAND_ROOT / rel
        if not p.is_file() or file_meta(p) != (size, sha):
            fail("CANDIDATE_IDENTITY")
    tree = ET.parse(CAND_ROOT / "_data/templates.xml")
    titles = sorted(x.attrib.get("title") for x in tree.getroot().findall("template"))
    if titles != ["newfi_portal", "newfi_portal.less"]:
        fail("CANDIDATE_TEMPLATE_SET")
    service = (CAND_ROOT / "Service/PortalData.php").read_text("utf-8")
    if any(
        token in service
        for token in [
            "\\XF::db(",
            "->save(",
            "->delete(",
            "INSERT ",
            "DELETE ",
            "REPLACE ",
        ]
    ):
        fail("CANDIDATE_MUTATION_SCAN")
    emit("PORTAL200_CANDIDATE_IDENTITY", "PASS")
        "PORTAL200_ORCHESTRATION_FIXES",
            "staging_503_without_css_links": True,
            "failure_status": "FAIL",
            "prod_http_via_pve01_public_route": True,
            "css_href_regex_fixed": True,
        },
    remote = remote_out.split()[0] if remote_out else ""
    if branch != BRANCH or head != OLD_HEAD or remote != OLD_HEAD or status:
        fail("GIT_PRESTATE")
        "PORTAL200_GIT_PRESTATE",
        {"branch": branch, "head": head, "remote": remote, "clean": True},
    cluster_guard()
    lint_php("staging", INSPECT_PHP, "INSPECT_STAGING")
    lint_php("staging", IMPORT_PHP, "IMPORT_STAGING")
    lint_php("staging", RUNTIME_VERIFY_PHP_TEMPLATE.replace("__STATE__", "new"), "RUNTIME_STAGING")
    lint_php("prod", INSPECT_PHP, "INSPECT_PROD")
    lint_php("prod", IMPORT_PHP, "IMPORT_PROD")
    lint_php("prod", RUNTIME_VERIFY_PHP_TEMPLATE.replace("__STATE__", "new"), "RUNTIME_PROD")
    emit("PORTAL200_RUNTIME_HELPER_LINT", "PASS")
    s0 = inspect("staging")
    p0 = inspect("prod")
    if not app_contract(s0, "staging") or not app_contract(p0, "prod"):
        fail("APP_PRESTATE")
    if not source_is_old(s0) or not source_is_old(p0):
        fail("SOURCE_PRESTATE")
    if s0.get("templates") != p0.get("templates"):
    s_error = int(s0["application"]["error_max_id"])
    p_error = int(p0["application"]["error_max_id"])
    s_old_templates = s0["templates"]
    p_old_templates = p0["templates"]
    emit("PORTAL200_ERROR_BASELINE", {"staging": s_error, "prod": p_error})
    http_verify("staging", "old")
    runtime_verify("prod", "old")
    http_verify("prod", "old")
    assert_no_new_errors("staging", s_error, "preflight")
    assert_no_new_errors("prod", p_error, "preflight")
    emit("PORTAL200_PREFLIGHT", "PASS")
    guest_backup("staging")
    staging_backup = True
    staging_mutated = True
    install_candidate("staging")
    import_addon("staging")
    verify_new("staging", s_error)
    emit("PORTAL200_STAGING_FIRST_APPLY", "PASS")
    restore_old("staging")
    verify_old("staging", s_error, s_old_templates)
    staging_mutated = False
    emit("PORTAL200_STAGING_ROLLBACK_PROOF", "PASS")
    staging_final_new = True
    emit("PORTAL200_STAGING_FINAL", "PASS")
    for rel in EXPECTED_NEW:
        dest = GIT_PORTAL / rel
        old_git_bytes[str(rel)] = dest.read_bytes() if dest.exists() else None
        dest.parent.mkdir(parents=True, exist_ok=True)
        shutil.copyfile(CAND_ROOT / rel, dest)
    git_files_changed = True
    if remote_head != new_head:
        fail("GIT_REMOTE_HEAD")
    push_confirmed = True
        "PORTAL200_GIT_PROMOTION",
            "old_head": OLD_HEAD,
            "new_head": new_head,
            "remote_head": remote_head,
            "result": "PASS",
        fail("PROD_ERROR_DRIFT")
    guest_backup("prod")
    prod_backup = True
    prod_mutated = True
    install_candidate("prod")
    import_addon("prod")
    verify_new("prod", p_error)
    emit("PORTAL200_PRODUCTION", "PASS")
    sf = verify_new("staging", s_error)
    pf = verify_new("prod", p_error)
    if sf["source"] != pf["source"]:
        fail("FINAL_SOURCE_PARITY")
    if sf["templates"] != pf["templates"]:
        fail("FINAL_TEMPLATE_PARITY")
    if git("rev-parse", "HEAD") != new_head:
    if git("status", "--porcelain=v1", "--untracked-files=all"):
        fail("FINAL_GIT_DIRTY")
        "PORTAL200_FINAL_EVIDENCE",
            "commit": new_head,
            "source_files": 3,
            "templates": 2,
            "staging_error_baseline": s_error,
            "prod_error_baseline": p_error,
    emit("PORTAL200_PORTAL", "PASS")
    emit("PORTAL200_STAGING", "PASS")
    emit("PORTAL200_REMAINING_FULL_PRODUCT_COMMANDS_AFTER_PASS", 21)
    result_contract("OK", True, False, None)
    emit("NEWFI200_END")
    raise SystemExit(0)
except PortalError as exc:
    code = str(exc)
    emit("PORTAL200_FAILURE", code)
    if push_confirmed:
        if prod_mutated and prod_backup:
            failure_rollback_started = True
                restore_old("prod")
                import_addon("prod")
                verify_old("prod", p_error, p_old_templates, strict_errors=False)
                prod_mutated = False
                failure_rollback_restored = False
                emit("PORTAL200_FAILURE_PROD_ROLLBACK", "PASS_PARTIAL_OVERALL_STATE_CHANGED")
                emit("PORTAL200_FAILURE_PROD_ROLLBACK", "FAIL")
    else:
        if staging_mutated and staging_backup:
                restore_old("staging")
                import_addon("staging")
                verify_old("staging", s_error, s_old_templates, strict_errors=False)
                staging_mutated = False
                staging_final_new = False
                failure_rollback_restored = True
                emit("PORTAL200_FAILURE_STAGING_ROLLBACK", "PASS")
                emit("PORTAL200_FAILURE_STAGING_ROLLBACK", "FAIL")
        if git_files_changed and not commit_created:
                for rel, old in old_git_bytes.items():
                    dest = GIT_PORTAL / rel
                    if old is None:
                        if dest.exists():
                            dest.unlink()
                try:
                    service_dir.rmdir()
                except OSError:
                    pass
                emit("PORTAL200_FAILURE_GIT_WORKTREE_RESTORE", "PASS")
                emit("PORTAL200_FAILURE_GIT_WORKTREE_RESTORE", "FAIL")
        if commit_created:
                "PORTAL200_FAILURE_GIT_HISTORY",
                "COMMIT_CREATED_NO_DESTRUCTIVE_RESET_FRESH_ID_REQUIRED",
    persisted = bool(push_confirmed or commit_created or staging_final_new or prod_mutated)
    if failure_rollback_started and failure_rollback_restored is None:
        failure_rollback_restored = False
    result_contract(
        "FAIL",
        persisted,
        failure_rollback_started,
        failure_rollback_restored,
    emit("PORTAL200_ROADMAP_REMAINS", 22)
    raise SystemExit(1)
except Exception:
    emit("PORTAL200_FAILURE", "UNEXPECTED_EXCEPTION")
        bool(push_confirmed or commit_created or staging_final_new or prod_mutated),
        False,
CANDIDATE_LAYER_1_RELEVANT_END
CANDIDATE_LAYER_1_FULL_BEGIN
import hashlib
import json
import pathlib
import shlex
import shutil
import subprocess
import sys
import xml.etree.ElementTree as ET

CID = "NEWFI-260907-PORTAL200-APPLY3"
BRANCH = "implementation/newfi-closed-mvp-20260906"
OLD_HEAD = "06bab5acd124e768f70305b3d7eca2f595871890"
WT = pathlib.Path("/root/newfi-portal/.worktrees/implementation-newfi-closed-mvp-20260906")
CAND_ROOT = pathlib.Path("/root/newfi-portal200-candidate/src/addons/Newfi/Portal")
PORTAL_REL = pathlib.Path("src/addons/Newfi/Portal")
GIT_PORTAL = WT / PORTAL_REL
EXPECTED_NEW = {
    "Pub/Controller/Portal.php": (363, "619d489f7cc3b461479c702fa8825097ae22d3c6cfeb911e9f14ea1de9c36d6d"),
    "Service/PortalData.php": (5213, "4da03f26294ab6d163a1a746dee4bb6a82d7d4f39e66b24246bc0eb4c455dd1d"),
    "_data/templates.xml": (10977, "1650c3d868d4bc47703f1b75f1f179cd25309b2d3b20a2b848e81a4423c00861"),
}
EXPECTED_OLD = {
    "Pub/Controller/Portal.php": (247, "53909e753ea5a011550fd280ad1c342a74ca9949be092dfbd95244243cae92f3"),
    "_data/templates.xml": (1461, "c29c04f8b937284e821c87957653e87c2f6ac014fac30648a0f6b18e84392adf"),
}
EXPECTED_GIT_PATHS = [str(PORTAL_REL / p) for p in sorted(EXPECTED_NEW)]
BACKUP = f"/root/newfi-portal200-backup-{CID}"


class PortalError(RuntimeError):
    pass


def emit(key, value=None):
    if value is None:
        print(key, flush=True)
    elif isinstance(value, (dict, list, tuple, bool, int, float)):
        print(
            key + "=" + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")),
            flush=True,
        )
    else:
        print(key + "=" + str(value), flush=True)


def fail(code):
    raise PortalError(code)


def sha_bytes(data):
    return hashlib.sha256(data).hexdigest()


def file_meta(path):
    data = pathlib.Path(path).read_bytes()
    return len(data), sha_bytes(data)


def run(cmd, *, input_bytes=None, timeout=180, ok=(0,)):
    try:
        p = subprocess.run(
            cmd,
            input=input_bytes,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            timeout=timeout,
            check=False,
        )
    except subprocess.TimeoutExpired:
        fail("SUBPROCESS_TIMEOUT")
    if p.returncode not in ok:
        fail("SUBPROCESS_RC")
    return p


def git(*args, ok=(0,), timeout=120):
    p = run(["/usr/bin/git", "-C", str(WT), *args], timeout=timeout, ok=ok)
    return p.stdout.decode("utf-8", "replace").strip()


def qga(target, argv, *, input_bytes=None, timeout=180):
    if target == "prod":
        cmd = [
            "/usr/sbin/qm",
            "guest",
            "exec",
            "210",
            "--synchronous",
            "1",
            "--timeout",
            str(timeout),
        ]
        if input_bytes is not None:
            cmd += ["--pass-stdin", "1"]
        cmd += ["--", *argv]
    elif target == "staging":
        q = [
            "/usr/sbin/qm",
            "guest",
            "exec",
            "211",
            "--synchronous",
            "1",
            "--timeout",
            str(timeout),
        ]
        if input_bytes is not None:
            q += ["--pass-stdin", "1"]
        q += ["--", *argv]
        remote = (
            "test \"$(id -u):$(hostname -s)\" = 0:pve03 || exit 72; "
            "for f in /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md /etc/pve/31_HOMELAB_REFERENCE.md; do "
            "test -r \"$f\" && test -s \"$f\" && cat -- \"$f\" >/dev/null || exit 73; done; exec "
            + " ".join(shlex.quote(x) for x in q)
        )
        cmd = [
            "/usr/bin/ssh",
            "-T",
            "-o",
            "BatchMode=yes",
            "-o",
            "StrictHostKeyChecking=yes",
            "-o",
            "UpdateHostKeys=no",
            "-o",
            "ConnectTimeout=10",
            "-o",
            "ConnectionAttempts=1",
            "root@pve03",
            remote,
        ]
    else:
        fail("BAD_TARGET")

    p = run(cmd, input_bytes=input_bytes, timeout=timeout + 30)
    try:
        data = json.loads(p.stdout.decode("utf-8"))
    except Exception:
        fail("QGA_JSON")
    if data.get("exited") not in (1, True) or data.get("exitcode") != 0:
        fail("QGA_GUEST_RC")
    if data.get("out-truncated") or data.get("err-truncated"):
        fail("QGA_TRUNCATED")
    return data.get("out-data", "")


INSPECT_PHP = r'''<?php
$root = '/var/www/forums/newfi/public';
require $root . '/src/XF.php';
\XF::start($root);
$app = \XF::setupApp('XF\\Pub\\App');
$db = $app->db();
function srcMeta(string $path): ?array {
    if (!is_file($path)) { return null; }
    return ['bytes' => filesize($path), 'sha256' => hash_file('sha256', $path)];
}
$portal = $root . '/src/addons/Newfi/Portal';
$templates = $db->fetchAll(
    "SELECT title, style_id, LENGTH(template) AS bytes, SHA2(template,256) AS sha256, " .
    "LOCATE('data-newfi-portal200', template) > 0 AS html_marker, " .
    "LOCATE('--newfi-portal200', template) > 0 AS css_marker " .
    "FROM xf_template WHERE addon_id='Newfi/Portal' ORDER BY style_id,title"
);
$result = [
 'hostname' => gethostname(),
 'application' => [
  'boardActive' => (string)$db->fetchOne("SELECT option_value FROM xf_option WHERE option_id='boardActive'"),
  'boardUrl' => (string)$db->fetchOne("SELECT option_value FROM xf_option WHERE option_id='boardUrl'"),
  'indexRoute' => (string)$db->fetchOne("SELECT option_value FROM xf_option WHERE option_id='indexRoute'"),
  'defaultStyleId' => (string)$db->fetchOne("SELECT option_value FROM xf_option WHERE option_id='defaultStyleId'"),
  'error_count' => (int)$db->fetchOne("SELECT COUNT(*) FROM xf_error_log"),
  'error_max_id' => (int)$db->fetchOne("SELECT COALESCE(MAX(error_id),0) FROM xf_error_log")
 ],
 'addon' => $db->fetchRow(
  "SELECT addon_id,version_id,version_string,active,is_processing,last_pending_action " .
  "FROM xf_addon WHERE addon_id='Newfi/Portal'"
 ),
 'source' => [
  'Pub/Controller/Portal.php' => srcMeta($portal . '/Pub/Controller/Portal.php'),
  'Service/PortalData.php' => srcMeta($portal . '/Service/PortalData.php'),
  '_data/templates.xml' => srcMeta($portal . '/_data/templates.xml')
 ],
 'templates' => $templates
];
echo "PORTAL200_INSPECT_JSON_BEGIN\n";
echo json_encode(
    $result,
    JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_INVALID_UTF8_SUBSTITUTE
);
echo "\nPORTAL200_INSPECT_JSON_END\n";
?>'''


IMPORT_PHP = r'''<?php
$root = '/var/www/forums/newfi/public';
require $root . '/src/XF.php';
\XF::start($root);
$app = \XF::setupApp('XF\\Pub\\App');
$addOn = $app->addOnManager()->getById('Newfi/Portal');
if (!$addOn || !$addOn->isInstalled()) {
    fwrite(STDERR, "ADDON_NOT_INSTALLED\n");
    exit(30);
}
$addOn->preRebuild();
$dataManager = $app->addOnDataManager();
$imported = [];
foreach ($dataManager->getDataTypeClasses() as $dataType) {
    $handler = $dataManager->getDataTypeHandler($dataType);
    $xml = $handler->openTypeFile($addOn->getDataDirectory());
    if (!$xml || !$xml->count()) {
        $handler->deleteAddOnData($addOn->addon_id);
        continue;
    }
    $start = 0;
    $guard = 0;
    while (true) {
        $last = $handler->importAddOnData($addOn->addon_id, $xml, $start, 0);
        if (!$last) { break; }
        if ($last <= $start || ++$guard > 100000) {
            fwrite(STDERR, "IMPORT_PROGRESS_INVALID\n");
            exit(31);
        }
        $start = $last;
    }
    $handler->deleteOrphanedAddOnData($addOn->addon_id, $xml);
    \XF::triggerRunOnce();
    $imported[] = $handler->getContainerTag();
}
$addOn->postDataImport();
$repo = $app->repository(\XF\Repository\StyleRepository::class);
$repo->updateAllStylesLastModifiedDateLater();
$addOn->postRebuild();
echo "PORTAL200_IMPORT_JSON=" . json_encode(
    ['result'=>'PASS','containers'=>$imported],
    JSON_UNESCAPED_SLASHES
) . "\n";
?>'''


ERRORS_PHP_TEMPLATE = r'''<?php
$root='/var/www/forums/newfi/public';
require $root.'/src/XF.php';
\XF::start($root);
$app=\XF::setupApp('XF\\Pub\\App');
$db=$app->db();
$b=%d;
$rows=$db->fetchAll(
    "SELECT error_id,exception_type,message,filename,line FROM xf_error_log WHERE error_id>? ORDER BY error_id",
    [$b]
);
echo json_encode(
    $rows,
    JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_INVALID_UTF8_SUBSTITUTE
);
?>'''


RUNTIME_VERIFY_PHP_TEMPLATE = r'''<?php
$state = '__STATE__';
$root = '/var/www/forums/newfi/public';
require $root . '/src/XF.php';
\XF::start($root);
$app = \XF::setupApp('XF\\Pub\\App');
$result = ['state'=>$state];
if ($state === 'new') {
    $service = new \Newfi\Portal\Service\PortalData($app);
    $portal = $service->build();
    $html = (string)$app->templater()->renderTemplate('public:newfi_portal', ['portal'=>$portal]);
    $result['sections'] = [];
    foreach (['forums','resources','media','events','dogs'] as $key) {
        $section = $portal[$key] ?? null;
        if (!is_array($section)) { fwrite(STDERR, "SECTION_MISSING\n"); exit(60); }
        $result['sections'][$key] = [
            'available'=>(bool)($section['available'] ?? false),
            'hasItems'=>(bool)($section['hasItems'] ?? false),
            'status'=>(string)($section['status'] ?? '')
        ];
    }
    $result['html_marker'] = strpos($html, 'data-newfi-portal200="1"') !== false;
    $result['locked_resources'] = strpos($html, 'data-portal-locked="resources"') !== false;
    $result['locked_media'] = strpos($html, 'data-portal-locked="media"') !== false;
    $result['html_bytes'] = strlen($html);
    if (!$result['html_marker']) { fwrite(STDERR, "HTML_MARKER_MISSING\n"); exit(61); }
    if ($result['sections']['resources']['available'] || $result['sections']['media']['available']) {
        fwrite(STDERR, "GUEST_PERMISSION_CONTRACT_CHANGED\n"); exit(62);
    }
    if (!$result['locked_resources'] || !$result['locked_media']) {
        fwrite(STDERR, "LOCKED_COPY_MISSING\n"); exit(63);
    }
    if (!$result['sections']['forums']['available'] || !$result['sections']['events']['available'] || !$result['sections']['dogs']['available']) {
        fwrite(STDERR, "PUBLIC_SECTION_UNAVAILABLE\n"); exit(64);
    }
} else if ($state === 'old') {
    $html = (string)$app->templater()->renderTemplate('public:newfi_portal', []);
    $result['html_marker'] = strpos($html, 'data-newfi-portal200') !== false;
    $result['old_copy'] = strpos($html, 'Закрытое пространство для общения, знаний и материалов сообщества.') !== false;
    $result['html_bytes'] = strlen($html);
    if ($result['html_marker'] || !$result['old_copy']) {
        fwrite(STDERR, "OLD_RENDER_CONTRACT\n"); exit(65);
    }
} else {
    exit(66);
}
echo "PORTAL200_RUNTIME_JSON=" . json_encode(
    $result,
    JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_INVALID_UTF8_SUBSTITUTE
) . "\n";
?>'''


HTTP_VERIFY_PY = r'''import html,json,re,subprocess,sys,urllib.parse
mode=sys.argv[1]
state=sys.argv[2]
if mode=='staging':
    host='newfi-staging.gram1.ru'; scheme='http'; port='80'; expected_status=503
elif mode=='prod':
    host='newfi.ru'; scheme='https'; port='443'; expected_status=200
else:
    raise SystemExit(40)
def fetch(url):
    cmd=[
        '/usr/bin/curl','-sS','-k','--max-time','30','--connect-timeout','10'
    ]
    if mode=='staging':
        cmd += ['--resolve',f'{host}:{port}:127.0.0.1']
    cmd += [
        '-o','-','-w','\n__PORTAL_STATUS__%{http_code}',url
    ]
    p=subprocess.run(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE,check=False)
    if p.returncode!=0: raise SystemExit(41)
    text=p.stdout.decode('utf-8','replace')
    marker='\n__PORTAL_STATUS__'
    if marker not in text: raise SystemExit(42)
    body,status=text.rsplit(marker,1)
    return int(status.strip()),body
root=f'{scheme}://{host}/'
status,body=fetch(root)
hrefs=[]
for raw in re.findall(r"href=[\"']([^\"']*css\.php[^\"']*)[\"']",body,re.I):
    raw=html.unescape(raw)
    hrefs.append(urllib.parse.urljoin(root,raw))
normal_css=[]
normal_status=[]
for u in hrefs:
    s,b=fetch(u)
    normal_status.append(s)
    normal_css.append(b)
normal_css_text='\n'.join(normal_css)
forced_css_marker=None
forced_css_status=None
if mode=='staging' and state=='new' and hrefs:
    u=urllib.parse.urlsplit(hrefs[0])
    pairs=urllib.parse.parse_qsl(u.query,keep_blank_values=True)
    out=[]
    found=False
    for k,v in pairs:
        if k=='css':
            found=True
            parts=[x for x in v.split(',') if x]
            if 'public:newfi_portal.less' not in parts:
                parts.append('public:newfi_portal.less')
            v=','.join(parts)
        out.append((k,v))
    if found:
        forced=urllib.parse.urlunsplit((u.scheme,u.netloc,u.path,urllib.parse.urlencode(out),u.fragment))
        forced_css_status,forced_body=fetch(forced)
        forced_css_marker='--newfi-portal200' in forced_body
result={
 'status':status,
 'bytes':len(body.encode()),
 'html_marker':'data-newfi-portal200="1"' in body,
 'css_marker':'--newfi-portal200' in normal_css_text,
 'old_copy':'Закрытое пространство для общения, знаний и материалов сообщества.' in body,
 'css_count':len(hrefs),
 'css_status':normal_status,
 'forced_css_status':forced_css_status,
 'forced_css_marker':forced_css_marker,
}
print('PORTAL200_HTTP_JSON='+json.dumps(result,ensure_ascii=False,sort_keys=True,separators=(',',':')))
if status!=expected_status: raise SystemExit(45)
if mode=='prod':
    if not hrefs or any(x!=200 for x in normal_status): raise SystemExit(46)
elif mode=='staging':
    if any(x!=200 for x in normal_status): raise SystemExit(46)
else:
    raise SystemExit(50)
if mode=='prod' and state=='new':
    if not result['html_marker'] or not result['css_marker']: raise SystemExit(47)
elif mode=='prod' and state=='old':
    if result['html_marker'] or result['css_marker'] or not result['old_copy']: raise SystemExit(48)
elif mode=='staging' and state=='new':
    if hrefs and (forced_css_status!=200 or not forced_css_marker): raise SystemExit(49)
elif mode=='staging' and state=='old':
    pass
else:
    raise SystemExit(50)
'''


def parse_inspect(text):
    b = "PORTAL200_INSPECT_JSON_BEGIN\n"
    e = "\nPORTAL200_INSPECT_JSON_END"
    if b not in text or e not in text:
        fail("INSPECT_MARKERS")
    try:
        return json.loads(text.split(b, 1)[1].split(e, 1)[0])
    except Exception:
        fail("INSPECT_JSON")


def inspect(target):
    return parse_inspect(
        qga(target, ["/usr/bin/php8.3"], input_bytes=INSPECT_PHP.encode(), timeout=120)
    )


def source_is_old(data):
    src = data["source"]
    for rel, (size, sha) in EXPECTED_OLD.items():
        row = src.get(rel)
        if not row or row.get("bytes") != size or row.get("sha256") != sha:
            return False
    return src.get("Service/PortalData.php") is None


def source_is_new(data):
    src = data["source"]
    for rel, (size, sha) in EXPECTED_NEW.items():
        row = src.get(rel)
        if not row or row.get("bytes") != size or row.get("sha256") != sha:
            return False
    return True


def template_state_new(data):
    rows = data.get("templates", [])
    rows0 = [r for r in rows if int(r.get("style_id", -1)) == 0]
    titles = [r.get("title") for r in rows0]
    by = {r.get("title"): r for r in rows0}
    return (
        titles == ["newfi_portal", "newfi_portal.less"]
        and int(by["newfi_portal"].get("html_marker", 0)) == 1
        and int(by["newfi_portal.less"].get("css_marker", 0)) == 1
    )


def app_contract(data, target):
    a = data.get("application", {})
    addon = data.get("addon") or {}
    expected = {
        "staging": ("0", "http://newfi-staging.gram1.ru"),
        "prod": ("1", "https://newfi.ru"),
    }[target]
    return (
        a.get("boardActive") == expected[0]
        and a.get("boardUrl") == expected[1]
        and a.get("indexRoute") == "portal/"
        and a.get("defaultStyleId") == "4"
        and addon.get("addon_id") == "Newfi/Portal"
        and int(addon.get("version_id", 0)) == 1000010
        and addon.get("version_string") == "1.0.0 Alpha 1"
        and int(addon.get("active", 0)) == 1
        and int(addon.get("is_processing", 1)) == 0
    )


def new_errors(target, baseline):
    raw = qga(
        target,
        ["/usr/bin/php8.3"],
        input_bytes=(ERRORS_PHP_TEMPLATE % int(baseline)).encode(),
        timeout=90,
    )
    try:
        return json.loads(raw)
    except Exception:
        fail("ERROR_LOG_JSON")


def assert_no_new_errors(target, baseline, phase):
    rows = new_errors(target, baseline)
    if rows:
        emit(
            "PORTAL200_NEW_XF_ERRORS",
            {
                "target": target,
                "phase": phase,
                "count": len(rows),
                "ids": [r.get("error_id") for r in rows],
            },
        )
        fail("NEW_XF_ERRORS")


def guest_backup(target):
    script = f'''set -eu
B={shlex.quote(BACKUP)}
P=/var/www/forums/newfi/public/src/addons/Newfi/Portal
test ! -e "$B"
install -d -m 700 "$B/Pub/Controller" "$B/_data"
cp -p -- "$P/Pub/Controller/Portal.php" "$B/Pub/Controller/Portal.php"
cp -p -- "$P/_data/templates.xml" "$B/_data/templates.xml"
test ! -e "$P/Service/PortalData.php"
printf 'PORTAL200_BACKUP=PASS\n'
'''
    out = qga(target, ["/bin/bash", "-lc", script], timeout=90)
    if "PORTAL200_BACKUP=PASS" not in out:
        fail("BACKUP_FAILED")


def guest_write(target, rel, data):
    dest = f"/var/www/forums/newfi/public/src/addons/Newfi/Portal/{rel}"
    parent = str(pathlib.PurePosixPath(dest).parent)
    script = (
        "set -eu; umask 022; "
        + "install -d -m 755 "
        + shlex.quote(parent)
        + "; T=$(mktemp "
        + shlex.quote(parent + "/.portal200.XXXXXX")
        + "); cat >\"$T\"; chmod 0644 \"$T\"; chown root:root \"$T\"; "
        + "mv -f -- \"$T\" "
        + shlex.quote(dest)
        + "; printf 'PORTAL200_WRITE=PASS\\n'"
    )
    out = qga(
        target,
        ["/bin/bash", "-lc", script],
        input_bytes=data,
        timeout=90,
    )
    if "PORTAL200_WRITE=PASS" not in out:
        fail("WRITE_FAILED")


def install_candidate(target):
    for rel in [
        "Pub/Controller/Portal.php",
        "Service/PortalData.php",
        "_data/templates.xml",
    ]:
        guest_write(target, rel, (CAND_ROOT / rel).read_bytes())


def restore_old(target):
    script = f'''set -eu
B={shlex.quote(BACKUP)}
P=/var/www/forums/newfi/public/src/addons/Newfi/Portal
test -f "$B/Pub/Controller/Portal.php"
test -f "$B/_data/templates.xml"
cp -p -- "$B/Pub/Controller/Portal.php" "$P/Pub/Controller/Portal.php"
cp -p -- "$B/_data/templates.xml" "$P/_data/templates.xml"
rm -f -- "$P/Service/PortalData.php"
rmdir -- "$P/Service" 2>/dev/null || true
printf 'PORTAL200_SOURCE_RESTORE=PASS\n'
'''
    out = qga(target, ["/bin/bash", "-lc", script], timeout=90)
    if "PORTAL200_SOURCE_RESTORE=PASS" not in out:
        fail("SOURCE_RESTORE_FAILED")


def import_addon(target):
    out = qga(
        target,
        ["/usr/bin/php8.3"],
        input_bytes=IMPORT_PHP.encode(),
        timeout=240,
    )
    if "PORTAL200_IMPORT_JSON=" not in out:
        fail("IMPORT_FAILED")


def runtime_verify(target, state):
    src = RUNTIME_VERIFY_PHP_TEMPLATE.replace("__STATE__", state)
    out = qga(target, ["/usr/bin/php8.3"], input_bytes=src.encode(), timeout=120)
    line = next(
        (x for x in out.splitlines() if x.startswith("PORTAL200_RUNTIME_JSON=")),
        None,
    )
    if not line:
        fail("RUNTIME_MARKER_MISSING")
    try:
        data = json.loads(line.split("=", 1)[1])
    except Exception:
        fail("RUNTIME_JSON")
    emit("PORTAL200_RUNTIME", {"target": target, **data})
    return data


def http_verify(target, state):
    argv = ["/usr/bin/python3", "-", target, state]
    if target == "prod":
        try:
            p = subprocess.run(
                argv,
                input=HTTP_VERIFY_PY.encode(),
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                timeout=180,
                check=False,
            )
        except subprocess.TimeoutExpired:
            fail("HTTP_PROD_TIMEOUT")
        out = p.stdout.decode("utf-8", "replace")
        err = p.stderr.decode("utf-8", "replace")
        line = next(
            (x for x in out.splitlines() if x.startswith("PORTAL200_HTTP_JSON=")),
            None,
        )
        data = None
        if line:
            try:
                data = json.loads(line.split("=", 1)[1])
                emit("PORTAL200_HTTP", {"target": target, "state": state, **data})
            except Exception:
                fail("HTTP_JSON")
        if p.returncode != 0:
            emit(
                "PORTAL200_HTTP_EXEC",
                {
                    "target": target,
                    "state": state,
                    "rc": p.returncode,
                    "stderr": err[:800],
                },
            )
            fail("HTTP_PROD_RC")
        if data is None:
            fail("HTTP_MARKER_MISSING")
        return data

    out = qga(
        target,
        argv,
        input_bytes=HTTP_VERIFY_PY.encode(),
        timeout=180,
    )
    line = next(
        (x for x in out.splitlines() if x.startswith("PORTAL200_HTTP_JSON=")),
        None,
    )
    if not line:
        fail("HTTP_MARKER_MISSING")
    try:
        data = json.loads(line.split("=", 1)[1])
    except Exception:
        fail("HTTP_JSON")
    emit("PORTAL200_HTTP", {"target": target, "state": state, **data})
    return data


def verify_new(target, baseline):
    data = inspect(target)
    if not app_contract(data, target):
        fail("APP_CONTRACT_NEW")
    if not source_is_new(data):
        fail("SOURCE_NEW_MISMATCH")
    if not template_state_new(data):
        fail("TEMPLATE_NEW_MISMATCH")
    runtime_verify(target, "new")
    http_verify(target, "new")
    assert_no_new_errors(target, baseline, "verify-new")
    return inspect(target)


def verify_old(target, baseline, old_templates, strict_errors=True):
    data = inspect(target)
    if not app_contract(data, target):
        fail("APP_CONTRACT_OLD")
    if not source_is_old(data):
        fail("SOURCE_OLD_MISMATCH")
    if data.get("templates") != old_templates:
        fail("TEMPLATE_ROLLBACK_MISMATCH")
    if target == "prod":
        runtime_verify(target, "old")
    http_verify(target, "old")
    if strict_errors:
        assert_no_new_errors(target, baseline, "verify-old")
    return inspect(target)


def lint_php(target, source, label):
    out = qga(
        target,
        ["/usr/bin/php8.3", "-l"],
        input_bytes=source.encode(),
        timeout=90,
    )
    if "No syntax errors detected" not in out:
        fail("PHP_LINT_" + label)


def cluster_guard():
    p = run(["/usr/sbin/qm", "status", "210"], timeout=30)
    if "status: running" not in p.stdout.decode():
        fail("VM210_NOT_RUNNING")
    p = run(
        [
            "/usr/bin/ssh",
            "-T",
            "-o",
            "BatchMode=yes",
            "-o",
            "StrictHostKeyChecking=yes",
            "-o",
            "UpdateHostKeys=no",
            "-o",
            "ConnectTimeout=10",
            "root@pve03",
            "/usr/sbin/qm status 211",
        ],
        timeout=40,
    )
    if "status: running" not in p.stdout.decode():
        fail("VM211_NOT_RUNNING")
    emit("PORTAL200_CLUSTER", "PASS")


def result_contract(status, changes, rollback_started, rollback_restored):
    emit(
        "HOMELAB_RESULT_CONTRACT",
        {
            "version": 1,
            "command_id": CID,
            "status": status,
            "changes_made": bool(changes),
            "rollback_started": bool(rollback_started),
            "rollback_restored": rollback_restored,
        },
    )


emit("NEWFI200_BEGIN")
emit("COMMAND_ID", CID)
emit("PORTAL200_REMAINING_FULL_PRODUCT_COMMANDS_BEFORE", 22)
emit(
    "PORTAL200_FLOW",
    "candidate->staging-apply->rollback-proof->staging-reapply->git-push->production",
)

staging_backup = False
staging_mutated = False
staging_final_new = False
prod_backup = False
prod_mutated = False
git_files_changed = False
commit_created = False
push_confirmed = False
failure_rollback_started = False
failure_rollback_restored = None
old_git_bytes = {}
new_head = None
s_error = None
p_error = None
s_old_templates = None
p_old_templates = None

try:
    compile(HTTP_VERIFY_PY, "<portal200-http-verify>", "exec")
    for rel, (size, sha) in EXPECTED_NEW.items():
        p = CAND_ROOT / rel
        if not p.is_file() or file_meta(p) != (size, sha):
            fail("CANDIDATE_IDENTITY")
    tree = ET.parse(CAND_ROOT / "_data/templates.xml")
    titles = sorted(x.attrib.get("title") for x in tree.getroot().findall("template"))
    if titles != ["newfi_portal", "newfi_portal.less"]:
        fail("CANDIDATE_TEMPLATE_SET")
    service = (CAND_ROOT / "Service/PortalData.php").read_text("utf-8")
    if any(
        token in service
        for token in [
            "\\XF::db(",
            "->save(",
            "->delete(",
            "INSERT ",
            "UPDATE ",
            "DELETE ",
            "REPLACE ",
        ]
    ):
        fail("CANDIDATE_MUTATION_SCAN")
    emit("PORTAL200_CANDIDATE_IDENTITY", "PASS")
    emit(
        "PORTAL200_ORCHESTRATION_FIXES",
        {
            "staging_503_without_css_links": True,
            "failure_status": "FAIL",
            "prod_http_via_pve01_public_route": True,
            "css_href_regex_fixed": True,
        },
    )

    branch = git("branch", "--show-current")
    head = git("rev-parse", "HEAD")
    status = git("status", "--porcelain=v1", "--untracked-files=all")
    remote_out = git("ls-remote", "origin", f"refs/heads/{BRANCH}")
    remote = remote_out.split()[0] if remote_out else ""
    if branch != BRANCH or head != OLD_HEAD or remote != OLD_HEAD or status:
        fail("GIT_PRESTATE")
    emit(
        "PORTAL200_GIT_PRESTATE",
        {"branch": branch, "head": head, "remote": remote, "clean": True},
    )
    cluster_guard()

    lint_php("staging", INSPECT_PHP, "INSPECT_STAGING")
    lint_php("staging", IMPORT_PHP, "IMPORT_STAGING")
    lint_php("staging", RUNTIME_VERIFY_PHP_TEMPLATE.replace("__STATE__", "new"), "RUNTIME_STAGING")
    lint_php("prod", INSPECT_PHP, "INSPECT_PROD")
    lint_php("prod", IMPORT_PHP, "IMPORT_PROD")
    lint_php("prod", RUNTIME_VERIFY_PHP_TEMPLATE.replace("__STATE__", "new"), "RUNTIME_PROD")
    emit("PORTAL200_RUNTIME_HELPER_LINT", "PASS")

    s0 = inspect("staging")
    p0 = inspect("prod")
    if not app_contract(s0, "staging") or not app_contract(p0, "prod"):
        fail("APP_PRESTATE")
    if not source_is_old(s0) or not source_is_old(p0):
        fail("SOURCE_PRESTATE")
    if s0.get("templates") != p0.get("templates"):
        fail("TEMPLATE_PRESTATE_PARITY")
    s_error = int(s0["application"]["error_max_id"])
    p_error = int(p0["application"]["error_max_id"])
    s_old_templates = s0["templates"]
    p_old_templates = p0["templates"]
    emit("PORTAL200_ERROR_BASELINE", {"staging": s_error, "prod": p_error})
    http_verify("staging", "old")
    runtime_verify("prod", "old")
    http_verify("prod", "old")
    assert_no_new_errors("staging", s_error, "preflight")
    assert_no_new_errors("prod", p_error, "preflight")
    emit("PORTAL200_PREFLIGHT", "PASS")

    guest_backup("staging")
    staging_backup = True
    staging_mutated = True
    install_candidate("staging")
    import_addon("staging")
    verify_new("staging", s_error)
    emit("PORTAL200_STAGING_FIRST_APPLY", "PASS")

    restore_old("staging")
    import_addon("staging")
    verify_old("staging", s_error, s_old_templates)
    staging_mutated = False
    emit("PORTAL200_STAGING_ROLLBACK_PROOF", "PASS")

    staging_mutated = True
    install_candidate("staging")
    import_addon("staging")
    verify_new("staging", s_error)
    staging_final_new = True
    emit("PORTAL200_STAGING_FINAL", "PASS")

    for rel in EXPECTED_NEW:
        dest = GIT_PORTAL / rel
        old_git_bytes[str(rel)] = dest.read_bytes() if dest.exists() else None
        dest.parent.mkdir(parents=True, exist_ok=True)
        shutil.copyfile(CAND_ROOT / rel, dest)
    git_files_changed = True

    if git("diff", "--check"):
        fail("GIT_DIFF_CHECK")
    status_lines = git("status", "--porcelain=v1", "--untracked-files=all").splitlines()
    actual_paths = sorted(line[3:] for line in status_lines if len(line) >= 4)
    if actual_paths != sorted(EXPECTED_GIT_PATHS):
        fail("GIT_SCOPE")
    git("add", "--", *EXPECTED_GIT_PATHS)
    staged = sorted(x for x in git("diff", "--cached", "--name-only").splitlines() if x)
    if staged != sorted(EXPECTED_GIT_PATHS):
        fail("GIT_STAGED_SCOPE")
    git("commit", "-m", f"feat: build data-backed Newfi portal [{CID}]")
    commit_created = True
    new_head = git("rev-parse", "HEAD")
    if new_head == OLD_HEAD:
        fail("GIT_COMMIT_HEAD")
    if git("status", "--porcelain=v1", "--untracked-files=all"):
        fail("GIT_POSTCOMMIT_DIRTY")
    git("push", "origin", f"HEAD:refs/heads/{BRANCH}", timeout=180)
    remote_out = git("ls-remote", "origin", f"refs/heads/{BRANCH}")
    remote_head = remote_out.split()[0] if remote_out else ""
    if remote_head != new_head:
        fail("GIT_REMOTE_HEAD")
    push_confirmed = True
    emit(
        "PORTAL200_GIT_PROMOTION",
        {
            "old_head": OLD_HEAD,
            "new_head": new_head,
            "remote_head": remote_head,
            "result": "PASS",
        },
    )

    p_pre = inspect("prod")
    if not source_is_old(p_pre) or p_pre.get("templates") != p_old_templates:
        fail("PROD_DRIFT_BEFORE_APPLY")
    if int(p_pre["application"]["error_max_id"]) != p_error:
        fail("PROD_ERROR_DRIFT")
    guest_backup("prod")
    prod_backup = True
    prod_mutated = True
    install_candidate("prod")
    import_addon("prod")
    verify_new("prod", p_error)
    emit("PORTAL200_PRODUCTION", "PASS")

    sf = verify_new("staging", s_error)
    pf = verify_new("prod", p_error)
    if sf["source"] != pf["source"]:
        fail("FINAL_SOURCE_PARITY")
    if sf["templates"] != pf["templates"]:
        fail("FINAL_TEMPLATE_PARITY")
    if git("rev-parse", "HEAD") != new_head:
        fail("FINAL_LOCAL_HEAD")
    remote_out = git("ls-remote", "origin", f"refs/heads/{BRANCH}")
    if not remote_out or remote_out.split()[0] != new_head:
        fail("FINAL_REMOTE_HEAD")
    if git("status", "--porcelain=v1", "--untracked-files=all"):
        fail("FINAL_GIT_DIRTY")
    cluster_guard()
    emit(
        "PORTAL200_FINAL_EVIDENCE",
        {
            "commit": new_head,
            "source_files": 3,
            "templates": 2,
            "staging_error_baseline": s_error,
            "prod_error_baseline": p_error,
        },
    )
    emit("PORTAL200_PORTAL", "PASS")
    emit("PORTAL200_STAGING", "PASS")
    emit("PORTAL200_PRODUCTION", "PASS")
    emit("PORTAL200_REMAINING_FULL_PRODUCT_COMMANDS_AFTER_PASS", 21)
    result_contract("OK", True, False, None)
    emit("NEWFI200_END")
    raise SystemExit(0)

except PortalError as exc:
    code = str(exc)
    emit("PORTAL200_FAILURE", code)

    if push_confirmed:
        if prod_mutated and prod_backup:
            failure_rollback_started = True
            try:
                restore_old("prod")
                import_addon("prod")
                verify_old("prod", p_error, p_old_templates, strict_errors=False)
                prod_mutated = False
                failure_rollback_restored = False
                emit("PORTAL200_FAILURE_PROD_ROLLBACK", "PASS_PARTIAL_OVERALL_STATE_CHANGED")
            except Exception:
                failure_rollback_restored = False
                emit("PORTAL200_FAILURE_PROD_ROLLBACK", "FAIL")
    else:
        if staging_mutated and staging_backup:
            failure_rollback_started = True
            try:
                restore_old("staging")
                import_addon("staging")
                verify_old("staging", s_error, s_old_templates, strict_errors=False)
                staging_mutated = False
                staging_final_new = False
                failure_rollback_restored = True
                emit("PORTAL200_FAILURE_STAGING_ROLLBACK", "PASS")
            except Exception:
                failure_rollback_restored = False
                emit("PORTAL200_FAILURE_STAGING_ROLLBACK", "FAIL")

        if git_files_changed and not commit_created:
            try:
                for rel, old in old_git_bytes.items():
                    dest = GIT_PORTAL / rel
                    if old is None:
                        if dest.exists():
                            dest.unlink()
                    else:
                        dest.write_bytes(old)
                service_dir = GIT_PORTAL / "Service"
                try:
                    service_dir.rmdir()
                except OSError:
                    pass
                emit("PORTAL200_FAILURE_GIT_WORKTREE_RESTORE", "PASS")
            except Exception:
                emit("PORTAL200_FAILURE_GIT_WORKTREE_RESTORE", "FAIL")

        if commit_created:
            emit(
                "PORTAL200_FAILURE_GIT_HISTORY",
                "COMMIT_CREATED_NO_DESTRUCTIVE_RESET_FRESH_ID_REQUIRED",
            )

    persisted = bool(push_confirmed or commit_created or staging_final_new or prod_mutated)
    if failure_rollback_started and failure_rollback_restored is None:
        failure_rollback_restored = False
    result_contract(
        "FAIL",
        persisted,
        failure_rollback_started,
        failure_rollback_restored,
    )
    emit("PORTAL200_ROADMAP_REMAINS", 22)
    emit("NEWFI200_END")
    raise SystemExit(1)

except Exception:
    emit("PORTAL200_FAILURE", "UNEXPECTED_EXCEPTION")
    result_contract(
        "FAIL",
        bool(push_confirmed or commit_created or staging_final_new or prod_mutated),
        False,
        None,
    )
    emit("PORTAL200_ROADMAP_REMAINS", 22)
    emit("NEWFI200_END")
    raise SystemExit(1)

CANDIDATE_LAYER_1_FULL_END
CANDIDATE_LAYER_2_SOURCE=carrier
CANDIDATE_LAYER_2_SHA256=573c5c94fe59ed072e094281668ce9847dc4a9882019a2b89d8ba5f190d92176
CANDIDATE_LAYER_2_BYTES=17209
CANDIDATE_LAYER_2_RELEVANT_BEGIN
exit /b %RC%
# NEWFI54_POWERSHELL_BODY
$ErrorActionPreference='Stop'
$plinkPath='C:\Program Files\PuTTY\plink.exe'
$remotePath=Join-Path $env:TEMP 'NEWFI54_PORTAL200_APPLY3_REMOTE.txt'
$stdoutPath=Join-Path $env:TEMP 'NEWFI54_PORTAL200_APPLY3_STDOUT.txt'
$stderrPath=Join-Path $env:TEMP 'NEWFI54_PORTAL200_APPLY3_STDERR.txt'
$statusPath=Join-Path $env:TEMP 'NEWFI54_PORTAL200_APPLY3_STATUS.txt'

foreach($p in @($remotePath,$stdoutPath,$stderrPath,$statusPath)) {
    Remove-Item -LiteralPath $p -Force -ErrorAction SilentlyContinue
}
if(-not (Test-Path -LiteralPath $plinkPath -PathType Leaf)) {
    [IO.File]::WriteAllText($statusPath,'STOP=PLINK_MISSING'+[Environment]::NewLine)
    exit 2
$remote=@'
(set -eu; PATH=/usr/sbin:/usr/bin:/sbin:/bin; LC_ALL=C; export PATH LC_ALL; printf 'TARGET=pve01 STEP=NEWFI54 PORTAL200_APPLY3\n'; case $(id -u):$(hostname -s) in 0:pve01) ;; *) exit 2;; esac; test -f /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md && test -r /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md && test -s /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md && cat -- /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md >/dev/null || exit 2; test -f /etc/pve/31_HOMELAB_REFERENCE.md && test -r /etc/pve/31_HOMELAB_REFERENCE.md && test -s /etc/pve/31_HOMELAB_REFERENCE.md && cat -- /etc/pve/31_HOMELAB_REFERENCE.md >/dev/null || exit 2; printf '\033[31mERROR_REGISTER_CHECK=OK\033[0m\n\033[31mREFERENCE_CHECK=OK\033[0m\n'; umask 077; unset BASH_ENV ENV PYTHONPATH PYTHONHOME; test -f /usr/local/sbin/homelab-chat-run && test ! -L /usr/local/sbin/homelab-chat-run && test -x /usr/local/sbin/homelab-chat-run && test -x /usr/bin/python3 || exit 2; test -d /root/newfi-portal200-candidate || exit 2; d=/root/newfi-run-NEWFI-260907-PORTAL200-APPLY3; mkdir -m 700 -- $d 2>/dev/null || exit 2; printf 'COMMAND_ID=NEWFI-260907-PORTAL200-APPLY3\nSTATE=RESERVED_BEFORE_EXECUTION\n' >$d/attempt.txt; if /usr/local/sbin/homelab-chat-run NEWFI-260907-PORTAL200-APPLY3 change newfi-portal200-deploy -- /usr/bin/python3 -I -B -c 'import base64,hashlib,sys,zlib;d=zlib.decompress(base64.b64decode(sys.argv[1]));assert hashlib.sha256(d).hexdigest()==sys.argv[2];exec(d)' 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 97a1807a275e5d309591f7a7e044d7d56ba5b2f4c6a068df6353bae147707184 >$d/wrapper.log 2>&1; then rc=0; else rc=$?; fi; printf 'COMMAND_ID=NEWFI-260907-PORTAL200-APPLY3\n' >$d/receipt.txt; printf 'WRAPPER_RC=' >>$d/receipt.txt; echo $rc >>$d/receipt.txt; printf 'PUBLICATION=GITEA_RUNTIME_EXPECTED\n' >>$d/receipt.txt; exit $rc)
'@
$remote=$remote.Replace("`r`n","`n").TrimEnd("`n")+"`n"
[IO.File]::WriteAllText($remotePath,$remote,[Text.UTF8Encoding]::new($false))
$h=[Security.Cryptography.SHA256]::Create()
try {
    $actual=([BitConverter]::ToString($h.ComputeHash([IO.File]::ReadAllBytes($remotePath)))).Replace('-','').ToLowerInvariant()
} finally {$h.Dispose()}
$p=Start-Process -FilePath $plinkPath -ArgumentList $args -NoNewWindow -Wait -PassThru -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath
$rc=$p.ExitCode
$status=@(
    'COMMAND_ID=NEWFI-260907-PORTAL200-APPLY3',
    'PLINK_RC='+$rc,
    'REMOTE_RESULT=VERIFY_IN_GITEA',
    'LOCAL_STDOUT='+$stdoutPath,
    'LOCAL_STDERR='+$stderrPath
) -join [Environment]::NewLine
[IO.File]::WriteAllText($statusPath,$status+[Environment]::NewLine,[Text.UTF8Encoding]::new($false))
exit $rc
CANDIDATE_LAYER_2_RELEVANT_END
CANDIDATE_LAYER_2_FULL_BEGIN
@echo off
setlocal DisableDelayedExpansion
set "SELF=%~f0"
"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "$ErrorActionPreference='Stop'; $t=[IO.File]::ReadAllText($env:SELF).Replace([string][char]13,''); $m='# NEWFI54_POWERSHELL_BODY'+[char]10; $i=$t.IndexOf($m); if($i -lt 0){exit 2}; $b=$t.Substring($i+$m.Length); $h=[Security.Cryptography.SHA256]::Create(); $d=([BitConverter]::ToString($h.ComputeHash([Text.Encoding]::UTF8.GetBytes($b)))).Replace('-','').ToLowerInvariant(); $h.Dispose(); if($d -ne 'fe66d389a08cceef3f1b8d656e926cc75a85f71d1e44b8b8b29ee9fa4f785dfc'){exit 2}; & ([ScriptBlock]::Create($b));"
set "RC=%ERRORLEVEL%"
exit /b %RC%
# NEWFI54_POWERSHELL_BODY
$ErrorActionPreference='Stop'
$plinkPath='C:\Program Files\PuTTY\plink.exe'
$remotePath=Join-Path $env:TEMP 'NEWFI54_PORTAL200_APPLY3_REMOTE.txt'
$stdoutPath=Join-Path $env:TEMP 'NEWFI54_PORTAL200_APPLY3_STDOUT.txt'
$stderrPath=Join-Path $env:TEMP 'NEWFI54_PORTAL200_APPLY3_STDERR.txt'
$statusPath=Join-Path $env:TEMP 'NEWFI54_PORTAL200_APPLY3_STATUS.txt'

foreach($p in @($remotePath,$stdoutPath,$stderrPath,$statusPath)) {
    Remove-Item -LiteralPath $p -Force -ErrorAction SilentlyContinue
}

if(-not (Test-Path -LiteralPath $plinkPath -PathType Leaf)) {
    [IO.File]::WriteAllText($statusPath,'STOP=PLINK_MISSING'+[Environment]::NewLine)
    exit 2
}
if(@(Get-Process -Name 'pageant*' -ErrorAction SilentlyContinue).Count -lt 1) {
    [IO.File]::WriteAllText($statusPath,'STOP=PAGEANT_NOT_RUNNING'+[Environment]::NewLine)
    exit 2
}

$remote=@'
(set -eu; PATH=/usr/sbin:/usr/bin:/sbin:/bin; LC_ALL=C; export PATH LC_ALL; printf 'TARGET=pve01 STEP=NEWFI54 PORTAL200_APPLY3\n'; case $(id -u):$(hostname -s) in 0:pve01) ;; *) exit 2;; esac; test -f /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md && test -r /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md && test -s /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md && cat -- /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md >/dev/null || exit 2; test -f /etc/pve/31_HOMELAB_REFERENCE.md && test -r /etc/pve/31_HOMELAB_REFERENCE.md && test -s /etc/pve/31_HOMELAB_REFERENCE.md && cat -- /etc/pve/31_HOMELAB_REFERENCE.md >/dev/null || exit 2; printf '\033[31mERROR_REGISTER_CHECK=OK\033[0m\n\033[31mREFERENCE_CHECK=OK\033[0m\n'; umask 077; unset BASH_ENV ENV PYTHONPATH PYTHONHOME; test -f /usr/local/sbin/homelab-chat-run && test ! -L /usr/local/sbin/homelab-chat-run && test -x /usr/local/sbin/homelab-chat-run && test -x /usr/bin/python3 || exit 2; test -d /root/newfi-portal200-candidate || exit 2; d=/root/newfi-run-NEWFI-260907-PORTAL200-APPLY3; mkdir -m 700 -- $d 2>/dev/null || exit 2; printf 'COMMAND_ID=NEWFI-260907-PORTAL200-APPLY3\nSTATE=RESERVED_BEFORE_EXECUTION\n' >$d/attempt.txt; if /usr/local/sbin/homelab-chat-run NEWFI-260907-PORTAL200-APPLY3 change newfi-portal200-deploy -- /usr/bin/python3 -I -B -c 'import base64,hashlib,sys,zlib;d=zlib.decompress(base64.b64decode(sys.argv[1]));assert hashlib.sha256(d).hexdigest()==sys.argv[2];exec(d)' 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 97a1807a275e5d309591f7a7e044d7d56ba5b2f4c6a068df6353bae147707184 >$d/wrapper.log 2>&1; then rc=0; else rc=$?; fi; printf 'COMMAND_ID=NEWFI-260907-PORTAL200-APPLY3\n' >$d/receipt.txt; printf 'WRAPPER_RC=' >>$d/receipt.txt; echo $rc >>$d/receipt.txt; printf 'PUBLICATION=GITEA_RUNTIME_EXPECTED\n' >>$d/receipt.txt; exit $rc)
'@
$remote=$remote.Replace("`r`n","`n").TrimEnd("`n")+"`n"
[IO.File]::WriteAllText($remotePath,$remote,[Text.UTF8Encoding]::new($false))

$h=[Security.Cryptography.SHA256]::Create()
try {
    $actual=([BitConverter]::ToString($h.ComputeHash([IO.File]::ReadAllBytes($remotePath)))).Replace('-','').ToLowerInvariant()
} finally {$h.Dispose()}

if($actual -ne '71da2a66224075527d8aa04c1281fdb7a2b9f078d76c349fc05b48a4b803a4c3') {
    [IO.File]::WriteAllText($statusPath,'STOP=REMOTE_HASH_MISMATCH'+[Environment]::NewLine)
    exit 2
}

$args=@('-batch','-agent','-ssh','-P','22','-m',$remotePath,'root@[PRIVATE_IP]')
$p=Start-Process -FilePath $plinkPath -ArgumentList $args -NoNewWindow -Wait -PassThru -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath
$rc=$p.ExitCode

$status=@(
    'COMMAND_ID=NEWFI-260907-PORTAL200-APPLY3',
    'PLINK_RC='+$rc,
    'REMOTE_RESULT=VERIFY_IN_GITEA',
    'LOCAL_STDOUT='+$stdoutPath,
    'LOCAL_STDERR='+$stderrPath
) -join [Environment]::NewLine
[IO.File]::WriteAllText($statusPath,$status+[Environment]::NewLine,[Text.UTF8Encoding]::new($false))

exit $rc

CANDIDATE_LAYER_2_FULL_END
RECOVERED_VERSION_IDS=
RECOVERED_VERSION_STRINGS==
RECOVERED_SHA256_COUNT=8
RECOVERED_SHA256=619d489f7cc3b461479c702fa8825097ae22d3c6cfeb911e9f14ea1de9c36d6d,4da03f26294ab6d163a1a746dee4bb6a82d7d4f39e66b24246bc0eb4c455dd1d,1650c3d868d4bc47703f1b75f1f179cd25309b2d3b20a2b848e81a4423c00861,53909e753ea5a011550fd280ad1c342a74ca9949be092dfbd95244243cae92f3,c29c04f8b937284e821c87957653e87c2f6ac014fac30648a0f6b18e84392adf,fe66d389a08cceef3f1b8d656e926cc75a85f71d1e44b8b8b29ee9fa4f785dfc,97a1807a275e5d309591f7a7e044d7d56ba5b2f4c6a068df6353bae147707184,71da2a66224075527d8aa04c1281fdb7a2b9f078d76c349fc05b48a4b803a4c3
RECOVERY_RESULT=EXACT_OLD_APPLY3_CARRIER_DECODED
LOCAL_APPLY3_EXACT_CARRIER_RECOVERY_END=true

OUTPUT_END
CHAT_OUTPUT_END
