diff --git a/.custodian-brief.md b/.custodian-brief.md index 714bfa0..f4de94b 100644 --- a/.custodian-brief.md +++ b/.custodian-brief.md @@ -2,11 +2,19 @@ # Custodian Brief — activity-core **Domain:** infotech -**Last synced:** 2026-07-03 17:22 UTC +**Last synced:** 2026-07-02 00:19 UTC **State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* ## Active Workstreams +### LLM Output Robustness & The Producer Trust Boundary +Progress: 8/10 done | workstream_id: `4ef0d53b-1777-41ae-80c6-1b69fdb34726` + +**Open tasks:** +- ! Reproduce & Root-Cause The Failure `74fd16a5` + *(wait: Local analysis complete: mechanism is the unbounded ~1-per-workstream recommendation list (16 active workstreams; break at char 5268 ~rank 8-9); both first attempt and retry failed. Exact token + finish_reason are unrecoverable from activity-core (complete() drops finish_reason; report cap 4000 < 5268; log cap 2000). Remaining: pull llm-connect producer-side logs on railiance01 (cluster/operator-owned). Does NOT block T02/T03 — mitigation is identical regardless.)* +- ► Tests + Calibration Re-Entry `c881500b` + ### Adopt State Hub Beachhead Endpoint Progress: 0/2 done | workstream_id: `bbc07f9e-9323-4b2b-b556-c33b37d0b228` @@ -14,10 +22,19 @@ Progress: 0/2 done | workstream_id: `bbc07f9e-9323-4b2b-b556-c33b37d0b228` - ! Point STATE_HUB_URL at the beachhead `76b6132d` - ! Retire the bespoke actcore-state-hub-bridge proxy `526c2129` -### Intent gap closure -Progress: 5/6 done | workstream_id: `d64cfbba-6da7-4737-afb9-866afa0e9cda` +### Daily Triage LLM Reconciliation And Evidence +Progress: 2/5 done | workstream_id: `f2c73ac6-13f0-4005-82cc-76c7c9f9c8b9` **Open tasks:** +- ! Run Daily Triage Fixture Smoke `10e0df77` +- ! Collect Three Clean Scheduled Runs `dc6b9482` +- ! Close Handoff State `ecc57e21` + +### Intent gap closure +Progress: 4/6 done | workstream_id: `d64cfbba-6da7-4737-afb9-866afa0e9cda` + +**Open tasks:** +- ! Close Daily Triage Scheduled-Run Trust Gap `7012e4fd` - ! Promote Issue-Core Task Emission Safely `3854677b` ### Weekly Coding Retrospection schedule (Saturday evenings) diff --git a/.forgejo/workflows/ci-smoke.yaml b/.forgejo/workflows/ci-smoke.yaml deleted file mode 100644 index 6c2b29e..0000000 --- a/.forgejo/workflows/ci-smoke.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: CI Smoke - -on: - push: - branches: - - main - workflow_dispatch: - -jobs: - host-smoke: - runs-on: self-hosted - steps: - - name: Routing probe (host runner) - run: | - set -eu - echo "repository=${GITHUB_REPOSITORY:-unknown}" - echo "sha=${GITHUB_SHA:-unknown}" - echo "runner=${RUNNER_NAME:-unknown}" - uname -a - - container-smoke: - runs-on: ubuntu-latest - steps: - - name: Routing probe (container label) - run: | - set -eu - echo "container-smoke ok for ${GITHUB_REPOSITORY:-unknown}" \ No newline at end of file diff --git a/.forgejo/workflows/image.yaml b/.forgejo/workflows/image.yaml deleted file mode 100644 index 1217b8b..0000000 --- a/.forgejo/workflows/image.yaml +++ /dev/null @@ -1,50 +0,0 @@ -name: Build and Publish Container Image - -on: - push: - branches: - - main - paths: - - ".forgejo/workflows/image.yaml" - - "Dockerfile" - - "src/**" - - "migrations/**" - - "scripts/**" - - "activity-definitions/**" - - "event-types/**" - - "tasks/**" - - "pyproject.toml" - - "uv.lock" - - "alembic.ini" - workflow_dispatch: - -env: - REGISTRY: forgejo.coulomb.social - IMAGE_NAME: coulomb/activity-core - DOCKER_HOST: tcp://127.0.0.1:2375 - -jobs: - build-and-push: - runs-on: container-build - steps: - - name: Build and push image - env: - REGISTRY_USER: ${{ secrets.REGISTRY_USER }} - REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} - run: | - set -eu - REF="${GITHUB_SHA:-main}" - SHORT="${REF:0:7}" - mkdir -p buildctx "${HOME}/bin" - wget -qO /tmp/repo.tar.gz \ - "https://forgejo.coulomb.social/${GITHUB_REPOSITORY}/archive/${SHORT}.tar.gz" - tar xzf /tmp/repo.tar.gz -C buildctx --strip-components=1 - wget -qO- https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz \ - | tar xz --strip-components=1 -C "${HOME}/bin" docker/docker - export PATH="${HOME}/bin:${PATH}" - echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" -u "${REGISTRY_USER}" --password-stdin - IMAGE="${REGISTRY}/${IMAGE_NAME}" - docker build -t "${IMAGE}:latest" -t "${IMAGE}:main-${SHORT}" buildctx - docker push "${IMAGE}:latest" - docker push "${IMAGE}:main-${SHORT}" - echo "pushed ${IMAGE}:latest and ${IMAGE}:main-${SHORT}" \ No newline at end of file diff --git a/k8s/railiance/20-runtime.yaml b/k8s/railiance/20-runtime.yaml index 1cf93b2..1e017cd 100644 --- a/k8s/railiance/20-runtime.yaml +++ b/k8s/railiance/20-runtime.yaml @@ -14,8 +14,8 @@ data: LLM_CONNECT_URL: http://llm-connect.activity-core.svc.cluster.local:8080 LLM_CONNECT_TIMEOUT_SECONDS: "300" REPO_SCOPING_URL: http://repo-scoping.repo-scoping.svc.cluster.local:8020 - ISSUE_CORE_URL: http://actcore-issue-core-bridge.activity-core.svc.cluster.local:8765 - ISSUE_SINK_TYPE: "rest" + ISSUE_CORE_URL: http://issue-core.issue-core.svc.cluster.local:8010 + ISSUE_SINK_TYPE: "null" ACTIVITY_DEFINITION_DIRS: /etc/activity-core/external-definitions OPS_INVENTORY_PATH: /etc/activity-core/ops/service-inventory.yml INTER_HUB_URL: "" @@ -643,117 +643,6 @@ spec: periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 6 -apiVersion: v1 -kind: Service -metadata: - name: actcore-issue-core-bridge - namespace: activity-core - labels: - app.kubernetes.io/name: actcore-issue-core-bridge - app.kubernetes.io/part-of: activity-core -spec: - selector: - app.kubernetes.io/name: actcore-issue-core-bridge - ports: - - name: http - port: 8765 - targetPort: http ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: actcore-issue-core-bridge - namespace: activity-core - labels: - app.kubernetes.io/name: actcore-issue-core-bridge - app.kubernetes.io/part-of: activity-core -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: actcore-issue-core-bridge - template: - metadata: - labels: - app.kubernetes.io/name: actcore-issue-core-bridge - app.kubernetes.io/part-of: activity-core - spec: - hostNetwork: true - dnsPolicy: ClusterFirstWithHostNet - containers: - - name: proxy - image: activity-core:railiance01-prod - imagePullPolicy: Never - ports: - - name: http - containerPort: 18081 - command: - - python - - -c - - | - from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer - from urllib.error import HTTPError, URLError - from urllib.request import Request, urlopen - - TARGET = "http://127.0.0.1:18765" - HOP_HEADERS = {"connection", "host", "keep-alive", "proxy-authenticate", - "proxy-authorization", "te", "trailers", - "transfer-encoding", "upgrade"} - - class Proxy(BaseHTTPRequestHandler): - def do_GET(self): - self._proxy() - - def do_POST(self): - self._proxy() - - def do_PATCH(self): - self._proxy() - - def _proxy(self): - length = int(self.headers.get("content-length", "0") or "0") - body = self.rfile.read(length) if length else None - headers = { - key: value - for key, value in self.headers.items() - if key.lower() not in HOP_HEADERS - } - request = Request( - TARGET + self.path, - data=body, - headers=headers, - method=self.command, - ) - try: - timeout = 360 if self.command == "POST" else 30 - with urlopen(request, timeout=timeout) as response: - payload = response.read() - self.send_response(response.status) - for key, value in response.headers.items(): - if key.lower() not in HOP_HEADERS: - self.send_header(key, value) - self.end_headers() - self.wfile.write(payload) - except HTTPError as exc: - payload = exc.read() - self.send_response(exc.code) - self.end_headers() - self.wfile.write(payload) - except URLError as exc: - self.send_response(502) - self.end_headers() - self.wfile.write(str(exc).encode()) - - ThreadingHTTPServer(("0.0.0.0", 18081), Proxy).serve_forever() - readinessProbe: - httpGet: - path: /healthz - port: http - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 ---- --- apiVersion: batch/v1 kind: Job diff --git a/src/activity_core/context_resolvers/state_hub.py b/src/activity_core/context_resolvers/state_hub.py index 3b7bd78..4de5439 100644 --- a/src/activity_core/context_resolvers/state_hub.py +++ b/src/activity_core/context_resolvers/state_hub.py @@ -513,19 +513,11 @@ def _daily_triage_digest(params: dict[str, Any]) -> str: max_workstreams = int(params.get("max_workstreams", 12)) max_next_steps = int(params.get("max_next_steps", 8)) open_workstreams = _open_workstream_digest(summary, workplan_index, max_workstreams) - ranked_suggestions = summary.get("ranked_suggestions") or [] - if not isinstance(ranked_suggestions, list): - ranked_suggestions = [] - digest = { "generated_at": summary.get("generated_at"), "totals": summary.get("totals", {}), "open_workstreams": open_workstreams, "next_steps": [_safe_next_step(item) for item in next_steps[:max_next_steps]], - "ranked_suggestions": [ - _safe_ranked_suggestion(item) - for item in ranked_suggestions[: int(params.get("max_suggestions", 10))] - ], "inbox": { "unread_count": len(inbox), "samples": [_safe_inbox_item(item) for item in inbox[:3]], @@ -627,19 +619,6 @@ def _safe_next_step(item: dict[str, Any]) -> dict[str, Any]: } -def _safe_ranked_suggestion(item: dict[str, Any]) -> dict[str, Any]: - return { - "id": item.get("id"), - "title": _short_text(item.get("title", ""), 120), - "stage": item.get("stage"), - "domain_slug": item.get("domain_slug"), - "origin_ref": item.get("origin_ref"), - "relevance": item.get("relevance"), - "wsjf": item.get("wsjf"), - "last_requested_at": item.get("last_requested_at"), - } - - def _safe_inbox_item(item: dict[str, Any]) -> dict[str, Any]: return { "id": item.get("id"), diff --git a/tests/test_state_hub_context_resolver.py b/tests/test_state_hub_context_resolver.py index 0ff5367..0f8727e 100644 --- a/tests/test_state_hub_context_resolver.py +++ b/tests/test_state_hub_context_resolver.py @@ -486,18 +486,6 @@ def test_daily_triage_digest_is_curated_scalar_json(monkeypatch) -> None: "/state/summary": { "generated_at": "2026-05-19T05:20:00Z", "totals": {"tasks": {"todo": 4, "wait": 1}}, - "ranked_suggestions": [ - { - "id": "sug-1", - "title": "Issue-core ingestion API key path", - "stage": "suggestion", - "domain_slug": "custodian", - "origin_ref": "issue-core-ingestion-api-key", - "relevance": 2, - "wsjf": 4.7, - "last_requested_at": "2026-05-19T04:00:00Z", - } - ], "topics": [ { "slug": "custodian", @@ -606,4 +594,3 @@ def test_daily_triage_digest_is_curated_scalar_json(monkeypatch) -> None: assert digest["deterministic_scoring"]["future_mode"] == ( "code_score_high_gain_high_effort_candidates" ) - assert digest["ranked_suggestions"][0]["origin_ref"] == "issue-core-ingestion-api-key" diff --git a/workplans/ACTIVITY-WP-0008-weekly-coding-retro.md b/workplans/ACTIVITY-WP-0008-weekly-coding-retro.md index 5453dc6..794cb3d 100644 --- a/workplans/ACTIVITY-WP-0008-weekly-coding-retro.md +++ b/workplans/ACTIVITY-WP-0008-weekly-coding-retro.md @@ -4,11 +4,11 @@ type: workplan title: "Weekly Coding Retrospection schedule (Saturday evenings)" domain: custodian repo: activity-core -status: active +status: blocked owner: codex topic_slug: custodian created: "2026-06-07" -updated: "2026-07-03" +updated: "2026-06-17" state_hub_workstream_id: "7387fc50-1f2c-471a-9d85-bb085cbd0b63" --- @@ -107,11 +107,3 @@ specs because that weekly read model has zero suggestions. The schedule remains disabled until a non-empty weekly read model, or an explicit operator decision that a zero-suggestion dry-run is an acceptable enablement proof, confirms correct routing and no duplicate target tasks on re-run. - -2026-07-03 reevaluation: `AGENTIC-WP-0010` remains finished and the resolver -smoke still works, but State Hub still has only the 2026-06-07 weekly -`coding_retro` read model (`ec20ac1c`, `window.days=7`) with zero suggestions. -No newer weekly publish exists yet. This is a **time-controlled wait** on the -next Saturday 19:00 Europe/Berlin publish (or an operator decision to enable on -the zero-suggestion proof), not a hard blocker. Workplan status returns to -`active`; T03 stays `wait`. diff --git a/workplans/ACTIVITY-WP-0009-intent-gap-closure.md b/workplans/ACTIVITY-WP-0009-intent-gap-closure.md index b573dfc..1eb0686 100644 --- a/workplans/ACTIVITY-WP-0009-intent-gap-closure.md +++ b/workplans/ACTIVITY-WP-0009-intent-gap-closure.md @@ -4,11 +4,11 @@ type: workplan title: "Intent gap closure" domain: custodian repo: activity-core -status: active +status: blocked owner: codex topic_slug: custodian created: "2026-06-16" -updated: "2026-07-03" +updated: "2026-06-18" state_hub_workstream_id: "d64cfbba-6da7-4737-afb9-866afa0e9cda" --- @@ -38,7 +38,7 @@ The preserved analysis lives in: ```task id: ACTIVITY-WP-0009-T01 -status: done +status: wait priority: high state_hub_task_id: "7012e4fd-2530-49b7-9c2f-1d949809a144" ``` @@ -73,12 +73,6 @@ fixture smoke passed. The remaining work is the activity-core / Railiance runtime reconciliation and daily-triage evidence collection path captured in `ACTIVITY-WP-0010`. -2026-07-03 closeout: `ACTIVITY-WP-0010` and `ACTIVITY-WP-0006-T03` now record -three consecutive clean scheduled daily triage runs (2026-06-28 through -2026-06-30, streak continuing through 2026-07-03) with matching State Hub -progress, working-memory notes, and calibration feedback. The scheduled-run trust -gap is closed. T01 is done. - ## Promote Issue-Core Task Emission Safely ```task @@ -106,10 +100,6 @@ Current wait reason: production Railiance currently uses null-sink audit mode; live issue-core credentials/access and duplicate-handling are not yet verified for this repo. -2026-07-03 reevaluation: this is an external-credential gate, not a workplan -blocker. Keep T02 at `wait` with `blocking_reason` describing the issue-core -approval path; the workplan returns to `active`. - ## Resolve Review-Required Contract Drift ```task @@ -241,6 +231,7 @@ Agent-actionable closure is complete for T03, T04, T05, and T06. Remaining waits: +- T01 waits on real scheduled daily triage run evidence. - T02 waits on issue-core production endpoint/credentials and duplicate-handling approval. diff --git a/workplans/ACTIVITY-WP-0010-daily-triage-llm-reconciliation.md b/workplans/ACTIVITY-WP-0010-daily-triage-llm-reconciliation.md index 3e99006..d385bfb 100644 --- a/workplans/ACTIVITY-WP-0010-daily-triage-llm-reconciliation.md +++ b/workplans/ACTIVITY-WP-0010-daily-triage-llm-reconciliation.md @@ -4,11 +4,11 @@ type: workplan title: "Daily Triage LLM Reconciliation And Evidence" domain: custodian repo: activity-core -status: finished +status: blocked owner: codex topic_slug: custodian created: "2026-06-18" -updated: "2026-07-03" +updated: "2026-06-27" state_hub_workstream_id: "f2c73ac6-13f0-4005-82cc-76c7c9f9c8b9" --- @@ -135,7 +135,7 @@ Hub handoff message `9a074b7c-4b87-4e3c-a6bf-e1fe5580daa8` asks ```task id: ACTIVITY-WP-0010-T03 -status: done +status: wait priority: high state_hub_task_id: "10e0df77-c230-4a82-b720-23c66bd17c0a" ``` @@ -173,17 +173,11 @@ Done when: after the WP-0016 code/schema/runtime-prompt deployment due the 2026-06-26 and 2026-06-27 malformed-output failures. -2026-07-03 closeout: `ACTIVITY-WP-0016` is finished and scheduled -`daily_triage` progress from **2026-06-28 through 2026-07-03** all show -`output_validated=true` with working-memory notes. The `railiance-cluster` -inbox reply (`e360e38f`, 2026-07-01) confirms the live runtime, provider Secret -presence, and in-namespace fixture smoke. T03 is done. - ## Collect Three Clean Scheduled Runs ```task id: ACTIVITY-WP-0010-T04 -status: done +status: wait priority: high state_hub_task_id: "dc6b9482-cf43-4fc5-994b-dcd7dea47db7" ``` @@ -210,17 +204,11 @@ Done when: Railiance runtime prompt and max_tokens changes, run a live smoke, then restart the three-consecutive-scheduled-run gate from zero. -2026-07-03 closeout: post-`ACTIVITY-WP-0016` scheduled runs on **2026-06-28, -2026-06-29, and 2026-06-30** (`6a44d6dd`, `1dfb47c9`, `ac3d71a0`) are three -consecutive clean scheduled successes with matching State Hub progress and -working-memory notes. The streak continued through 2026-07-03. Calibration -feedback was already recorded when `ACTIVITY-WP-0006-T03` closed. T04 is done. - ## Close Handoff State ```task id: ACTIVITY-WP-0010-T05 -status: done +status: wait priority: medium state_hub_task_id: "ecc57e21-1716-4daa-aba6-d8a6d824e4ed" ``` @@ -235,8 +223,3 @@ Done when: - any temporary `needs_human` flags created for the llm-connect provider/config handoff are cleared or replaced by a narrower follow-up; - this workplan is marked `finished`. - -2026-07-03 closeout: `ACTIVITY-WP-0006-T03` already records the three-run -calibration evidence. `ACTIVITY-WP-0009-T01` is updated in the same pass. -No temporary `needs_human` flags remain for the llm-connect provider/config -handoff. T05 is done; this workplan is `finished`. diff --git a/workplans/ACTIVITY-WP-0015-adopt-statehub-beachhead-endpoint.md b/workplans/ACTIVITY-WP-0015-adopt-statehub-beachhead-endpoint.md index 75b8963..2398848 100644 --- a/workplans/ACTIVITY-WP-0015-adopt-statehub-beachhead-endpoint.md +++ b/workplans/ACTIVITY-WP-0015-adopt-statehub-beachhead-endpoint.md @@ -4,11 +4,11 @@ type: workplan title: "Adopt State Hub Beachhead Endpoint" domain: infotech repo: activity-core -status: active +status: blocked owner: claude topic_slug: activity-core created: "2026-06-24" -updated: "2026-07-03" +updated: "2026-06-24" state_hub_workstream_id: "bbc07f9e-9323-4b2b-b556-c33b37d0b228" --- @@ -19,15 +19,10 @@ Carries the **blocked remainder** of [[ACTIVITY-WP-0014]] T05. The in-repo half client-side adoption that depends on the state-hub-owned **beachhead** capability (per-machine read cache + write outbox) existing first. -**Waiting on:** the state-hub beachhead (proposal sent to the `state-hub` agent, +**Blocked on:** the state-hub beachhead (proposal sent to the `state-hub` agent, 2026-06-23). Do not build queue/cache logic in activity-core — see [[statehub-beachhead-principle]]. -2026-07-03 reevaluation: no state-hub-owned beachhead endpoint is available yet. -`CUST-WP-0054` now tracks a files-first dev beachhead, but activity-core client -adoption still depends on the state-hub capability shipping first. Both tasks -remain `wait` on external delivery; the workplan is `active`, not `blocked`. - ## Point STATE_HUB_URL at the beachhead ```task diff --git a/workplans/ACTIVITY-WP-0016-llm-output-robustness-trust-boundary.md b/workplans/ACTIVITY-WP-0016-llm-output-robustness-trust-boundary.md index ded096f..f601013 100644 --- a/workplans/ACTIVITY-WP-0016-llm-output-robustness-trust-boundary.md +++ b/workplans/ACTIVITY-WP-0016-llm-output-robustness-trust-boundary.md @@ -4,7 +4,7 @@ type: workplan title: "LLM Output Robustness & The Producer Trust Boundary" domain: custodian repo: activity-core -status: finished +status: active owner: codex topic_slug: custodian created: "2026-06-26" @@ -90,7 +90,7 @@ there are two sensible strategies — and they compose: ```task id: ACTIVITY-WP-0016-T01 -status: cancel +status: wait priority: high state_hub_task_id: "74fd16a5-4ea5-4dfe-8526-dfa27cf76138" ``` @@ -353,7 +353,7 @@ Done when: ```task id: ACTIVITY-WP-0016-T05 -status: done +status: progress priority: high state_hub_task_id: "c881500b-5459-4620-81c0-b176971e989f" ``` @@ -415,20 +415,3 @@ for the next 07:20 Berlin fire, then confirm State Hub `daily_triage` evidence i - **Boundary discipline:** keeps activity-core inside its SCOPE — this hardens the instruction-executor output contract; it does not move provider credentials, cluster reconciliation, or task lifecycle into this repo. - - -## Closure 2026-07-02 (RAIL-BS-WP-0008 live deploy) - -- T05 done: the robustness bundle (strict per-item schema + T03 quarantine - parser + bounded top-7/NDJSON runtime prompt, activity-core `7612112`) was - deployed to railiance01 and live-proven. A manually triggered daily-triage - run produced a clean schema-valid report with exactly 7 ranked - recommendations: State Hub event `24d2d321-c761-47f7-bf9e-7950a6253c21`, - `output_validated=true`, working memory written. Calibration re-entry: the - three-clean-run streak (WP-0006-T03 / WP-0010-T04) restarts from this run. -- T01 cancelled: the raw 2026-06-26 llm-connect response is unrecoverable - (stateless pod, no response store, log stream holds only 2 startup lines - since 2026-06-19). Root cause stands on the retained 4000-char preview and - break-at-char-5268 evidence: output exceeded the old ~1200-token budget and - truncated mid-JSON. The deployed mitigation (1800-token headroom, bounded - top-7, per-item recovery) addresses exactly that failure mode.