From 5c5c2986439a17f6c78e272ece88a84b1747a165 Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 22 Sep 2026 12:00:59 +0200 Subject: [PATCH] FI-WP-0005 T01-T03: owner declaration, publication decision, egress hosts. - Owner declaration for the profiled fi-daily-research-brief (proposed): inputs, single-commit briefs/** grant, fast-forward-only publication, quality rules, model requirements without env selection, completion evidence and rollback pins. - Decision: origin publication is a typed grant on the run, not an executor default. - docs/sources-egress.yaml: 17 exact host:443 entries for the sandbox, tested for sand-boxer format and drift against the prose allowlist. Co-Authored-By: Claude Opus 5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 51320@bnt-lap001 Assistant-Session: 9d40b4c7-8e3c-42ee-b755-d658d4640d6c --- .../fi-daily-research-brief.declaration.yaml | 113 ++++++++++++++++++ .../fi-daily-research-brief.md | 4 + .../2026-09-22-brief-origin-publication.md | 60 ++++++++++ docs/sources-allowlist.md | 5 + docs/sources-egress.yaml | 99 +++++++++++++++ scripts/test_declaration.py | 42 +++++++ scripts/test_sources_egress.py | 51 ++++++++ .../FI-WP-0005-profiled-brief-execution.md | 26 +++- 8 files changed, 396 insertions(+), 4 deletions(-) create mode 100644 activity-definitions/fi-daily-research-brief.declaration.yaml create mode 100644 docs/decisions/2026-09-22-brief-origin-publication.md create mode 100644 docs/sources-egress.yaml create mode 100644 scripts/test_declaration.py create mode 100644 scripts/test_sources_egress.py diff --git a/activity-definitions/fi-daily-research-brief.declaration.yaml b/activity-definitions/fi-daily-research-brief.declaration.yaml new file mode 100644 index 0000000..cf927dc --- /dev/null +++ b/activity-definitions/fi-daily-research-brief.declaration.yaml @@ -0,0 +1,113 @@ +# Owner declaration — fi-daily-research-brief (FI-WP-0005-T01) +# +# Answers rein-aharness d0b45acb / REINAH-WP-0003-T04: the owner-approved +# replacement for the profile-absent `fi-research-brief` route. This file is +# FI's authority over WHAT runs and under WHICH grant. It does not schedule +# (activity-core), pick a rein/sandbox/model (glas-harness) or execute +# (rein-aharness). Where another layer has not shipped the needed piece yet, +# the field names the FI-WP-0005 task that tracks it. +declaration_version: "1" +definition_id: fi-daily-research-brief +owner: freedom-intelligence +approved_by: Bernd Worsch +approved_at: "2026-09-22" +status: proposed # -> active at FI-WP-0005-T08 cutover + +execution: + shape: agent # operator decision 2026-09-22 (option B) + harness_profile_ref: harness.fi-research-brief@1.0.0 # requested, FI-WP-0005-T05; not yet in the Glas catalog + selection: harness_profile_ref only. No label or approach_hint routing, no + fallback to the compatibility executor once cut over. + +inputs: + # Read from the checked-out workspace at the run's base commit. + brief_date: + type: date + tz: Europe/Berlin + source: activity-core due item (context.item.date) + repository_files: + - docs/sources-allowlist.md + - docs/daily-brief-playbook.md + - briefs/_template.md + - inventory/RESERVE-STATUS.md + - research/2026-07-24-baseline-field-survey.md + - research/2026-07-24-nas-strategic-collection-plan.md + repository_globs: + - inventory/catalog/*.yaml # authority for sizes, licenses, collection status + prior_briefs: + names: last 5 by date, excluding brief_date + full_text: most recent 1 + git_log: last 12 commits, oneline + network: + egress_hosts: docs/sources-egress.yaml # FI-WP-0005-T03; model route is profile-owned + secrets: none. The run reads no FI credential. Model credentials stay with + the profile's owner route (Glas / sand-boxer). + +outputs: + artifact: briefs/{yyyy}/{mm}/{date}.md + format: briefs/_template.md, axes A–D, collection-candidate section + exactly_one_new_file: true + must_not_modify_existing_files: true + +repository_grant: + # Grant v1 fields (activity-core RepositoryGrant). v1 rejects publish=true, + # so publication rides on grant v2 (FI-WP-0005-T04). + version: "2" # requested; v1 shape below validated against activity-core b63131e+ + allowed_paths: + - briefs/** + commit_count: {min: 1, max: 1} + publication: + remote: origin + ref: main + mode: fast-forward-only + decision: docs/decisions/2026-09-22-brief-origin-publication.md + commit_message: "FI daily research brief {date}" + +quality_rules: + # Acceptance for canary comparison (FI-WP-0005-T07) and prompt contract. + - inventory/catalog is the authority. Do not re-announce cataloged models + as new, and never state sizes or licenses that contradict the catalog. + - Every parameter count, size, price or benchmark claim cites a primary + source that was fetched in this run. If it is not verifiable, say so + instead of estimating. + - Deltas only, measured against prior briefs and the baseline, not a resurvey. + - Cover axes A–D, or state explicitly that an axis had no delta. + - Rumor (social media, unsourced aggregators) is not reported as confirmed. + - No credentials, customer data or model weights in the brief. + +model: + # FI states requirements; the Glas profile pins provider/model/route. + model_class: frontier + required_capabilities: [tool_use, web_fetch_via_https_proxy, long_context] + limits_hint: {timeout_seconds: 1800, max_turns: 60} + constraints: + - No Binky-named variables (BRIEF_DAILY_MODEL, MAIL_TRIAGE_MODEL) and no + process-environment model selection. The profile version is the only selector. + - The playbook rule "no Claude Code / Codex on railiance01" means no + host-installed agent. A pinned agent executable inside the Glas sandbox is + acceptable only if the profile owner confirms it is not a host + installation. Operator to confirm (FI-WP-0005-T05). + +completion: + event: fi_daily_brief + required_detail: + date: brief_date + path: outputs.artifact + pushed: true + origin_sha: 40-hex commit on origin/main that contains the artifact + harness_profile_ref: execution.harness_profile_ref + due_resolution: published-artifact resolver (FI-WP-0005-T04). The day + clears only if the artifact is on origin/main AND the event says + pushed=true. In the interim, fi_brief_status (activity-core 289aff3), same semantics. + on_failure: executor_run ok=false, no fi_daily_brief, day stays due. + independent_audit: scripts/verify_brief_durability.py --require-date {date} + +rollback: + pins: + - glas profile version (previous harness.fi-research-brief@x.y.z) + - this declaration and the definition, by git revision in this repo + procedure: pin the previous profile version or revert the definition + revision, then sync activity-core definitions. Until the legacy expiry + (2026-12-31) the compatibility executor rein-aharness@11020e8 remains a + manual one-shot recovery. There is no host timer (FI-WP-0005-T06). + evidence_required: one exercised rollback at cutover (FI-WP-0005-T08) diff --git a/activity-definitions/fi-daily-research-brief.md b/activity-definitions/fi-daily-research-brief.md index 5e4a752..5b3450f 100644 --- a/activity-definitions/fi-daily-research-brief.md +++ b/activity-definitions/fi-daily-research-brief.md @@ -65,5 +65,9 @@ action: 5. [x] Executor: `rein-aharness fi-research-brief` + `scripts/railiance-rhythm/` (install on railiance01; not workstation cron) +Migration (FI-WP-0005): the owner declaration for the profiled replacement is +`fi-daily-research-brief.declaration.yaml` (status `proposed`). This definition +stays on the compatibility route until the T08 cutover. + Layers: activity-core (when/what/where) → rein-aharness (execute) → this repo. See `docs/recurrence-ops.md` and `activity-core/docs/recurring-automations-playbook.md`. diff --git a/docs/decisions/2026-09-22-brief-origin-publication.md b/docs/decisions/2026-09-22-brief-origin-publication.md new file mode 100644 index 0000000..df74699 --- /dev/null +++ b/docs/decisions/2026-09-22-brief-origin-publication.md @@ -0,0 +1,60 @@ +# Decision: Origin publication of daily briefs is an explicit, typed grant + +**Date:** 2026-09-22 +**Status:** accepted +**Decided by:** Bernd Worsch (operator), FI owner +**Affects:** `activity-definitions/fi-daily-research-brief.md`, +`activity-definitions/fi-daily-research-brief.declaration.yaml`, FI-WP-0005 +**Supersedes:** the grant wording in FI-WP-0004-T04, which granted "origin +publication as a named capability of `fi-research-brief`". That capability +lived inside one rein command. This decision moves it to the queued run's +repository grant. + +--- + +## Context + +A brief day counts only when the brief is on `origin/main` (FI-WP-0004). The +executor therefore has to push. rein-aharness (`d0b45acb`, REINAH-WP-0003-T04) +requires that publication be a separate, owner-approved decision, not a +default of the executor. activity-core repository-grant v1 is local-only and +rejects `publish: true`. + +## Decision + +1. **FI grants publication, scoped exactly:** + - remote `origin`, ref `main` + - mode **fast-forward only**: no force push, no other refs, no tags + - only commits whose changes stay within `briefs/**` + - at most one commit per run +2. **The grant rides on the run, not the executor.** It is carried as + activity-core repository-grant v2 `publication` (requested in FI-WP-0005-T04). + No rein, profile or tool may publish FI content without that grant on the + claimed `ops_run`. +3. **Failure semantics:** + - Push rejected or failed: the run fails, no `fi_daily_brief` is posted, + and the day stays due. + - Non-fast-forward (origin moved): the executor may rebase its single + brief commit onto the new origin head once, then retry. A second failure + is a failed run. It never merges and never forces. + - Success requires `origin_sha` evidence, and activity-core close + reconciliation checks it against the grant. +4. **Until grant v2 is live**, the compatibility path (`rein-aharness + fi-research-brief`, commit `11020e8`) continues under FI-WP-0004-T04 semantics. + It ends at cutover (FI-WP-0005-T08) or at the legacy expiry, whichever + comes first. + +## Not granted + +- Writes outside `briefs/**`. The catalog, inventory, workplans and docs stay + human- or workplan-driven. +- Publication to any other remote or mirror. +- Opening PRs or issues, or posting to other external systems. + +## Consequences + +- activity-core needs grant v2 and origin-SHA close evidence (FI-WP-0005-T04). +- The Glas tool profile must be able to push, but only under a grant + (FI-WP-0005-T05). +- `scripts/verify_brief_durability.py` remains the independent audit of the + result. diff --git a/docs/sources-allowlist.md b/docs/sources-allowlist.md index d365639..eb933b5 100644 --- a/docs/sources-allowlist.md +++ b/docs/sources-allowlist.md @@ -3,6 +3,11 @@ Standing channels for Freedom Intelligence briefs (axes A–D). Prefer **primary** sources over aggregators. This is a watchlist, not a scrape mandate. +Automated runs reach the network only through the hosts in +[`sources-egress.yaml`](sources-egress.yaml) (FI-WP-0005-T03). Adding a +channel here that needs a new host means adding that host there too; +`scripts/test_sources_egress.py` checks the two stay in step. + --- ## Axis A — Frontier & commercial diff --git a/docs/sources-egress.yaml b/docs/sources-egress.yaml new file mode 100644 index 0000000..11be0a2 --- /dev/null +++ b/docs/sources-egress.yaml @@ -0,0 +1,99 @@ +# Network egress for the daily research brief sandbox (FI-WP-0005-T03). +# +# Machine-readable companion to docs/sources-allowlist.md. The Glas profile +# for fi-daily-research-brief declares exactly these hosts in +# network.egress (default: deny). The model provider route is NOT listed +# here; it belongs to the profile (glas-harness), not to FI. +# +# sand-boxer rules (docs/bwrap-egress.md): exact lowercase DNS names, port 443 +# only, no wildcards, no IP literals. Enforcement is by destination, not by +# path: every host below is a whole-host trust decision. Keep the list minimal. +# +# `channel` must appear verbatim in docs/sources-allowlist.md +# (scripts/test_sources_egress.py enforces this so the two cannot drift). +version: 1 +hosts: + # Axis A — frontier & commercial + - host: openai.com:443 + axes: [A] + channel: OpenAI / Anthropic / Google / xAI / DeepSeek blogs & release notes + why: Release notes, model pages, API pricing. Returned 403 to a plain + client on 2026-09-22 (bot protection); may be unreadable unattended. + - host: www.anthropic.com:443 + axes: [A] + channel: OpenAI / Anthropic / Google / xAI / DeepSeek blogs & release notes + why: News and model announcements (apex redirects here). + - host: platform.claude.com:443 + axes: [A] + channel: Model cards for API models + why: Model overview, context limits, pricing tables (docs.anthropic.com + now 301-redirects here, 2026-09-22). + - host: blog.google:443 + axes: [A] + channel: OpenAI / Anthropic / Google / xAI / DeepSeek blogs & release notes + why: Gemini release announcements. + - host: ai.google.dev:443 + axes: [A] + channel: Official pricing pages + why: Gemini API models and pricing. + - host: x.ai:443 + axes: [A] + channel: OpenAI / Anthropic / Google / xAI / DeepSeek blogs & release notes + why: Grok release notes. Returned 403 to a plain client on 2026-09-22. + - host: api-docs.deepseek.com:443 + axes: [A, B] + channel: OpenAI / Anthropic / Google / xAI / DeepSeek blogs & release notes + why: DeepSeek news, API pricing and model list. + - host: arena.ai:443 + axes: [A] + channel: LMSYS / Arena / artificialanalysis-class charts + why: Directional leaderboard only; note gaming risk (lmarena.ai + 301-redirects here, 2026-09-22). + - host: artificialanalysis.ai:443 + axes: [A] + channel: LMSYS / Arena / artificialanalysis-class charts + why: Price/performance charts, directional. + - host: www.swebench.com:443 + axes: [A, D] + channel: SWE-bench Verified / Live leaderboards + why: Harness+model pairs. + # Axis B — edge / local / open + - host: huggingface.co:443 + axes: [B, C] + channel: Hugging Face org feeds + why: Model cards, licenses, sizes via /api/models. No weight download + (weight CDN hosts are deliberately absent). + - host: ollama.com:443 + axes: [B] + channel: llama.cpp, vLLM, MLX, Ollama release notes + why: Ollama library and release notes. + # Axes B/C/D — GitHub-hosted releases (llama.cpp, vLLM, MLX, Unsloth, TRL, + # OpenHands, Aider, SWE-agent, OpenCode, Cline, ...) + - host: github.com:443 + axes: [B, C, D] + channel: OpenHands, Aider, SWE-agent, OpenCode, Cline repos/releases + why: Release pages for runtimes, training tools and harnesses. + - host: api.github.com:443 + axes: [B, C, D] + channel: llama.cpp, vLLM, MLX, Ollama release notes + why: Structured release listing instead of scraping HTML. + # Axis C — training & specialization + - host: arxiv.org:443 + axes: [C] + channel: arXiv cs.LG, cs.CL, cs.AI (recent) + why: Abstracts and listings. + - host: export.arxiv.org:443 + axes: [C] + channel: arXiv cs.LG, cs.CL, cs.AI (recent) + why: arXiv query API (the sanctioned programmatic endpoint). + # Axis D — harness & fleet + - host: modelcontextprotocol.io:443 + axes: [D] + channel: MCP / tool-protocol standards + why: MCP specification and changelog. +# Deliberately absent: +# - Model provider API hosts (profile-owned route). +# - Hugging Face / GitHub content CDNs (no weight or asset download in briefs). +# - Social media / X (allowlist: rumor is not confirmation). +# - Internal Coulomb repos (sand-boxer, activity-core): read from the +# checked-out workspace or hub, not over the network. diff --git a/scripts/test_declaration.py b/scripts/test_declaration.py new file mode 100644 index 0000000..3ee6d6c --- /dev/null +++ b/scripts/test_declaration.py @@ -0,0 +1,42 @@ +"""The fi-daily-research-brief owner declaration parses and its references resolve.""" +from pathlib import Path +import unittest + +import yaml + +ROOT = Path(__file__).resolve().parent.parent +DECLARATION = ROOT / 'activity-definitions' / 'fi-daily-research-brief.declaration.yaml' + + +class DeclarationTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.doc = yaml.safe_load(DECLARATION.read_text(encoding='utf-8')) + + def test_referenced_files_exist(self): + inputs = self.doc['inputs'] + paths = list(inputs['repository_files']) + paths.append(inputs['network']['egress_hosts']) + paths.append(self.doc['repository_grant']['publication']['decision']) + for rel in paths: + self.assertTrue((ROOT / rel).is_file(), rel) + + def test_grant_is_single_commit_within_briefs(self): + grant = self.doc['repository_grant'] + self.assertEqual(grant['allowed_paths'], ['briefs/**']) + self.assertEqual(grant['commit_count'], {'min': 1, 'max': 1}) + self.assertEqual(grant['publication'], + {'remote': 'origin', 'ref': 'main', 'mode': 'fast-forward-only', + 'decision': grant['publication']['decision']}) + + def test_completion_requires_published_evidence(self): + detail = self.doc['completion']['required_detail'] + self.assertIs(detail['pushed'], True) + self.assertIn('origin_sha', detail) + + def test_no_environment_model_selection(self): + self.assertNotIn('env', self.doc['model']) + + +if __name__ == '__main__': + unittest.main() diff --git a/scripts/test_sources_egress.py b/scripts/test_sources_egress.py new file mode 100644 index 0000000..24cc040 --- /dev/null +++ b/scripts/test_sources_egress.py @@ -0,0 +1,51 @@ +"""docs/sources-egress.yaml stays valid for sand-boxer and in step with the prose allowlist.""" +from pathlib import Path +import re +import unittest + +import yaml + +ROOT = Path(__file__).resolve().parent.parent +EGRESS = ROOT / 'docs' / 'sources-egress.yaml' +ALLOWLIST = ROOT / 'docs' / 'sources-allowlist.md' +# sand-boxer bwrap-egress: exact lowercase DNS name, port 443, no wildcard or IP literal. +HOST = re.compile(r'^(?=.{1,253}:443$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}:443$') +MAX_HOSTS = 24 # every host is a whole-host trust decision; raise only deliberately + + +class SourcesEgressTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.doc = yaml.safe_load(EGRESS.read_text(encoding='utf-8')) + cls.hosts = cls.doc['hosts'] + cls.prose = ALLOWLIST.read_text(encoding='utf-8') + + def test_version(self): + self.assertEqual(self.doc['version'], 1) + + def test_host_format(self): + for entry in self.hosts: + self.assertRegex(entry['host'], HOST) + + def test_no_duplicates_and_bounded(self): + names = [e['host'] for e in self.hosts] + self.assertEqual(len(names), len(set(names))) + self.assertLessEqual(len(names), MAX_HOSTS) + + def test_entries_complete(self): + for entry in self.hosts: + self.assertEqual(set(entry), {'host', 'axes', 'channel', 'why'}, entry['host']) + self.assertTrue(entry['why'].strip(), entry['host']) + self.assertTrue(set(entry['axes']) <= set('ABCD'), entry['host']) + + def test_every_axis_covered(self): + covered = {axis for e in self.hosts for axis in e['axes']} + self.assertEqual(covered, set('ABCD')) + + def test_channel_named_in_prose_allowlist(self): + for entry in self.hosts: + self.assertIn(entry['channel'], self.prose, entry['host']) + + +if __name__ == '__main__': + unittest.main() diff --git a/workplans/FI-WP-0005-profiled-brief-execution.md b/workplans/FI-WP-0005-profiled-brief-execution.md index ab9dd49..281298e 100644 --- a/workplans/FI-WP-0005-profiled-brief-execution.md +++ b/workplans/FI-WP-0005-profiled-brief-execution.md @@ -70,7 +70,7 @@ network egress beyond a model provider. ```task id: FI-WP-0005-T01 -status: todo +status: done priority: high state_hub_task_id: "bf8c1870-bdca-51d6-beaf-c5f000037205" ``` @@ -86,27 +86,39 @@ publication policy (reference T02), and rollback. No Binky-named env vars. **Done when:** the declaration is committed, and every field rein listed in `d0b45acb` maps to a concrete value or to a named follow-up task here. +**2026-09-22:** Done. `activity-definitions/fi-daily-research-brief.declaration.yaml` +(status `proposed`). It covers profile ref (pending T05), inputs, outputs, grant +(its v1 fields validate against activity-core `RepositoryGrant`; publication pending +T04), quality rules, model requirements without env selection, completion +evidence and rollback pins. `scripts/test_declaration.py` checks it. Open +operator question: whether a pinned agent executable inside the Glas sandbox +satisfies the playbook's "no Claude Code / Codex on railiance01" rule. + ### T02 — Publication decision record ```task id: FI-WP-0005-T02 -status: todo +status: done priority: high state_hub_task_id: "25475ede-ebd9-5c31-8c22-718e30acf8ac" ``` -`docs/decisions/2026-09-XX-brief-origin-publication.md`: FI grants +`docs/decisions/2026-09-22-brief-origin-publication.md`: FI grants fast-forward-only publication of `briefs/**` to `origin/main`; no force, no other refs; publication failure fails the run and leaves the day due. Supersedes the implicit grant wording in FI-WP-0004-T04. **Done when:** the decision is committed and cited by T01 and T04. +**2026-09-22:** Done. Adds a single rebase-and-retry on non-fast-forward; +it never merges or forces. Cited by the declaration and the activity-core +proposal `8ee9a6ab`. + ### T03 — Machine-readable source host list ```task id: FI-WP-0005-T03 -status: todo +status: done priority: high state_hub_task_id: "9b92d7f4-f4c8-5ce5-9367-462653515e90" ``` @@ -120,6 +132,12 @@ path, so every host is a trust decision. **Done when:** the list is committed with a test, and the host count and the rationale for each host are recorded. +**2026-09-22:** Done. `docs/sources-egress.yaml` has 17 hosts covering axes A–D. +`scripts/test_sources_egress.py` checks sand-boxer format, the 24-host cap and +that each channel is named in the prose allowlist. A live check replaced +redirecting hosts (docs.anthropic.com → platform.claude.com, lmarena.ai → +arena.ai). openai.com and x.ai return 403 to plain clients, which is noted. + ### T04 — activity-core handoff: grant v2 publication + generic due resolver ```task