chore(consistency): sync task status from DB [auto]
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Reject generated-only commits / generated-commit-guard (push) Successful in 7s
Python Tests / pytest (push) Successful in 25s

Updated by fix-consistency on 2026-10-11:
  - update .custodian-brief.md for the-custodian
This commit is contained in:
custodian-sync 2026-10-11 21:30:44 +02:00
parent ebaa38bd56
commit 6fe7201fd5
15 changed files with 411 additions and 10 deletions

View file

@ -2,22 +2,22 @@
# Custodian Brief — the-custodian
**Domain:** infotech
**Last synced:** 2026-10-11 19:10 UTC
**Last synced:** 2026-10-11 19:30 UTC
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
## Active Workstreams
### Deterministic security flow: standing authorizations, flow report, and agent re-engagement
Progress: 2/7 done | workplan_id: `ab288f92-d364-5c24-9362-aee451f266c3`
Progress: 3/7 done | workplan_id: `ab288f92-d364-5c24-9362-aee451f266c3`
**Open tasks:**
- ! Schedule the flow report and decision sync `1be0569c`
*(wait: Awaiting daily schedule owner acceptance; workstation user service bus unavailable. Handoff 117e538e-da06-48b8-ba68-b82300a832e7.)*
- ! Encode the standing change rule as a policy-nexus guardrail `5dd52156`
*(wait: policy-nexus owns guardrail publication; handoff written as PNEX-WP-0006.)*
- ! Attended window #1 `e3da807f`
*(wait: Founder runs docs/attended-window/2026-10-11-checklist.md (about 2 h).)*
- ► Schedule the flow report and decision sync `1be0569c`
- ► Stop agent-authored index and sync commits `8a3dc219`
- · Re-engage the coding agent with the renewed goal `91ec924c`
### Separate agent credentials and establish supervised privileged execution
Progress: 2/5 done | workplan_id: `a98a9f34-83b4-5c8c-8107-e05f7806d50d`

View file

@ -0,0 +1,33 @@
name: Reject generated-only commits
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
generated-commit-guard:
runs-on: ubuntu-latest
steps:
- name: Check every proposed commit
env:
PUSH_BEFORE: ${{ github.event.before }}
PR_BASE: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.sha }}
run: |
set -eu
apt-get update -qq >/dev/null
apt-get install -y -qq git python3 >/dev/null
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
git clone --quiet "https://forgejo.coulomb.social/${GITHUB_REPOSITORY}.git" "$WORK/repo"
cd "$WORK/repo"
git fetch --quiet origin "$HEAD_SHA"
git checkout --quiet --detach "$HEAD_SHA"
BASE="${PR_BASE:-${PUSH_BEFORE:-}}"
if [ -z "$BASE" ] || [ "$BASE" = 0000000000000000000000000000000000000000 ]; then
BASE="$(git rev-parse HEAD^)"
fi
git fetch --quiet origin "$BASE"
python3 scripts/check_generated_only_commit.py --range "$BASE..$HEAD_SHA"

View file

@ -12,6 +12,7 @@ on:
- main
paths:
- "tools/**"
- "scripts/**"
- "tests/**"
- "canon/standards/**"
- ".forgejo/workflows/python-tests.yaml"
@ -26,7 +27,7 @@ jobs:
run: |
set -eu
apt-get update -qq >/dev/null
apt-get install -y -qq python3 python3-pip python3-yaml \
apt-get install -y -qq git python3 python3-pip python3-yaml \
python3-jsonschema python3-pytest wget >/dev/null
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
REF="${GITHUB_SHA:-main}"; SHORT="${REF:0:7}"

View file

@ -1,4 +1,14 @@
# the-custodian top-level Makefile
.PHONY: security-flow-daily security-flow-check generated-commit-check
security-flow-daily: ## Sync resolved decisions and notify changed actionable work
python3 scripts/security_flow_daily.py --apply
security-flow-check: ## Run the deterministic security flow and generated commit tests
python3 -m pytest tests/test_security_flow_daily.py tests/test_generated_commit_guard.py -q
generated-commit-check: ## Reject staged generated-only changes
python3 scripts/check_generated_only_commit.py --staged
#
# Custodian agent SSH identity
# ----------------------------

View file

@ -498,9 +498,9 @@
| task | CUST-WP-0078-T04 | done | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md |
| task | CUST-WP-0079-T01 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md |
| task | CUST-WP-0079-T02 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md |
| task | CUST-WP-0079-T03 | todo | — | workplans/CUST-WP-0079-deterministic-security-flow.md |
| task | CUST-WP-0079-T03 | progress | — | workplans/CUST-WP-0079-deterministic-security-flow.md |
| task | CUST-WP-0079-T04 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md |
| task | CUST-WP-0079-T05 | todo | — | workplans/CUST-WP-0079-deterministic-security-flow.md |
| task | CUST-WP-0079-T05 | progress | — | workplans/CUST-WP-0079-deterministic-security-flow.md |
| task | CUST-WP-0079-T06 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md |
| task | CUST-WP-0079-T07 | todo | — | workplans/CUST-WP-0079-deterministic-security-flow.md |
| task | THE-WP-0001-T01 | done | — | workplans/THE-WP-0001-federation-interface.md |

View file

@ -0,0 +1,34 @@
# Daily red-lane batch — 2026-10-11
One package for founder disposition; no per-item approval request has been sent.
This package contains the new publication item prepared during the renewed
critical-path run. It does not replace existing decisions or orange-window acts.
## PNEX-WP-0006 — publish build-mode authorization
Publish the accepted Custodian build-mode change rule as a Policy Nexus page
and its canon-derived JSON artifact:
- `/standards/build-mode-change-authorization/v0.1/`
- `/guardrails/build-mode-change-authorization.json`
Prepared implementation: `policy-nexus/tools/guardrails.py`, publication entry,
expiry header and digest-bound release verification. Sixty tests pass. Local
build renders 74 documents and the JSON equals the canonical YAML block.
Native publication has not run; both tasks remain waiting.
Review diff: `/tmp/pnex6/reviewed.patch`; validation:
`policy-nexus/docs/evidence/2026-10-11-build-mode-guardrail/validation.json`.
Canon SHA-256: `7135ad8e36041f6dc9633c029109b4751be275f912cec44fc462c9f55befaab8`.
Artifact SHA-256: `a86462a57bb1df5804d3088881af22569082d43ff68bcab2b67cbe56b6fc46cd`.
Abort owner: policy-nexus. Before execution, bind the release image and exact
source commit, review the resulting native diff, and retain the prior deployment
revision. Rollback restores that image/revision and retains immutable publication
paths. Verify the live expiry, JSON equality, artifact digest and installed source
revision. No new spend ceiling or credential/access scope is proposed.
Release remains blocked independently by an unreviewed
`net-kingdom/canon/standards/security-layer-model_v0.9.md` source-inventory entry.
The source owner must resolve its publication disposition before the full audit
passes; batch acceptance cannot bypass that release check.

View file

@ -0,0 +1,9 @@
[Unit]
Description=Custodian daily security decision sync and flow report
[Service]
Type=oneshot
WorkingDirectory=%h/the-custodian
ExecStart=/usr/bin/python3 %h/the-custodian/scripts/security_flow_daily.py --apply
NoNewPrivileges=yes
UMask=0077

View file

@ -0,0 +1,10 @@
[Unit]
Description=Daily Custodian security flow
[Timer]
OnCalendar=*-*-* 08:00:00
Persistent=true
RandomizedDelaySec=10m
[Install]
WantedBy=timers.target

View file

@ -0,0 +1,12 @@
{
"case_id": "case:cross-owner:CUST-WP-0079-T03:activity-core",
"scenario_type": "cross_owner_wait",
"beneficiary_workplan_id": "CUST-WP-0079",
"beneficiary_task_id": "CUST-WP-0079-T03",
"obligor_repo": "activity-core",
"obligor_workplan_id": null,
"state": "waiting",
"reason": "Awaiting schedule owner acceptance and named non-residual owner task; workstation user service bus unavailable. No founder act requested.",
"proposal_message_id": "117e538e-da06-48b8-ba68-b82300a832e7",
"progress_receipt_id": "8edea261-4841-4db7-bc0e-1db8021106a6"
}

View file

@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Reject commits consisting only of generated indexes/projection refreshes.
Applies to every author: automation must accompany an actual source change.
--staged supports a pre-commit hook; BASE..HEAD checks every commit for CI.
"""
import argparse
from pathlib import PurePosixPath
import subprocess
def generated(path):
parsed = PurePosixPath(path.lower())
name = parsed.name
return (name in {"work-records.md", ".custodian-brief.md", "source-index.md",
"publication-index.md", "index.generated.md"}
or name.startswith("source-inventory")
or name.startswith("decision-packets")
or name.startswith("publication-index") or name.startswith("projection-refresh")
or str(parsed).startswith("generated/")
or str(parsed).startswith("docs/attended-window/")
or any(part in {"evidence", "decision-packets", "evidence-packets"}
for part in parsed.parts[:-1]))
def standalone(paths):
return bool(paths) and all(generated(path) for path in paths)
def git(*args):
return subprocess.check_output(["git", *args])
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--staged", action="store_true")
parser.add_argument("--range", default="HEAD^..HEAD")
args = parser.parse_args()
commits = [None] if args.staged else git("rev-list", "--reverse", args.range).decode().splitlines()
failed = []
for commit in commits:
raw = git("diff", "--cached", "--name-only", "-z") if commit is None else git(
"diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "-z", commit)
paths = [path.decode() for path in raw.split(b"\0") if path]
if standalone(paths):
failed.append(commit or "staged")
if failed:
parser.exit(1, "Generated-only commits rejected: " + ", ".join(failed) + "\n")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,5 @@
#!/bin/sh
# Install as .git/hooks/pre-commit; rejects generated/evidence-only auto commits too.
set -eu
REPO_ROOT="$(git rev-parse --show-toplevel)"
exec python3 "$REPO_ROOT/scripts/check_generated_only_commit.py" --staged

View file

@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""Daily decision sync and security report, with per-recipient delivery checkpoints.
Dry-run by default. --apply is the scheduled mode. No credentials are stored.
"""
import argparse
import fcntl
import json
from pathlib import Path
import subprocess
import sys
from urllib.request import Request, urlopen
from security_flow_report import DEFAULT_REPOS
SCRIPTS = Path(__file__).resolve().parent
def notifications(report, state, agent, founder):
pending = []
ready = {key: report[key] for key in ("READY", "UNBLOCKED")}
if ready != state.get("agent"):
pending.append(("agent", agent, ready))
# ATTENDED is the daily operator reminder; empty lists never message the founder.
if report["ATTENDED"]:
pending.append(("founder", founder, {"ATTENDED": report["ATTENDED"]}))
return pending
def post(hub, recipient, payload):
body = {"from_agent": "the-custodian", "to_agent": recipient,
"subject": "Daily security flow", "body": json.dumps(payload, sort_keys=True)}
request = Request(hub.rstrip("/") + "/messages/", data=json.dumps(body).encode(),
headers={"Content-Type": "application/json"}, method="POST")
with urlopen(request, timeout=30) as response:
return json.load(response)["id"]
def save(path, data):
temporary = path.with_suffix(path.suffix + ".tmp")
temporary.write_text(json.dumps(data, indent=2) + "\n")
temporary.replace(path)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", default=str(Path.home()))
parser.add_argument("--repos", default=",".join(["the-custodian", *DEFAULT_REPOS]))
parser.add_argument("--hub", default="http://127.0.0.1:8000")
parser.add_argument("--agent", default="the-custodian")
parser.add_argument("--founder", default="human")
parser.add_argument("--state-dir", type=Path, default=Path.home() / ".local/state/custodian-security-flow")
parser.add_argument("--apply", action="store_true")
args = parser.parse_args()
args.state_dir.mkdir(parents=True, exist_ok=True)
with (args.state_dir / "run.lock").open("w") as lock:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
command = [sys.executable, str(SCRIPTS / "sync_resolved_decisions.py"),
"--root", args.root, "--hub", args.hub]
subprocess.run(command + (["--apply"] if args.apply else []), check=True)
raw = subprocess.check_output([sys.executable, str(SCRIPTS / "security_flow_report.py"),
"--root", args.root, "--repos", args.repos, "--json"], text=True)
report = json.loads(raw)
state_path = args.state_dir / "delivery.json"
state = json.loads(state_path.read_text()) if state_path.exists() else {}
save(args.state_dir / "report.json", report)
for key, recipient, payload in notifications(report, state, args.agent, args.founder):
if not args.apply:
print(json.dumps({"dry_run": True, "to_agent": recipient, "report": payload}))
continue
message_id = post(args.hub, recipient, payload)
state[key] = payload
state[key + "_message_id"] = message_id
save(state_path, state) # A later recipient failure does not replay earlier sends.
print(raw)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,49 @@
import importlib.util
from pathlib import Path
import subprocess
SCRIPT = Path(__file__).resolve().parents[1] / "scripts/check_generated_only_commit.py"
spec = importlib.util.spec_from_file_location("guard", SCRIPT)
guard = importlib.util.module_from_spec(spec)
spec.loader.exec_module(guard)
def test_source_change_is_required_with_generated_indexes():
assert guard.standalone(["WORK-RECORDS.md", "docs/publication-index.md"])
assert guard.standalone(["docs/source-inventory.json", "generated/index.json"])
assert not guard.standalone(["WORK-RECORDS.md", "scripts/issuer.py"])
assert not guard.standalone([])
assert not guard.standalone(["canon/standards/work-record-types_v0.1.md"])
def test_evidence_packets_and_generated_brief_need_source_change():
assert guard.standalone(["docs/evidence/run/receipt.json", ".custodian-brief.md"])
assert guard.standalone(["docs/decision-packets/daily-batch.md"])
assert guard.standalone(["docs/decision-packets-2026-10-03.md"])
assert guard.standalone(["docs/attended-window/2026-10-11-daily-red-batch.md"])
assert guard.standalone(["docs/evidence-packets/live-proof.md", "docs/source-index.md"])
assert not guard.standalone(["docs/evidence/run/receipt.json", "scripts/verify_live.py"])
assert not guard.standalone(["docs/implementation/delivery-protocol.md"])
assert not guard.standalone(["docs/decisions/authorization.md"])
def test_ci_checks_each_commit_in_range(tmp_path):
def git(*args):
return subprocess.check_output(["git", *args], cwd=tmp_path).decode().strip()
git("init", "-q")
git("config", "user.name", "Test")
git("config", "user.email", "test@example.test")
(tmp_path / "source.py").write_text("source\n")
git("add", ".")
git("commit", "-qm", "source")
base = git("rev-parse", "HEAD")
(tmp_path / "WORK-RECORDS.md").write_text("generated\n")
git("add", ".")
git("commit", "-qm", "generated only")
(tmp_path / "source.py").write_text("changed\n")
git("add", ".")
git("commit", "-qm", "source again")
result = subprocess.run(["python3", str(SCRIPT), "--range", base + "..HEAD"],
cwd=tmp_path, capture_output=True, text=True)
assert result.returncode == 1
assert "Generated-only commits rejected" in result.stderr

View file

@ -0,0 +1,80 @@
import json
from pathlib import Path
import sys
import pytest
SCRIPTS = Path(__file__).resolve().parents[1] / "scripts"
sys.path.insert(0, str(SCRIPTS))
import security_flow_daily as daily
import security_flow_report as flow
def test_notifications_compare_entire_actionable_lists():
report = {"READY": [{"id": "T1", "status": "todo"}], "UNBLOCKED": [], "ATTENDED": []}
first = daily.notifications(report, {}, "coding", "human")
assert first == [("agent", "coding", {"READY": report["READY"], "UNBLOCKED": []})]
assert daily.notifications(report, {"agent": first[0][2]}, "coding", "human") == []
report["READY"] = []
assert daily.notifications(report, {"agent": first[0][2]}, "coding", "human")
def test_attended_goes_only_to_founder_and_is_daily():
report = {"READY": [], "UNBLOCKED": [], "ATTENDED": [{"id": "T2"}]}
state = {"agent": {"READY": [], "UNBLOCKED": []}}
assert daily.notifications(report, state, "coding", "human") == [
("founder", "human", {"ATTENDED": report["ATTENDED"]})]
def test_atomic_checkpoint_retains_prior_recipient(tmp_path):
checkpoint = tmp_path / "delivery.json"
daily.save(checkpoint, {"agent_message_id": "receipt-1"})
assert checkpoint.read_text().find("receipt-1") >= 0
assert not checkpoint.with_suffix(".json.tmp").exists()
def test_retry_does_not_replay_successful_agent_delivery(tmp_path, monkeypatch):
report = {"READY": [{"id": "T1"}], "UNBLOCKED": [], "ATTENDED": [{"id": "T2"}]}
monkeypatch.setattr(sys, "argv", ["security_flow_daily", "--apply", "--state-dir", str(tmp_path)])
commands = []
monkeypatch.setattr(daily.subprocess, "run", lambda command, **kw: commands.append(command))
monkeypatch.setattr(daily.subprocess, "check_output", lambda *a, **kw: json.dumps(report))
sent = []
def fail_founder(hub, recipient, payload):
sent.append(recipient)
if recipient == "human":
raise OSError("hub interrupted")
return "agent-receipt"
monkeypatch.setattr(daily, "post", fail_founder)
with pytest.raises(OSError):
daily.main()
assert "--apply" in commands[0]
assert json.loads((tmp_path / "delivery.json").read_text())["agent_message_id"] == "agent-receipt"
monkeypatch.setattr(daily, "post", lambda hub, recipient, payload: sent.append(recipient) or "receipt")
daily.main()
assert sent == ["the-custodian", "human", "human"]
def test_sync_failure_prevents_notifications(tmp_path, monkeypatch):
monkeypatch.setattr(sys, "argv", ["security_flow_daily", "--apply", "--state-dir", str(tmp_path)])
def fail_sync(*args, **kwargs):
raise daily.subprocess.CalledProcessError(1, args[0])
monkeypatch.setattr(daily.subprocess, "run", fail_sync)
with pytest.raises(daily.subprocess.CalledProcessError):
daily.main()
assert not (tmp_path / "delivery.json").exists()
def test_report_classifies_terminal_unknown_and_human_dependencies():
def task(tid, status, deps=(), human=False, flavor="implementation"):
return {"id": tid, "status": status, "deps": deps, "human": human,
"blocked_on": "", "priority": "high",
"wp": {"status": "active", "flavor": flavor}}
tasks = {"X-T01": task("X-T01", "done"),
"X-T02": task("X-T02", "wait", ["X-T01"]),
"X-T03": task("X-T03", "wait", ["missing"]),
"X-T04": task("X-T04", "wait", ["X-T01"], human=True),
"Y-T01": task("Y-T01", "todo", flavor="residual")}
report = flow.classify(tasks)
assert [t["id"] for t in report["UNBLOCKED"]] == ["X-T02"]
assert [t["id"] for t in report["ATTENDED"]] == ["X-T04"]
assert not report["READY"]

View file

@ -59,9 +59,11 @@ resolved or accepted).
```task
id: CUST-WP-0079-T03
status: todo
status: wait
priority: high
state_hub_task_id: "1be0569c-221d-5925-a0ff-fd2af88e52b1"
blocked_on: message-from:activity-core
blocking_reason: "Awaiting daily schedule owner acceptance; workstation user service bus unavailable. Handoff 117e538e-da06-48b8-ba68-b82300a832e7."
```
Run `sync_resolved_decisions.py --apply` and `security_flow_report.py --json` daily (activity-core
@ -69,6 +71,17 @@ schedule, or a workstation timer until activity-core has the lane). Post a hub m
coding agent only when UNBLOCKED or READY changed, and to the founder only when ATTENDED is
non-empty. Agents must not poll wait tasks themselves.
Implementation (2026-10-11): `scripts/security_flow_daily.py` runs both commands, includes
`the-custodian` in the default security estate, checkpoints each delivered recipient, and
notifies the coding agent only when READY/UNBLOCKED changes. Empty ATTENDED lists never
message the founder. User timer units are in `infra/security-flow/`; `make security-flow-check`
passes nine tests including interrupted delivery and retry. Live hub dry-run succeeds.
Schedule activation handoff `117e538e-da06-48b8-ba68-b82300a832e7` to `activity-core`
is attached to `case:cross-owner:CUST-WP-0079-T03:activity-core`
(`infra/security-flow/scheduling-handoff.json`); no owner acceptance or foreign dependency is
invented. Schedule activation remains pending: workstation `systemctl --user` cannot connect to its
user bus (`No data available`), so checked-in units alone do not establish daily execution.
## Encode the standing change rule as a policy-nexus guardrail
```task
@ -87,7 +100,7 @@ Canon source written 2026-10-11: `canon/standards/build-mode-change-authorizatio
```task
id: CUST-WP-0079-T05
status: todo
status: progress
priority: medium
state_hub_task_id: "8a3dc219-8982-5903-9407-e1b549d4544c"
```
@ -97,6 +110,14 @@ regenerated by a `make` target in a pre-commit hook or a Forgejo Action, not by
Start with the security repos. Done when a week of git log shows no standalone index-only commits
from agents.
Implementation (2026-10-11): `scripts/check_generated_only_commit.py` checks staged files
or every commit in a revision range. Forgejo workflow `generated-commit-guard.yaml` rejects
standalone WORK-RECORDS, source-inventory, publication-index, projection-refresh,
generated briefs and explicit evidence/decision-packet commits regardless of author; a later source commit cannot hide an earlier
generated-only commit. `make generated-commit-check` provides the local hook entry point.
The CI suite covers the rejection using a real temporary git repository. Consumer generator
adoption and the required week of clean logs remain open; this task is not claimed complete.
## Attended window #1
```task
@ -112,10 +133,16 @@ state_hub_task_id: "e3da807f-9a6e-513f-82dc-3714a489e7bf"
```task
id: CUST-WP-0079-T07
status: todo
status: done
priority: high
state_hub_task_id: "91ec924c-b305-5a63-925d-087a3b927d60"
```
Replace the Sol goal with the renewed text in the assessment history file (section "Renewed
goal"). Re-engage now for the READY lane. It does not depend on T06.
Completed 2026-10-11: the renewed critical-path goal is active in thread
`01a126e1-76f8-7643-8cf3-e93336539ade`; its attached text was read before
execution. Work resumed from READY/UNBLOCKED, with three workers pinned to
eligible workplans and the accepted build-mode authorization in force. This
closes re-engagement only, not the live critical-path goal.