Close WP-0035 and count declared sink evidence in automation status
The status surface queried only four fixed State Hub event types, so bounded-operation evidence (Forgejo prune, CNPG, SBOM) showed evidence=0 despite successful runs. Default queries now include every state-hub-progress event type the definitions' report/evidence sinks declare. Three natural prune fires (2026-09-06/13/20) supply the last missing bounded-operation evidence, so ACTIVITY-WP-0035-T08 and the workplan finish. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 241500@bnt-lap001 Assistant-Session: 4a77db80-b523-4a03-83c4-e3da08755c30
This commit is contained in:
parent
6b3c4222dd
commit
0c403d2239
3 changed files with 87 additions and 8 deletions
|
|
@ -150,6 +150,7 @@ def file_definitions() -> list[dict[str, Any]]:
|
||||||
"trigger_type": trigger_type,
|
"trigger_type": trigger_type,
|
||||||
"trigger_config": trigger,
|
"trigger_config": trigger,
|
||||||
"instructions": list(definition.instructions or []),
|
"instructions": list(definition.instructions or []),
|
||||||
|
"context_sources": list(definition.context_sources or []),
|
||||||
"source": "files",
|
"source": "files",
|
||||||
})
|
})
|
||||||
return sorted(records, key=lambda item: item["name"])
|
return sorted(records, key=lambda item: item["name"])
|
||||||
|
|
@ -166,7 +167,7 @@ def filter_definitions(definitions: list[dict[str, Any]], ids: list[str], names:
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def progress_event_types(args: argparse.Namespace) -> list[str | None]:
|
def progress_event_types(args: argparse.Namespace, definitions: list[dict[str, Any]] | None = None) -> list[str | None]:
|
||||||
raw = args.progress_event_type
|
raw = args.progress_event_type
|
||||||
if raw is None:
|
if raw is None:
|
||||||
env_value = os.environ.get("AUTOMATION_STATUS_PROGRESS_EVENT_TYPES")
|
env_value = os.environ.get("AUTOMATION_STATUS_PROGRESS_EVENT_TYPES")
|
||||||
|
|
@ -175,11 +176,31 @@ def progress_event_types(args: argparse.Namespace) -> list[str | None]:
|
||||||
"schedule_miss",
|
"schedule_miss",
|
||||||
"ops_inventory_probe",
|
"ops_inventory_probe",
|
||||||
"legacy_meter_weekly_review",
|
"legacy_meter_weekly_review",
|
||||||
|
*declared_progress_event_types(definitions or []),
|
||||||
]
|
]
|
||||||
values = [item.strip() for item in raw if item and item.strip()]
|
values = list(dict.fromkeys(item.strip() for item in raw if item and item.strip()))
|
||||||
return [None if item == "all" else item for item in values]
|
return [None if item == "all" else item for item in values]
|
||||||
|
|
||||||
|
|
||||||
|
def declared_progress_event_types(definitions: list[dict[str, Any]]) -> list[str]:
|
||||||
|
"""Event types the definitions' State Hub report/evidence sinks write."""
|
||||||
|
sinks: list[Any] = []
|
||||||
|
for definition in definitions:
|
||||||
|
for instruction in definition.get("instructions") or []:
|
||||||
|
if isinstance(instruction, dict):
|
||||||
|
sinks.extend(instruction.get("report_sinks") or [])
|
||||||
|
for source in definition.get("context_sources") or []:
|
||||||
|
params = source.get("params") if isinstance(source, dict) else None
|
||||||
|
if isinstance(params, dict):
|
||||||
|
raw = params.get("evidence_sinks") or params.get("evidence_sink") or []
|
||||||
|
sinks.extend(raw if isinstance(raw, list) else [raw])
|
||||||
|
found: list[str] = []
|
||||||
|
for sink in sinks:
|
||||||
|
if isinstance(sink, dict) and sink.get("type") == "state-hub-progress" and sink.get("event_type"):
|
||||||
|
found.append(str(sink["event_type"]))
|
||||||
|
return list(dict.fromkeys(found))
|
||||||
|
|
||||||
|
|
||||||
def expected_fires(definition: dict[str, Any], window: dict[str, Any]) -> list[str]:
|
def expected_fires(definition: dict[str, Any], window: dict[str, Any]) -> list[str]:
|
||||||
cfg = definition.get("trigger_config") or {}
|
cfg = definition.get("trigger_config") or {}
|
||||||
if definition.get("trigger_type") == "scheduled":
|
if definition.get("trigger_type") == "scheduled":
|
||||||
|
|
@ -252,7 +273,7 @@ async def db_definitions(db_url: str) -> list[dict[str, Any]]:
|
||||||
try:
|
try:
|
||||||
async with engine.connect() as conn:
|
async with engine.connect() as conn:
|
||||||
result = await conn.execute(text(
|
result = await conn.execute(text(
|
||||||
"select id, name, enabled, trigger_type, trigger_config, instructions_json, version "
|
"select id, name, enabled, trigger_type, trigger_config, instructions_json, context_sources, version "
|
||||||
"from activity_definitions where trigger_type in ('cron', 'scheduled') order by name"
|
"from activity_definitions where trigger_type in ('cron', 'scheduled') order by name"
|
||||||
))
|
))
|
||||||
return [{
|
return [{
|
||||||
|
|
@ -262,6 +283,7 @@ async def db_definitions(db_url: str) -> list[dict[str, Any]]:
|
||||||
"trigger_type": row["trigger_type"],
|
"trigger_type": row["trigger_type"],
|
||||||
"trigger_config": dict(row["trigger_config"] or {}),
|
"trigger_config": dict(row["trigger_config"] or {}),
|
||||||
"instructions": list(row["instructions_json"] or []),
|
"instructions": list(row["instructions_json"] or []),
|
||||||
|
"context_sources": list(row["context_sources"] or []),
|
||||||
"version": row["version"],
|
"version": row["version"],
|
||||||
"source": "db",
|
"source": "db",
|
||||||
} for row in result.mappings().all()]
|
} for row in result.mappings().all()]
|
||||||
|
|
@ -544,7 +566,7 @@ async def build_report(args: argparse.Namespace) -> tuple[dict[str, Any], int]:
|
||||||
window,
|
window,
|
||||||
limit=args.progress_limit,
|
limit=args.progress_limit,
|
||||||
timeout_seconds=timeout,
|
timeout_seconds=timeout,
|
||||||
event_types=progress_event_types(args),
|
event_types=progress_event_types(args, definitions),
|
||||||
)
|
)
|
||||||
wm_evidence, sources["working_memory"] = load_working_memory_evidence(args.working_memory_dir, window)
|
wm_evidence, sources["working_memory"] = load_working_memory_evidence(args.working_memory_dir, window)
|
||||||
temporal_by_activity, sources["temporal"] = await load_temporal_visibility(
|
temporal_by_activity, sources["temporal"] = await load_temporal_visibility(
|
||||||
|
|
|
||||||
|
|
@ -323,3 +323,38 @@ def test_inventory_cli_emits_json(monkeypatch, capsys) -> None:
|
||||||
assert exit_code == 0
|
assert exit_code == 0
|
||||||
assert payload["mode"] == "automation-inventory"
|
assert payload["mode"] == "automation-inventory"
|
||||||
assert payload["automations"][0]["name"] == "Daily Check"
|
assert payload["automations"][0]["name"] == "Daily Check"
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_progress_event_types_include_declared_sinks() -> None:
|
||||||
|
definitions = [
|
||||||
|
{
|
||||||
|
"instructions": [{"report_sinks": [
|
||||||
|
{"type": "state-hub-progress", "event_type": "sbom_catchup"},
|
||||||
|
{"type": "working-memory"},
|
||||||
|
]}],
|
||||||
|
"context_sources": [
|
||||||
|
{"type": "shell", "params": {"evidence_sinks": [
|
||||||
|
{"type": "state-hub-progress", "event_type": "forgejo_package_prune"},
|
||||||
|
]}},
|
||||||
|
{"type": "shell", "params": {"evidence_sinks": [
|
||||||
|
{"type": "state-hub-progress", "event_type": "daily_triage"},
|
||||||
|
]}},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
args = status.parse_args([])
|
||||||
|
|
||||||
|
event_types = status.progress_event_types(args, definitions)
|
||||||
|
|
||||||
|
assert "forgejo_package_prune" in event_types
|
||||||
|
assert "sbom_catchup" in event_types
|
||||||
|
assert event_types.count("daily_triage") == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_progress_event_types_ignore_declared_sinks() -> None:
|
||||||
|
definitions = [{"context_sources": [{"params": {"evidence_sinks": [
|
||||||
|
{"type": "state-hub-progress", "event_type": "forgejo_package_prune"},
|
||||||
|
]}}]}]
|
||||||
|
args = status.parse_args(["--progress-event-type", "daily_triage"])
|
||||||
|
|
||||||
|
assert status.progress_event_types(args, definitions) == ["daily_triage"]
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ type: workplan
|
||||||
title: "Make the execution boundary enforceable and the review contract truthful"
|
title: "Make the execution boundary enforceable and the review contract truthful"
|
||||||
domain: infotech
|
domain: infotech
|
||||||
repo: activity-core
|
repo: activity-core
|
||||||
status: active
|
status: finished
|
||||||
flavor: planning
|
flavor: planning
|
||||||
owner: codex
|
owner: codex
|
||||||
topic_slug: activity-core
|
topic_slug: activity-core
|
||||||
priority: high
|
priority: high
|
||||||
created: "2026-08-23"
|
created: "2026-08-23"
|
||||||
updated: "2026-09-04"
|
updated: "2026-09-21"
|
||||||
quality_dor: DoR-Ok
|
quality_dor: DoR-Ok
|
||||||
quality_dor_at: "2026-08-23"
|
quality_dor_at: "2026-08-23"
|
||||||
quality_dor_by: codex
|
quality_dor_by: codex
|
||||||
|
|
@ -271,7 +271,7 @@ activity-core is a general task executor.
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: ACTIVITY-WP-0035-T08
|
id: ACTIVITY-WP-0035-T08
|
||||||
status: progress
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "2ae1aca9-1c86-5b1a-b9fd-95059a24cd65"
|
state_hub_task_id: "2ae1aca9-1c86-5b1a-b9fd-95059a24cd65"
|
||||||
```
|
```
|
||||||
|
|
@ -298,7 +298,7 @@ Depends on T02–T07 as applicable.
|
||||||
- [x] Instruction audit wording matches persisted evidence and raw sensitive payloads remain excluded
|
- [x] Instruction audit wording matches persisted evidence and raw sensitive payloads remain excluded
|
||||||
- [x] `review_required` has truthful, tested hold/release or advisory-only semantics
|
- [x] `review_required` has truthful, tested hold/release or advisory-only semantics
|
||||||
- [x] Misleading executor/CRUD/README surfaces are removed or precisely qualified
|
- [x] Misleading executor/CRUD/README surfaces are removed or precisely qualified
|
||||||
- [ ] Full tests pass and production rollout preserves current schedules and evidence
|
- [x] Full tests pass and production rollout preserves current schedules and evidence
|
||||||
|
|
||||||
## Implementation evidence — 2026-08-23
|
## Implementation evidence — 2026-08-23
|
||||||
|
|
||||||
|
|
@ -363,6 +363,28 @@ Depends on T02–T07 as applicable.
|
||||||
lifecycle. That residual must preserve multi-cluster over-protection and the
|
lifecycle. That residual must preserve multi-cluster over-protection and the
|
||||||
existing apply refusal when the inventory is absent or empty.
|
existing apply refusal when the inventory is absent or empty.
|
||||||
|
|
||||||
|
## Closeout — 2026-09-21
|
||||||
|
|
||||||
|
- The third bounded-operation class now has natural post-rollout evidence.
|
||||||
|
Weekly Forgejo Package Prune fired unassisted on 2026-09-06, 09-13, and
|
||||||
|
09-20 against the durable inventory path, each with `apply: true`,
|
||||||
|
`error_count=0`, and protected skips honoured:
|
||||||
|
|
||||||
|
| Run | Deleted | Protected skips | State Hub progress |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `65354265-6425-51fb-be5f-20bfae99f550` | 175 | 4 | 2026-09-06T03:30:16Z |
|
||||||
|
| `d0ce2658-0bab-5fe3-9377-36d5c56193a4` | 85 | 5 | 2026-09-13T03:30:14Z |
|
||||||
|
| `fac35de6-095d-59ec-8af0-f8a15440d3b4` | 52 | 5 | `c5e538a7-e2a6-49df-9da5-1aef315ee77e` |
|
||||||
|
|
||||||
|
No schedule was manually fired. CNPG backup and SBOM catch-up remained
|
||||||
|
`completed` for every expected fire since 2026-09-05.
|
||||||
|
- Residual found during closeout: the deterministic status surface reported
|
||||||
|
`evidence=0` for these runs because its default State Hub query covered only
|
||||||
|
four fixed event types. Source now also queries every `state-hub-progress`
|
||||||
|
event type declared by definition report/evidence sinks (504 passed,
|
||||||
|
1 skipped). It takes effect in production at the next image rollout; the
|
||||||
|
evidence above was read directly from State Hub.
|
||||||
|
|
||||||
## Gap disposition
|
## Gap disposition
|
||||||
|
|
||||||
| 2026-08-23 gap | This workplan |
|
| 2026-08-23 gap | This workplan |
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue