Add bounded frontend-patterns feedback reporting resolver
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 50s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e241-8285-7a63-8c0c-51c9cb824dc3
This commit is contained in:
tegwick 2026-09-27 13:05:05 +02:00
parent fbf791811d
commit f7e26111f7
6 changed files with 584 additions and 1 deletions

View file

@ -0,0 +1,6 @@
# Bounded overlay of the deployed worker; no unrelated API/worker upgrade.
# Required local base image ID:
# sha256:cd4e924c2809f0d1d319e53a9ddd20db2e88a1b543d62e6442702c184b7842f4
FROM activity-core:fi-publication-20260914
COPY src/activity_core/context_resolvers/frontend_patterns.py /app/src/activity_core/context_resolvers/frontend_patterns.py
COPY src/activity_core/context_resolvers/__init__.py /app/src/activity_core/context_resolvers/__init__.py

View file

@ -51,6 +51,139 @@ metadata:
app.kubernetes.io/name: activity-core app.kubernetes.io/name: activity-core
app.kubernetes.io/part-of: activity-core app.kubernetes.io/part-of: activity-core
data: data:
frontend-patterns-daily.md: |
---
id: frontend-patterns-daily
name: Frontend Patterns Daily Review
enabled: false
owner: frontend-patterns
governance: custodian
status: proposed
trigger:
type: cron
cron_expression: "0 2 * * *"
timezone: Europe/Berlin
misfire_policy: catchup_latest
catchup_window_seconds: 86400
context_sources:
- type: frontend-patterns
query: daily
params: {required: true}
bind_to: context.daily_triage_digest
---
# Frontend patterns daily review
Domain-owned by FEP-WP-0008. Read-only bounded receipt collection and deterministic
reporting; no LLM calls or repository mutations. Source collection is pinned to Git
revisions. Outcome assertions remain unverified. The weekly report explicitly carries
the blocked executor dependency; it does not emit unexecutable ops_run work or claim
an improvement has happened. Enable after live report-sink proof.
```instruction
id: frontend-patterns-daily-report
trusted_fields: []
model: deterministic
temperature: 0
max_tokens: 1
prompt: Deterministic frontend-patterns receipt and execution-readiness digest.
output_schema: ""
review_advisory: false
report_sinks:
- type: state-hub-progress
event_type: fep_feedback_intake
author: activity-core
topic_id: fe2aaa78-9c20-4feb-b3d2-4fe0529572a3
```
frontend-patterns-monthly.md: |
---
id: frontend-patterns-monthly
name: Frontend Patterns Monthly Review
enabled: false
owner: frontend-patterns
governance: custodian
status: proposed
trigger:
type: cron
cron_expression: "0 9 1 * *"
timezone: Europe/Berlin
misfire_policy: catchup_latest
catchup_window_seconds: 86400
context_sources:
- type: frontend-patterns
query: monthly
params: {required: true}
bind_to: context.daily_triage_digest
---
# Frontend patterns monthly review
Domain-owned by FEP-WP-0008. Read-only bounded receipt collection and deterministic
reporting; no LLM calls or repository mutations. Source collection is pinned to Git
revisions. Outcome assertions remain unverified. The weekly report explicitly carries
the blocked executor dependency; it does not emit unexecutable ops_run work or claim
an improvement has happened. Enable after live report-sink proof.
```instruction
id: frontend-patterns-monthly-report
trusted_fields: []
model: deterministic
temperature: 0
max_tokens: 1
prompt: Deterministic frontend-patterns receipt and execution-readiness digest.
output_schema: ""
review_advisory: false
report_sinks:
- type: state-hub-progress
event_type: fep_monthly_review
author: activity-core
topic_id: fe2aaa78-9c20-4feb-b3d2-4fe0529572a3
```
frontend-patterns-weekly.md: |
---
id: frontend-patterns-weekly
name: Frontend Patterns Weekly Review
enabled: false
owner: frontend-patterns
governance: custodian
status: proposed
trigger:
type: cron
cron_expression: "0 3 * * 2"
timezone: Europe/Berlin
misfire_policy: catchup_latest
catchup_window_seconds: 86400
context_sources:
- type: frontend-patterns
query: weekly
params: {required: true}
bind_to: context.daily_triage_digest
---
# Frontend patterns weekly review
Domain-owned by FEP-WP-0008. Read-only bounded receipt collection and deterministic
reporting; no LLM calls or repository mutations. Source collection is pinned to Git
revisions. Outcome assertions remain unverified. The weekly report explicitly carries
the blocked executor dependency; it does not emit unexecutable ops_run work or claim
an improvement has happened. Enable after live report-sink proof.
```instruction
id: frontend-patterns-weekly-report
trusted_fields: []
model: deterministic
temperature: 0
max_tokens: 1
prompt: Deterministic frontend-patterns receipt and execution-readiness digest.
output_schema: ""
review_advisory: false
report_sinks:
- type: state-hub-progress
event_type: fep_improvement_review
author: activity-core
topic_id: fe2aaa78-9c20-4feb-b3d2-4fe0529572a3
```
weekly-forgejo-package-prune.md: | weekly-forgejo-package-prune.md: |
--- ---
id: weekly-forgejo-package-prune id: weekly-forgejo-package-prune
@ -1408,7 +1541,7 @@ spec:
fsGroup: 1000 fsGroup: 1000
containers: containers:
- name: worker - name: worker
image: activity-core:fi-publication-20260914 image: activity-core:fep-feedback-20260927
imagePullPolicy: Never imagePullPolicy: Never
command: ["python", "-m", "activity_core.worker"] command: ["python", "-m", "activity_core.worker"]
ports: ports:

View file

@ -1,5 +1,6 @@
from activity_core.context_resolvers import ( # noqa: F401 from activity_core.context_resolvers import ( # noqa: F401
core_hub, core_hub,
frontend_patterns,
event_payload, event_payload,
kaizen, kaizen,
ops_inventory, ops_inventory,

View file

@ -0,0 +1,254 @@
"""Read-only, bounded collection for the frontend-patterns reference loop.
Returns a compact JSON digest for the existing deterministic report instruction.
Never executes feedback, invokes a model, or authorizes repository mutation.
"""
from __future__ import annotations
from collections import Counter, defaultdict
from contextvars import ContextVar
from datetime import datetime, timezone
import hashlib
import json
from pathlib import PurePosixPath
import re
import time
from typing import Any
from zoneinfo import ZoneInfo
import httpx
import yaml
from activity_core.context_resolvers.base import CONTEXT_RESOLVER_REGISTRY, ContextResolver
HOST = "https://forgejo.coulomb.social"
TOPIC = "fe2aaa78-9c20-4feb-b3d2-4fe0529572a3"
SHA = re.compile(r"[0-9a-f]{40}")
DIGEST = re.compile(r"[0-9a-f]{64}")
SLUG = re.compile(r"[a-z0-9][a-z0-9-]{0,79}")
ID = re.compile(r"[A-Za-z0-9][A-Za-z0-9:_.-]{0,159}")
MAX_BYTES = 131072
MAX_RECEIPTS = 100
DEADLINE: ContextVar[float | None] = ContextVar("frontend_patterns_deadline", default=None)
def require(ok: Any, message: str) -> None:
if not ok:
raise ValueError(message)
def path_ok(value: Any) -> bool:
return (isinstance(value, str) and bool(value) and len(value) <= 240
and not PurePosixPath(value).is_absolute()
and all(part not in {"", ".", ".."} for part in value.split("/"))
and re.fullmatch(r"[A-Za-z0-9_./-]+", value) is not None)
def fetch(url: str) -> bytes:
# Callers construct only fixed-host URLs from validated slug/SHA/path parts.
deadline = DEADLINE.get()
remaining = deadline - time.monotonic() if deadline is not None else 15
require(remaining > 0, "collection_time_limit")
with httpx.stream("GET", url, timeout=min(5, remaining), follow_redirects=False) as response:
response.raise_for_status()
body = bytearray()
for part in response.iter_bytes():
require(deadline is None or time.monotonic() < deadline, "collection_time_limit")
body.extend(part)
require(len(body) <= MAX_BYTES, "response_size_limit")
return bytes(body)
def revision(repo: str, branch: str) -> str:
require(SLUG.fullmatch(repo) and SLUG.fullmatch(branch), "invalid_repository_or_branch")
data = json.loads(fetch(f"{HOST}/api/v1/repos/coulomb/{repo}/branches/{branch}"))
sha = data["commit"]["id"]
require(isinstance(sha, str) and SHA.fullmatch(sha), "invalid_revision")
return sha
def raw(repo: str, sha: str, path: str) -> bytes:
require(SLUG.fullmatch(repo) and SHA.fullmatch(sha) and path_ok(path), "invalid_source_path")
return fetch(f"{HOST}/coulomb/{repo}/raw/commit/{sha}/{path}")
def validate_event(event: Any, repo: str) -> None:
require(isinstance(event, dict) and event.get("schema_version") == 1, "invalid_event_schema")
for field in ("use_id", "event_id"):
require(isinstance(event.get(field), str) and ID.fullmatch(event[field]), "invalid_identity")
require(event.get("kind") in {"real", "synthetic"}, "invalid_kind")
previous = event.get("previous_event_sha256")
require(previous is None or isinstance(previous, str) and DIGEST.fullmatch(previous), "invalid_predecessor")
stamp = datetime.fromisoformat(event["recorded_at"].replace("Z", "+00:00"))
require(stamp.tzinfo is not None, "timestamp_requires_timezone")
require(stamp <= datetime.now(timezone.utc), "future_event")
consumer = event["consumer"]
require(consumer["repo"] == repo and SHA.fullmatch(consumer["revision"]), "consumer_mismatch")
require(all(isinstance(consumer[k], str) and consumer[k] for k in ("task_ref", "producer", "run_ref")), "missing_provenance")
reference = event["reference"]
require(SHA.fullmatch(reference["packaging_revision"]) and DIGEST.fullmatch(reference["manifest_sha256"]), "invalid_reference_pin")
require(isinstance(reference["version"], str) and reference["version"], "missing_reference_version")
context = event["context"]
require(isinstance(context["problem"], str) and 0 < len(context["problem"]) <= 1000, "invalid_problem")
require(isinstance(context["constraints"], list) and all(isinstance(x, str) and len(x) <= 500 for x in context["constraints"]), "invalid_constraints")
for field in ("considered", "selected"):
values = context[field]
require(isinstance(values, list) and len(values) <= 17 and len(set(values)) == len(values), "invalid_candidates")
require(all(isinstance(x, str) and re.fullmatch(r"CAND-\d{4}", x) and 1 <= int(x[5:]) <= 17 for x in values), "unknown_candidate")
require(set(context["selected"]) <= set(context["considered"]), "unconsidered_selection")
action = event["decision"]["action"]
require(action in {"applied", "adapted", "rejected", "no-match"}, "invalid_action")
require(bool(context["selected"]) == (action in {"applied", "adapted"}), "action_selection_mismatch")
require(isinstance(event["decision"]["reason"], str) and event["decision"]["reason"], "missing_decision_reason")
outcome = event["outcome"]
require(outcome["value"] in {"helpful", "harmful", "mixed", "no-effect", "unknown"}, "invalid_outcome")
require(outcome["basis"] in {"consumer-report", "model-assessment", "observed-result"}, "invalid_basis")
require(outcome["stage"] in {"decision", "implementation", "test", "operation"}, "invalid_stage")
require(isinstance(outcome["expectation"], str) and outcome["expectation"], "missing_expectation")
refs = outcome["evidence_refs"]
require(isinstance(refs, list) and len(refs) <= 10 and all(isinstance(x, str) and 0 < len(x) <= 1000 for x in refs), "invalid_evidence")
require(outcome["value"] == "unknown" or bool(refs) and isinstance(outcome["observation"], str) and bool(outcome["observation"]), "unevidenced_outcome")
def reduce_events(rows: list[tuple[dict, str]], errors: list[str]) -> list[dict]:
# A use is quarantined as a whole on identity collision or ambiguous lineage.
by_use: dict[str, dict[str, dict]] = defaultdict(dict)
identities: dict[tuple[str, str], tuple[str, str]] = {}
bad = set()
for event, digest in rows:
use = event["use_id"]
key = (event["consumer"]["repo"], event["event_id"])
if key in identities and identities[key] != (use, digest):
bad.update((use, identities[key][0]))
identities[key] = (use, digest)
by_use[use][digest] = event
latest = []
for use, nodes in by_use.items():
roots = [d for d, e in nodes.items() if e["previous_event_sha256"] is None]
seen = set()
current = roots[0] if len(roots) == 1 else None
previous_event = None
while current is not None and current not in seen:
seen.add(current)
event = nodes[current]
if previous_event is not None:
same = all(event[k] == previous_event[k] for k in ("kind", "reference", "context", "decision"))
same = same and event["consumer"]["repo"] == previous_event["consumer"]["repo"]
newer = datetime.fromisoformat(event["recorded_at"].replace("Z", "+00:00")) >= datetime.fromisoformat(previous_event["recorded_at"].replace("Z", "+00:00"))
if not same or not newer:
bad.add(use)
children = [d for d, e in nodes.items() if e["previous_event_sha256"] == current]
if len(children) > 1:
bad.add(use)
previous_event = event
current = children[0] if len(children) == 1 else None
if len(seen) != len(nodes) or use in bad:
errors.append("quarantined_event_lineage")
elif previous_event is not None:
latest.append(previous_event)
return latest
def collect(mode: str = "daily", now: datetime | None = None) -> dict:
require(mode in {"daily", "weekly", "monthly"}, "invalid_mode")
now = now or datetime.now(timezone.utc)
report: dict[str, Any] = {
"repo": "frontend-patterns", "mode": mode, "generated_at": now.isoformat(),
"status": "ok", "execution_ready": False, "model_calls": 0,
"eligible_tasks": None, "instrumented_repositories": 0,
"source_revisions": [], "receipt_events": 0, "duplicates": 0,
"real_uses": 0, "synthetic_uses": 0, "outcomes": {}, "decisions": {},
"signals": [], "errors": [],
"limits": ["Producer outcomes are not independently verified.",
"No admitted budget-enforced repository executor; automatic edits disabled.",
"Missing consumer instrumentation means coverage is unknown."],
"workplan": "FEP-WP-0008", "executor_dependency": "GLAS-WP-0012 / HFACT-WP-0001",
}
config = {"sources": []}
rows = []
events_seen = set()
total = 0
try:
root_sha = revision("frontend-patterns", "main")
report["source_revisions"].append("frontend-patterns@" + root_sha)
config = json.loads(raw("frontend-patterns", root_sha, "feedback/sources.json"))
require(config["schema_version"] == 1 and isinstance(config["sources"], list) and len(config["sources"]) <= 10, "invalid_source_roster")
source_ids = [s["repo"] for s in config["sources"]]
require(len(set(source_ids)) == len(source_ids), "duplicate_source")
for source in config["sources"]:
repo = source["repo"]
try:
sha = revision(repo, source["branch"])
report["source_revisions"].append(repo + "@" + sha)
index = json.loads(raw(repo, sha, source["index"]))
require(index["schema_version"] == 1 and isinstance(index["receipts"], list), "invalid_receipt_index")
total += len(index["receipts"])
require(total <= MAX_RECEIPTS, "collection_limit_exceeded")
report["instrumented_repositories"] += 1
for item in index["receipts"]:
try:
require(isinstance(item["sha256"], str) and DIGEST.fullmatch(item["sha256"]), "invalid_digest")
data = raw(repo, sha, item["path"])
digest = hashlib.sha256(data).hexdigest()
require(digest == item["sha256"], "receipt_digest_mismatch")
event = yaml.safe_load(data)
validate_event(event, repo)
if digest in events_seen:
report["duplicates"] += 1
continue
events_seen.add(digest)
rows.append((event, digest))
except (ValueError, KeyError, TypeError, AttributeError, yaml.YAMLError, httpx.HTTPError):
report["errors"].append("receipt_invalid_or_unavailable")
except (ValueError, KeyError, TypeError, AttributeError, httpx.HTTPError):
report["errors"].append("consumer_source_invalid_or_unavailable")
except (ValueError, KeyError, TypeError, AttributeError, httpx.HTTPError):
report["errors"].append("roster_invalid_or_unavailable")
report["receipt_events"] = len(rows)
latest = reduce_events(rows, report["errors"])
local = now.astimezone(ZoneInfo("Europe/Berlin"))
first = local.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
if mode == "monthly":
from datetime import timedelta
before = first - timedelta(days=1)
start = before.replace(day=1)
report["window"] = {"start": start.isoformat(), "end_exclusive": first.isoformat()}
# Select latest event as of the end of the month, not latest today.
eligible_rows = [(e, d) for e, d in rows if datetime.fromisoformat(e["recorded_at"].replace("Z", "+00:00")) < first]
latest = reduce_events(eligible_rows, report["errors"])
latest = [e for e in latest if datetime.fromisoformat(e["recorded_at"].replace("Z", "+00:00")) >= start]
report["synthetic_uses"] = sum(e["kind"] == "synthetic" for e in latest)
real = [e for e in latest if e["kind"] == "real"]
report["real_uses"] = len(real)
report["outcomes"] = dict(Counter(e["outcome"]["value"] for e in real))
report["decisions"] = dict(Counter(e["decision"]["action"] for e in real))
signals = []
for e in real:
value = e["outcome"]["value"]
if value in {"harmful", "mixed", "helpful"}:
signals.append({"use_id": e["use_id"], "outcome": value, "verification_required": True})
signals.sort(key=lambda s: ({"harmful": 0, "mixed": 1, "helpful": 2}[s["outcome"]], s["use_id"]))
report["signals"] = signals[:3]
report["unselected_signals"] = max(0, len(signals) - 3)
report["errors"] = dict(Counter(report["errors"]))
if report["errors"]:
report["status"] = "degraded"
elif not config["sources"]:
report["status"] = "no_consumers"
report["summary"] = f"Frontend patterns {mode}: {report['status']}; {len(real)} real uses, {len(signals)} unverified outcome signals. Automatic edits disabled."
return report
class FrontendPatternsResolver(ContextResolver):
def resolve(self, query: str, event: Any, params: dict[str, Any]) -> str:
token = DEADLINE.set(time.monotonic() + 45)
try:
report = collect(query)
finally:
DEADLINE.reset(token)
encoded = json.dumps(report, sort_keys=True, separators=(",", ":"))
require(len(encoded) <= 3900, "digest_size_limit")
return encoded
CONTEXT_RESOLVER_REGISTRY["frontend-patterns"] = FrontendPatternsResolver

View file

@ -0,0 +1,122 @@
import copy
import hashlib
import json
from datetime import datetime, timezone
from pathlib import Path
import pytest
import yaml
from activity_core.context_resolvers import frontend_patterns as f
from activity_core.definition_parser import parse_file
def event(kind='real'):
return dict(schema_version=1, use_id='demo:task:decision', event_id='first',
previous_event_sha256=None, recorded_at='2026-08-20T12:00:00Z', kind=kind,
consumer=dict(repo='demo', revision='a'*40, task_ref='workplans/task', producer='agent', run_ref='run:1'),
reference=dict(version='0.1', packaging_revision='b'*40, manifest_sha256='c'*64),
context=dict(problem='Choose frontend boundaries', constraints=[], considered=['CAND-0001'], selected=['CAND-0001']),
decision=dict(action='applied', reason='Fits the task'),
outcome=dict(value='unknown', basis='consumer-report', stage='decision', expectation='Fewer coupling defects', observation=None, evidence_refs=[]))
def fixtures(monkeypatch, events, duplicate=False):
blobs = [yaml.safe_dump(e).encode() for e in events]
entries = [dict(path=f'receipts/{i}.yaml', sha256=hashlib.sha256(b).hexdigest()) for i,b in enumerate(blobs)]
if duplicate and entries:
entries.append(entries[0])
def raw(repo, sha, path):
if repo == 'frontend-patterns':
return json.dumps(dict(schema_version=1,sources=[dict(repo='demo', branch='main', index='receipts/index.json')])).encode()
if path.endswith('index.json'):
return json.dumps(dict(schema_version=1,receipts=entries)).encode()
return blobs[int(path.split('/')[-1].split('.')[0])]
monkeypatch.setattr(f,'revision',lambda *args: 'd'*40)
monkeypatch.setattr(f,'raw',raw)
return entries
def test_unknown_real_duplicate_and_synthetic_separate(monkeypatch):
synthetic = event('synthetic'); synthetic['use_id']='fixture:task:decision'; synthetic['event_id']='fixture'
fixtures(monkeypatch,[event(),synthetic],True)
report=f.collect()
assert report['real_uses']==1 and report['synthetic_uses']==1
assert report['duplicates']==1 and report['outcomes']=={'unknown':1}
assert report['model_calls']==0 and report['execution_ready'] is False
def test_delayed_outcome_and_month_boundary(monkeypatch):
root=event(); follow=copy.deepcopy(root)
follow.update(event_id='later',previous_event_sha256=hashlib.sha256(yaml.safe_dump(root).encode()).hexdigest(),recorded_at='2026-09-05T00:00:00Z')
follow['outcome'].update(value='helpful',observation='Test passed',evidence_refs=['tests/result'])
fixtures(monkeypatch,[follow,root])
report=f.collect('monthly',datetime(2026,9,10,tzinfo=timezone.utc))
assert report['outcomes']=={'unknown':1}
assert f.collect()['outcomes']=={'helpful':1}
assert f.collect()['signals'][0]['verification_required'] is True
def test_identity_collision_quarantines_use(monkeypatch):
conflict=copy.deepcopy(event()); conflict['decision']['reason']='different'
fixtures(monkeypatch,[event(),conflict])
report=f.collect()
assert report['real_uses']==0 and report['status']=='degraded'
assert 'quarantined_event_lineage' in report['errors']
def test_missing_predecessor_quarantines_use(monkeypatch):
e=event(); e['previous_event_sha256']='e'*64
fixtures(monkeypatch,[e])
assert f.collect()['real_uses']==0
@pytest.mark.parametrize('value',['helpful','harmful','mixed','no-effect'])
def test_outcomes_need_evidence(value):
e=event(); e['outcome']['value']=value
with pytest.raises(ValueError): f.validate_event(e,'demo')
e['outcome'].update(observation='Observed result',evidence_refs=['result'])
f.validate_event(e,'demo')
@pytest.mark.parametrize('action',['rejected','no-match'])
def test_empty_selection_valid_for_abstention(action):
e=event(); e['context']['selected']=[]; e['decision']['action']=action
f.validate_event(e,'demo')
@pytest.mark.parametrize('path',['../secret','/etc/passwd','x//y','x/./y','x?token=a','https://evil/x'])
def test_reject_unsafe_path(path):
assert not f.path_ok(path)
def test_unavailable_is_not_no_data(monkeypatch):
monkeypatch.setattr(f,'revision',lambda *args: (_ for _ in ()).throw(ValueError('unavailable')))
report=f.collect()
assert report['status']=='degraded' and report['errors']
def test_empty_roster_is_no_consumers(monkeypatch):
monkeypatch.setattr(f,'revision',lambda *args:'a'*40)
monkeypatch.setattr(f,'raw',lambda *args:b'{"schema_version":1,"sources":[]}')
report=f.collect()
assert report['status']=='no_consumers' and report['eligible_tasks'] is None
assert len(f.FrontendPatternsResolver().resolve('daily',None,{})) < 3900
def test_over_limit_is_explicit(monkeypatch):
entries=fixtures(monkeypatch,[event()])
entries.extend([entries[0]]*100)
report=f.collect()
assert report['status']=='degraded' and report['receipt_events']==0
def test_domain_definitions_parse():
manifest = Path(__file__).parents[1] / 'k8s/railiance/20-runtime.yaml'
cm = next(d for d in yaml.safe_load_all(manifest.read_text()) if d and d.get('metadata', {}).get('name') == 'actcore-external-activity-definitions')
import tempfile
for name, body in cm['data'].items():
if not name.startswith('frontend-patterns-'): continue
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / name
path.write_text(body)
d=parse_file(path)
assert d.instructions[0]['model']=='deterministic'

View file

@ -0,0 +1,67 @@
---
id: ACTIVITY-WP-0040
type: workplan
title: "Deterministic frontend-patterns feedback collection and reports"
domain: infotech
repo: activity-core
status: active
owner: codex
topic_slug: activity-core
created: "2026-09-27"
updated: "2026-09-27"
---
User requested activity-core setup for FEP-WP-0008's unattended loop. This workplan
owns the reporting runtime; it does not claim an admitted repository executor.
## Implement bounded receipt collection
```task
id: ACTIVITY-WP-0040-T01
status: done
priority: high
```
The frontend-patterns resolver reads fixed-host, commit-pinned source indexes;
validates and reduces immutable receipts; quarantines conflicting histories;
separates synthetic use; reports errors and unknown coverage; emits no tasks or
model calls. New contract tests and existing instruction/sync tests verify this.
Domain definitions project into k8s/railiance/20-runtime.yaml, initially disabled.
Dockerfile.frontend-patterns overlays only two Python files on the deployed worker.
## Deploy, verify sinks and enable schedules
```task
id: ACTIVITY-WP-0040-T02
status: wait
priority: high
```
Target ADMINISTER @ realm:kubernetes/railiance01. User authorizes setup, but the
estate's 2026-09-21 Kubernetes change gate requires Git/ArgoCD for unmapped targets.
No activity-core ArgoCD application or lower-tier mapping was found. Route through
ArgoCD adoption or an explicit scoped exception before live mutation. Prepare
server dry-run and client diff for only the worker and external-definition
ConfigMap. Observed node requests: 3430m/4000m CPU and 9376Mi memory; verify again
before rollout. Do not rewrite API/router images or unrelated definitions.
After rollout, sync disabled definitions; manually exercise the report sinks;
enable all three domain definitions and matching projections; sync schedules;
record exact receipts and first natural scheduled execution. Rollback restores
activity-core:fi-publication-20260914 and disables the definitions with resync.
## Preserve execution dependency and complete operational handoff
```task
id: ACTIVITY-WP-0040-T03
status: todo
priority: high
```
GLAS-WP-0012 / HFACT-WP-0001 retain production profile readiness and spend custody.
FEP-WP-0008-T03 retains weekly investigation and publication; no legacy fallback.
The resolver always reports execution_ready=false until a separately reviewed
integration replaces that declaration with measured readiness. Source enrollment,
producer hooks, pagination and real-use acceptance remain FEP-WP-0008-T01/T02.
Keep this workplan active until production reporting is evidenced and unresolved
operational tasks have live owners. Zero enrolled consumers is not success evidence.