Compare commits
1 commit
master
...
task/ai-cl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b124e54e66 |
|
|
@ -1,116 +0,0 @@
|
|||
---
|
||||
name: kb-authoring
|
||||
description: Conventions for writing/editing source knowledge-base documents under kb/**/*.md (frontmatter schema, type taxonomy, visibility default, validation). Trigger whenever creating or editing a file under kb/ — this is about authoring the sources, not publishing kb-site (see kb-publish for that).
|
||||
---
|
||||
|
||||
## Scope
|
||||
|
||||
This skill governs **writing kb/\*\*/\*.md documents themselves** — the OKF-format
|
||||
knowledge base sources. It is not about publishing them: for "should I run the
|
||||
publish script", see the `kb-publish` skill, referenced again at the bottom
|
||||
here.
|
||||
|
||||
`docs/sessions/*.md` files use `type: session-log` frontmatter and are validated
|
||||
by the same script, but they are **not** kb/ documents — they are a running
|
||||
diary of work sessions. **Never convert a session log into a kb/ doc** and
|
||||
never move/copy a kb/ doc's content into docs/sessions/. If material in a
|
||||
session log deserves a permanent home (a decision, an incident writeup, a
|
||||
runbook), write a **new** file under kb/ that captures it properly — don't
|
||||
relocate the diary entry.
|
||||
|
||||
## Frontmatter schema (OKF v0.1)
|
||||
|
||||
Every kb/ document opens with a YAML frontmatter block (`---` ... `---`) as
|
||||
the first thing in the file. Validated by `scripts/kb/check_okf.py` — read
|
||||
that file if you need the exact rules; summary:
|
||||
|
||||
| Field | Required | Values | Notes |
|
||||
|---|---|---|---|
|
||||
| `okf` | yes | `"0.1"` | Pinned. Always this literal string, quoted. |
|
||||
| `type` | yes | one of the taxonomy below | Determines which `kb/<type>s/` directory the file lives in. |
|
||||
| `visibility` | yes | `private` \| `public` | **Default is `private`.** See below — never default to `public`. |
|
||||
| `status` | yes | `active` \| `deprecated` \| `planned` | `planned` = decisions not yet made / work not yet started. `deprecated` requires `superseded_by`. |
|
||||
| `updated` | yes | `YYYY-MM-DD` | Bump every time you edit the file's content. |
|
||||
| `links` | yes (may be `[]`) | list of paths | Relative to **this file's own directory**, e.g. `../phases/backlog.md` or `sibling-doc.md`. Every entry must resolve to an existing file — check_okf verifies this. |
|
||||
| `as_of` | only for `type: audit` | `YYYY-MM-DD` | Required exactly when `type: audit`, forbidden otherwise. See "audit" below for why this is a separate field from `updated`. |
|
||||
| `superseded_by` | only when `status: deprecated` | free text or path | Required exactly when `status: deprecated`, forbidden otherwise. Points to whatever replaced this doc (a doc path, or prose describing the replacement if there's no single successor file). |
|
||||
| `contradicts` | optional | list | Free-text notes about known conflicts with other sources (e.g. "CLAUDE.md says X, but the directory doesn't exist"). Entries that look like a `.md` path (contain `/` and end in `.md`) are checked for existence like `links`; plain-text entries are not. |
|
||||
| `stub` | optional | bool | Marks a doc as a placeholder/incomplete. Must be a real YAML bool (`true`/`false`), not a string. |
|
||||
|
||||
## Type taxonomy — kb/<type>s/
|
||||
|
||||
| `type` | Directory | What goes here |
|
||||
|---|---|---|
|
||||
| `node` | `kb/nodes/` | One doc per physical/virtual host — role, configured services, runtime data paths. Mirrors `hosts/<node>/`. |
|
||||
| `service` | `kb/services/` | One doc per deployed service — what it is, how it's used/configured. Mirrors `services/<svc>/`. |
|
||||
| `subsystem` | `kb/subsystems/` | Cross-cutting architecture/design docs describing how something works *in general* (access model, agent system, deployment conventions) — not tied to a single node or service. Kept in sync with current reality (unlike `audit`, see below). |
|
||||
| `decision` | `kb/decisions/` | A choice that was made (or is still open, `status: planned`) plus its rationale — forward-looking, governs future behavior. Gets edited in place and re-dated as the decision evolves; it is not a historical log of what happened. |
|
||||
| `incident` | `kb/incidents/` | A factual account of something that broke: symptom, root cause, fix/status, at a point in time. Slug conventionally date-prefixed (`YYYY-MM-DD-short-description.md`) since incidents are anchored to when they happened. Content generally stays close to the as-happened account rather than being rewritten into "current state" prose. |
|
||||
| `runbook` | `kb/runbooks/` | Step-by-step operational procedure — deploy, install, recover, troubleshoot. Imperative, command-heavy, meant to be followed live. |
|
||||
| `phase` | `kb/phases/` | A project/milestone plan broken into steps, tracking progress (including backlog/roll-up index docs). |
|
||||
| `audit` | `kb/audits/` | A **point-in-time snapshot** of actual/verified state (ground truth recon), never an ongoing description. Requires `as_of` — the date the finding was true — kept distinct from `updated` (the date the doc text was last edited) precisely because an audit's findings can go stale even when nobody touches the file. Slug conventionally date-suffixed (`topic-YYYY-MM-DD.md`). |
|
||||
|
||||
`session-log` also exists as a `type` value (for `docs/sessions/`) but is **out
|
||||
of scope for `kb/`** — see Scope above.
|
||||
|
||||
### Decision vs incident vs runbook vs audit — how ambiguous cases got resolved
|
||||
|
||||
This came up repeatedly during the kb/ migration (docs that mixed genres got
|
||||
split, not force-fit into one type):
|
||||
|
||||
- **decision vs incident**: a doc that both narrates "here's what broke" *and*
|
||||
states "here's the guardrail we adopted because of it" is two documents.
|
||||
Split the incident account into `kb/incidents/`, keep (or extract) the
|
||||
resulting decision/guardrail into `kb/decisions/`. Example:
|
||||
`home-assistant/DESIGN.md` → `kb/decisions/ha-configs-as-code.md` +
|
||||
`kb/incidents/2026-07-22-ha-dwie-instancje.md`.
|
||||
- **decision vs runbook**: if a decision doc contains a reusable operational
|
||||
recipe (install steps, recovery procedure), that section is a runbook, not
|
||||
part of the decision's rationale. Split it out. Example:
|
||||
`deploy-runner` → `kb/services/job-deploy-runner.md` (how it works) +
|
||||
`kb/decisions/deploy-runner-uzasadnienie.md` (why) +
|
||||
`kb/runbooks/deploy-runner-install.md` (how to install/operate it).
|
||||
- **subsystem vs audit**: a `subsystem` doc is the *maintained* description of
|
||||
how something is designed/intended to work — you keep it in sync. An
|
||||
`audit` is a *frozen* investigation result ("I checked X on this date and
|
||||
found Y") — you don't rewrite it as things change, you write a new audit
|
||||
or a decision/incident instead. This is why `audit` got its own `as_of`
|
||||
field distinct from `updated`.
|
||||
- When in doubt, prefer splitting a doc across two types over stretching one
|
||||
type's frontmatter to cover mixed content — that's the pattern the
|
||||
migration itself followed (see git log `feat(kb): SPLIT ...` commits for
|
||||
worked examples).
|
||||
|
||||
## Visibility default: private
|
||||
|
||||
**`visibility` defaults to `private`.** Every new document must be written
|
||||
`private` unless the operator has explicitly and consciously decided it
|
||||
should be `public` — never infer or default to `public` on your own, even if
|
||||
the content looks harmless. `visibility: public` documents are the only ones
|
||||
`scripts/kb/gen_pages.py` will ever emit to the public kb-site (fail-closed:
|
||||
missing/unparseable/unrecognized `visibility` is treated as private).
|
||||
|
||||
## After every change to kb/**/*.md
|
||||
|
||||
Run the validator and fix anything it flags before considering the edit done:
|
||||
|
||||
```bash
|
||||
python3 scripts/kb/check_okf.py
|
||||
```
|
||||
|
||||
It checks frontmatter parses, `okf` is pinned, `type`/`visibility`/`status`
|
||||
are from their closed lists, dates are well-formed, `as_of`/`superseded_by`
|
||||
are present exactly when required, and every `links`/path-like `contradicts`
|
||||
entry resolves to a real file. A red run means something is broken — fix it,
|
||||
don't skip it.
|
||||
|
||||
## Creating a new document
|
||||
|
||||
Use `scripts/kb/new-doc.sh <type> <slug> [--public]` to scaffold a
|
||||
correctly-placed file with valid frontmatter, then fill in the content.
|
||||
|
||||
## If you just made a public change
|
||||
|
||||
If a file you created or edited carries `visibility: public`, don't forget
|
||||
the KB site itself doesn't update on its own — see the `kb-publish` skill for
|
||||
the one-line reminder to run `scripts/kb/publish.sh`.
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
---
|
||||
name: kb-publish
|
||||
description: At session close, asks the operator whether to publish the KB site if kb/**/*.md changed this session. Trigger whenever the session is wrapping up (save-session, "kończymy sesję", natural end of task) and this session's own edits, or a merge/diff visible in git, touched files under kb/.
|
||||
---
|
||||
|
||||
## What this skill does
|
||||
|
||||
Trigger point: **session close**, not every response. Session close means the
|
||||
operator signals they're wrapping up — e.g. invoking `save-session`, saying
|
||||
something like "kończymy sesję" / "koniec na dziś" / "wrap up", or the task
|
||||
reaching its natural end with no further work queued. Don't act on this skill
|
||||
mid-session just because a `kb/` file was touched.
|
||||
|
||||
At that moment, if this session touched `kb/**/*.md` — either through your
|
||||
own edits or through a merge/diff you observed in git — **ask the operator
|
||||
directly**, as a question requiring a yes/no answer, not a passive reminder:
|
||||
|
||||
> Sesja dotknęła kb/. Zregenerować i zasugerować publikację (`bash scripts/kb/publish.sh`)?
|
||||
|
||||
- If the operator confirms (any affirmative reply): print the ready-to-paste
|
||||
command block so they can copy it straight into their own shell:
|
||||
|
||||
```bash
|
||||
bash scripts/kb/publish.sh
|
||||
```
|
||||
|
||||
Do not run it yourself — see below.
|
||||
- If the operator declines: drop it, nothing more to do.
|
||||
- If the operator doesn't respond or ignores the question (moves on to
|
||||
something else, ends the conversation): do **not** ask again in this
|
||||
session. One ask per session, max.
|
||||
|
||||
## What this skill does NOT do
|
||||
|
||||
**Never run `scripts/kb/publish.sh` yourself**, under any circumstances, even
|
||||
if the operator's task prompt says to deploy, publish, or calibrate. The
|
||||
script SSHes into PIHA and overwrites the live `kb-site` content volume on
|
||||
production — that is out of scope for a worktree agent and requires an
|
||||
explicit, direct instruction from the operator in the current turn.
|
||||
|
||||
If the operator explicitly asks you to run it, that instruction stands on its
|
||||
own — this skill only governs the session-close question, not that request.
|
||||
|
|
@ -93,7 +93,7 @@ services:
|
|||
|
||||
preflight fills `arch`, `ram_mb`, `docker_present`, `mm_runtime` — do NOT guess these.
|
||||
|
||||
Full schema: `kb/runbooks/node-onboarding-tool.md`.
|
||||
Full schema: `scripts/onboard/README.md`.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -22,8 +22,6 @@ venv/
|
|||
*.egg-info/
|
||||
packages/*/build/
|
||||
jobs/*/build/
|
||||
# wyjscie generatorow (scripts/kb/gen_pages.py -> build/kb-site/) — artefakt, nie zrodlo
|
||||
build/
|
||||
|
||||
# Tools
|
||||
.aider*
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"mcpServers": {
|
||||
"ha": {
|
||||
"command": "./services/ha-mcp/run.sh",
|
||||
"args": [],
|
||||
"env": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
CLAUDE.md
22
CLAUDE.md
|
|
@ -40,7 +40,7 @@ Pipeline stages: **prepare → validate → deploy → verify → diagnose (on f
|
|||
## Node Onboarding
|
||||
|
||||
New nodes are onboarded via `scripts/onboard/` — an idempotent bash tool driven by
|
||||
`hosts/<node>/node.yaml` manifests (no Ansible). See `kb/runbooks/node-onboarding-tool.md` for
|
||||
`hosts/<node>/node.yaml` manifests (no Ansible). See `scripts/onboard/README.md` for
|
||||
the full schema, step status table, and gotchas.
|
||||
|
||||
Key fields in `node.yaml`: `ssh_user`, `first_contact` (LAN IP — not `.local`),
|
||||
|
|
@ -90,26 +90,14 @@ The platform uses a multi-agent model with **human-in-the-loop** for destructive
|
|||
Agent → /opt/homelab/actions/pending/<id>.json
|
||||
→ Telegram notification → Operator approves
|
||||
→ /opt/homelab/actions/approved/<id>.json
|
||||
→ Executor dispatches to the target node → completed / failed
|
||||
→ Executor runs → completed / failed
|
||||
```
|
||||
|
||||
The executor never connects to a node (deliberate — see kb/phases/backlog.md
|
||||
"Remediacja floty bez SSH"). It writes a dispatch file that the node collects:
|
||||
|
||||
| Action type | Inbox | Executed on the node by |
|
||||
|---|---|---|
|
||||
| `container_restart` | `actions/dispatch/<node>/` | node-agent (own docker socket) |
|
||||
| `redeploy` | `actions/deploy/<node>/` | deploy-runner, host-level systemd (`jobs/deploy-runner/`) |
|
||||
|
||||
Both report back with an `action_result` event, which the executor turns into
|
||||
`completed` / `failed`. `disk_cleanup` and `alert_only` still resolve inside the
|
||||
executor.
|
||||
|
||||
Agents must never execute destructive actions (restarts, deploys, config changes) without a corresponding approved action file.
|
||||
|
||||
## Event System
|
||||
|
||||
Events are append-only, one JSON file per event, flat under `/opt/homelab/events/<node>/evt-<node>-<unixts>-<type>[-<service>].json`.
|
||||
Events are append-only JSON lines at `/opt/homelab/events/YYYY-MM-DD/<node>/events.jsonl`.
|
||||
|
||||
Emit via `scripts/lib/events.sh` (shell) or `scripts/lib/events.py` (Python).
|
||||
|
||||
|
|
@ -120,8 +108,8 @@ Normalized event types: `deployment_started/completed/failed`, `service_unhealth
|
|||
| Event type | Source | Action generated | Cooldown |
|
||||
|---|---|---|---|
|
||||
| `containers_not_running` | stability-agent | `container_restart` | dedup via stable ID |
|
||||
| `healthcheck_failed` | node-agent | `container_restart` | dedup via stable ID |
|
||||
| `service_unhealthy` / other | stability-agent | `redeploy` → dispatched to the node's deploy-runner (`jobs/deploy-runner/`) | dedup via stable ID |
|
||||
| `mqtt_unreachable` | stability-agent | `container_restart` | dedup via stable ID |
|
||||
| `service_unhealthy` / other | stability-agent | `redeploy` | dedup via stable ID |
|
||||
| `disk_pressure` (high) | stability-agent | `disk_cleanup` | dedup via stable ID |
|
||||
| `ha_websocket_dead` | ha-diag-agent | `container_restart` (homeassistant) | 30 min after completion |
|
||||
| `ha_websocket_recovered` | ha-diag-agent | cancels matching restart | — |
|
||||
|
|
|
|||
24
README.md
24
README.md
|
|
@ -31,29 +31,27 @@ Action approval flow: `pending/` → operator approves → `approved/` → execu
|
|||
|
||||
## Repository Structure
|
||||
|
||||
- `docs/`: [Infrastructure Standards](kb/subsystems/standards.md) and [Deployment Conventions](kb/subsystems/deployment.md).
|
||||
- `kb/phases/subsystem-a-naprawa.md`: [Current Maintenance Plan (Control Plane)](kb/phases/subsystem-a-naprawa.md).
|
||||
- `docs/`: [Infrastructure Standards](docs/standards.md) and [Deployment Conventions](docs/deployment.md).
|
||||
- `hosts/`: Host-specific configurations and service assignments.
|
||||
- `services/`: Reusable Docker Compose service definitions.
|
||||
- `scripts/`: Deployment and management scripts.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. **Standardization**: Follow the [Infrastructure Standards](kb/subsystems/standards.md).
|
||||
2. **Deployment**: See [Deployment Conventions](kb/subsystems/deployment.md) for how to roll out changes.
|
||||
1. **Standardization**: Follow the [Infrastructure Standards](docs/standards.md).
|
||||
2. **Deployment**: See [Deployment Conventions](docs/deployment.md) for how to roll out changes.
|
||||
3. **SATURN**: Remember that SATURN is the only node where commits should be made.
|
||||
|
||||
## Documentation Index
|
||||
|
||||
- [Current Maintenance Plan (Control Plane)](kb/phases/subsystem-a-naprawa.md)
|
||||
- [Infrastructure Standards](kb/subsystems/standards.md)
|
||||
- [Agent Operating Procedures](kb/subsystems/agent-operating-procedures.md) (For AI/Non-Human Agents)
|
||||
- [Deployment Conventions](kb/subsystems/deployment.md)
|
||||
- [Hardware](kb/nodes/legacy-hardware.md)
|
||||
- [Networking](kb/subsystems/networking.md)
|
||||
- [Services](kb/subsystems/legacy-services-list.md)
|
||||
- [Node Capabilities](kb/subsystems/capability-model.md)
|
||||
- [Action Model](kb/subsystems/action-approval-model.md)
|
||||
- [Infrastructure Standards](docs/standards.md)
|
||||
- [Agent Operating Procedures](docs/agents.md) (For AI/Non-Human Agents)
|
||||
- [Deployment Conventions](docs/deployment.md)
|
||||
- [Hardware](docs/hardware.md)
|
||||
- [Networking](docs/networking.md)
|
||||
- [Services](docs/services.md)
|
||||
- [Node Capabilities](docs/capabilities.md)
|
||||
- [Action Model](services/agent-system/action-model.md)
|
||||
|
||||
---
|
||||
*Note: This repository documents the state of the homelab. Runtime state lives outside the repository in `/opt/homelab`.*
|
||||
|
|
|
|||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: deprecated
|
||||
updated: 2026-04-15
|
||||
links: []
|
||||
superseded_by: "kb/subsystems/fleet-inventory.md + hosts/<node>/capabilities.yaml (stub z 2026-04-15, sprzed floty)"
|
||||
---
|
||||
|
||||
# Access
|
||||
|
||||
## Description
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: public
|
||||
status: active
|
||||
updated: 2026-05-20
|
||||
links: []
|
||||
---
|
||||
|
||||
# Agent Operating Procedures
|
||||
|
||||
This document defines the operating procedures, constraints, and interaction protocols for non-human agents (AI agents, autonomous scripts) within the Homelab Codex ecosystem.
|
||||
|
|
@ -15,9 +6,9 @@ This document defines the operating procedures, constraints, and interaction pro
|
|||
|
||||
1. **Read-Only by Default**: Agents should assume read-only access to the `/opt/homelab` runtime unless explicitly executing an approved action.
|
||||
2. **Git as Authority**: The repository on **SATURN** is the source of truth. Agents must not modify the runtime state on nodes directly without corresponding (or pending) Git state, unless it's an emergency mitigation.
|
||||
3. **Human-in-the-Loop (HIL)**: All destructive or structural changes (restarts, deployments, config changes) must follow the [Action Approval Model](action-approval-model.md).
|
||||
3. **Human-in-the-Loop (HIL)**: All destructive or structural changes (restarts, deployments, config changes) must follow the [Action Approval Model](../services/agent-system/action-model.md).
|
||||
4. **Idempotency**: All scripts and actions proposed or executed by agents MUST be idempotent.
|
||||
5. **Context-Awareness**: Agents MUST read the `README.md` and `kb/subsystems/agent-operating-procedures.md` at the start of every session to align with current infrastructure standards.
|
||||
5. **Context-Awareness**: Agents MUST read the `README.md` and `docs/agents.md` at the start of every session to align with current infrastructure standards.
|
||||
|
||||
## 2. Agent Roles
|
||||
|
||||
1209
docs/backlog.md
Normal file
1209
docs/backlog.md
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: public
|
||||
status: active
|
||||
updated: 2026-05-20
|
||||
links: []
|
||||
---
|
||||
|
||||
# Node Capability Model
|
||||
|
||||
This document defines the capability model for the homelab infrastructure. The goal is to provide a declarative way to describe what each node can do, its constraints, and its suitability for various workloads.
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: node
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-05-27
|
||||
links:
|
||||
- ../runbooks/chelsty-deploy-recovery.md
|
||||
---
|
||||
|
||||
# CHELSTY Runtime
|
||||
|
||||
This document describes the runtime environment and deployment flow for CHELSTY, an offline-capable home automation edge node split across two VMs.
|
||||
|
|
@ -110,6 +100,48 @@ services:
|
|||
|
||||
Remove `monitor: false` once node-agent is bootstrapped on this VM.
|
||||
|
||||
## Deployment Flow
|
||||
|
||||
### Initial Bootstrap
|
||||
```bash
|
||||
./scripts/bootstrap/chelsty-runtime.sh
|
||||
```
|
||||
|
||||
### Deploy services
|
||||
```bash
|
||||
./scripts/deploy/deploy-node.sh chelsty-infra
|
||||
./scripts/deploy/deploy-node.sh chelsty-ha
|
||||
```
|
||||
|
||||
### Manual (SSH) — chelsty-infra uses docker-compose v1
|
||||
```bash
|
||||
ssh oskar@100.122.201.22
|
||||
cd ~/homelab-codex-ws/services/<service>
|
||||
docker-compose -f docker-compose.yml \
|
||||
-f ../../hosts/chelsty-infra/runtime/<service>/docker-compose.override.yml \
|
||||
up -d --build --force-recreate
|
||||
```
|
||||
|
||||
> **Note:** `docker compose` (v2) is **not** available on chelsty-infra — always use `docker-compose` (hyphenated, v1 1.29.2).
|
||||
|
||||
## Recovery Procedures
|
||||
|
||||
### Mosquitto stopped
|
||||
```bash
|
||||
ssh oskar@100.122.201.22 "docker start mosquitto"
|
||||
# Ensure restart policy is correct:
|
||||
docker update --restart unless-stopped mosquitto
|
||||
```
|
||||
|
||||
### Zigbee2MQTT won't start
|
||||
1. Check logs: `docker logs zigbee2mqtt --tail 50`
|
||||
2. Verify SLZB-06U reachable from host: `nc -zv 192.168.1.105 6638`
|
||||
3. Verify config is not empty: `cat /opt/homelab/data/zigbee2mqtt/data/configuration.yaml`
|
||||
4. If config missing, recreate from the minimal template above
|
||||
|
||||
### SLZB-06U unreachable
|
||||
`192.168.1.105:6638` EHOSTUNREACH means the coordinator is offline or the LAN is down. Zigbee2MQTT will keep retrying — no restart needed once the coordinator returns.
|
||||
|
||||
## Critical Backup Sets
|
||||
|
||||
| Data | Path |
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: service
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-05-20
|
||||
links: []
|
||||
---
|
||||
|
||||
### CHELSTY Stability Agent
|
||||
|
||||
The stability-agent on CHELSTY provides local observability and health monitoring for the node's services and infrastructure.
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: deprecated
|
||||
updated: 2026-04-15
|
||||
links: []
|
||||
superseded_by: "kb/subsystems/fleet-inventory.md + hosts/<node>/capabilities.yaml (stub z 2026-04-15, sprzed floty)"
|
||||
---
|
||||
|
||||
# Core Stack
|
||||
|
||||
## Description
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-25
|
||||
links:
|
||||
- ../incidents/deploy-sh-vps-niszczy-control-plane.md
|
||||
---
|
||||
|
||||
# Deployment Conventions
|
||||
|
||||
This document describes the GitOps-lite deployment process for the homelab.
|
||||
|
|
@ -20,6 +10,21 @@ This document describes the GitOps-lite deployment process for the homelab.
|
|||
4. **Tailscale Mesh**: All hosts are connected via Tailscale, allowing secure communication without public port exposure.
|
||||
5. **Host Autonomy**: Services that must operate during WAN or Git outages keep their runtime dependencies on the execution node or local LAN.
|
||||
|
||||
## ⚠️ ZNANY BUG — `deploy.sh vps` niszczy control-plane (2026-06-25)
|
||||
|
||||
`deploy.sh vps` uruchamia `deploy-node.sh` w pętli po wszystkich serwisach VPS, w tym
|
||||
`control-plane`. Pętla używa innego `COMPOSE_PROJECT_NAME` niż `deploy-local.sh`
|
||||
(który uruchamiany jest z `cwd=services/control-plane`). Niezgodność project-name powoduje
|
||||
`Recreate` → `No such container` → `set -e` przerywa pętlę → **observer, supervisor,
|
||||
executor i operator-ui znikają z VPS.**
|
||||
|
||||
**Dopóki bug nie zostanie naprawiony (backlog — Krytyczny):**
|
||||
- Do deployu control-plane używać: `ssh -t vps 'cd ~/homelab-codex-ws && cd services/control-plane && bash deploy-local.sh'`
|
||||
- Inne serwisy VPS deployować punktowo: `deploy-node.sh` z `--service <name>` lub przez SSH + `docker compose up -d`
|
||||
- **NIE uruchamiać `deploy.sh vps` bez pełnej świadomości ryzyka.**
|
||||
|
||||
---
|
||||
|
||||
## Staged Deployment Framework
|
||||
|
||||
The homelab uses a modularized staged deployment framework located at `scripts/deploy/deploy.sh`. This script is designed to be resumable, stage-aware, and observable, with core logic split into maintainable libraries in `scripts/lib/`.
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: public
|
||||
status: active
|
||||
updated: 2026-05-12
|
||||
links: []
|
||||
---
|
||||
|
||||
# Homelab Event System
|
||||
|
||||
The homelab multi-agent platform uses a filesystem-first event architecture for observability, auditability, and agent reasoning.
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: node
|
||||
visibility: private
|
||||
status: deprecated
|
||||
updated: 2026-04-15
|
||||
links: []
|
||||
superseded_by: "hosts/<node>/capabilities.yaml + kb/subsystems/fleet-inventory.md"
|
||||
---
|
||||
|
||||
# Hardware
|
||||
|
||||
## Description
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: node
|
||||
visibility: private
|
||||
status: deprecated
|
||||
updated: 2026-04-15
|
||||
links: []
|
||||
superseded_by: "kb/nodes/vps.md + kb/subsystems/fleet-inventory.md (stub z 2026-04-15, sprzed floty)"
|
||||
---
|
||||
|
||||
# Hetzner VPS
|
||||
|
||||
## Description
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-03
|
||||
links: []
|
||||
---
|
||||
|
||||
# Inwentaryzacja floty homelab-codex — 2026-06-30
|
||||
|
||||
Zebrano: 2026-06-30 17:09 CEST
|
||||
|
|
@ -1,17 +1,8 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-02
|
||||
links: []
|
||||
---
|
||||
|
||||
# Weryfikacja inwentaryzacji floty 2026-06-30 — stan na 2026-07-02
|
||||
|
||||
Zebrano: 2026-07-02 ~15:20 CEST (read-only recon, zero zmian na nodach).
|
||||
Metoda: ssh + `docker ps -a / inspect / logs`, `free/df/nproc/lscpu/lsblk`, `git branch/log` (odczyt),
|
||||
`curl` do fleet-prometheus API. Porównanie z `kb/subsystems/fleet-inventory.md` (23 rozjazdy)
|
||||
`curl` do fleet-prometheus API. Porównanie z `docs/infra/inventory-2026-06-30.md` (23 rozjazdy)
|
||||
oraz z repo na `master` (HEAD `22adfb1`).
|
||||
|
||||
Dostępność nodów podczas weryfikacji:
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: audit
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-16
|
||||
as_of: 2026-07-16
|
||||
links: []
|
||||
---
|
||||
|
||||
# Recon: lustro `event=dead prom=up` — 1507 mismatchy w shadow-liveness.log (2026-07-16)
|
||||
|
||||
READ-ONLY recon. Zero zmian w kodzie/serwisach. Wszystkie czasy **UTC**
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: audit
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-14
|
||||
as_of: 2026-07-14
|
||||
links: []
|
||||
---
|
||||
|
||||
# Monitoring coverage — co biega vs co jest monitorowane (recon 2026-07-14)
|
||||
|
||||
**Pytanie:** czy wszystkie serwisy floty są monitorowane?
|
||||
|
|
@ -131,7 +121,7 @@ compose-service kontenera.
|
|||
| owntracks-prometheus-exporter-prometheus-owntracks-exporter-1 | linusgroh/prometheus-owntracks-exporter | Up 2w | 0.0.0.0:8780→80 |
|
||||
| own-tracks-frontend-owntracks-frontend-1 | owntracks/frontend | Up 2w | 0.0.0.0:8084→80 |
|
||||
|
||||
Zmiany vs audyt 2026-06-30 (`kb/subsystems/fleet-inventory.md`): **przybyły** paperless,
|
||||
Zmiany vs audyt 2026-06-30 (`docs/infra/inventory-2026-06-30.md`): **przybyły** paperless,
|
||||
paperless-db, paperless-broker (Deploy 1, 2026-07-10); **zniknęły** diskover i elasticsearch
|
||||
(w audycie 06-30 były w 33 shadow; dziś nie biegają). 06-30: 40 kontenerów → dziś: 42.
|
||||
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-07
|
||||
links: []
|
||||
---
|
||||
|
||||
# Migracja okit.pl: 42.pl (FreeDNS) -> Cloudflare — plan faz
|
||||
|
||||
Cel: okit.pl na Cloudflare (jak kapala.org) -> wildcard *.okit.pl DNS-01 ->
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: runbook
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-16
|
||||
links: []
|
||||
---
|
||||
|
||||
# Ollama SOLARIA: manual → declarative cutover runbook
|
||||
|
||||
Date: 2026-07-15
|
||||
|
|
@ -97,7 +88,7 @@ bind-mount the existing model directory.**
|
|||
docker exec ollama ollama ps
|
||||
```
|
||||
3. Embeddings endpoint + vector dimension (deferred check from
|
||||
`kb/phases/kb-m5-faza2.md` §6 step 2):
|
||||
`docs/kb/modules/05-faza2-plan.md` §6 step 2):
|
||||
```bash
|
||||
curl -s http://localhost:11434/api/embeddings -d '{"model":"bge-m3","prompt":"test"}' \
|
||||
| python3 -c "import json,sys; v=json.load(sys.stdin)['embedding']; print(len(v))"
|
||||
|
|
@ -145,7 +136,7 @@ SOLARIA:
|
|||
- Given the missing driver, the cutover proceeded **in CPU-only mode**: the
|
||||
`deploy.resources` GPU reservation was commented out in
|
||||
`services/ollama/docker-compose.yml` (commit `f57a01a`), and the driver fix
|
||||
was filed as a backlog item (see `kb/phases/backlog.md`) blocking the module 5
|
||||
was filed as a backlog item (see `docs/backlog.md`) blocking the module 5
|
||||
mail-embedding phase.
|
||||
- **2026-07-16: driver fixed.** Installed `nvidia-driver-595-open` from the
|
||||
distro repository — not the old `ppa:graphics-drivers/ppa` (jammy), which
|
||||
|
|
@ -1,16 +1,6 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: audit
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-02
|
||||
as_of: 2026-07-02
|
||||
links: []
|
||||
---
|
||||
|
||||
# Audyt odchudzania PIHA — 2026-07-02
|
||||
|
||||
> Faza 1 (READ-ONLY) modulu 0 filaru dokumentow (`kb/phases/kb-m0-piha-slim.md`).
|
||||
> Faza 1 (READ-ONLY) modulu 0 filaru dokumentow (`docs/kb/modules/00-piha-slim.md`).
|
||||
> Zadna akcja nie zostala wykonana — wylacznie `docker stats/inspect/logs`, `ss`, `curl` (odczyt).
|
||||
> Stan w momencie audytu: **RAM 7.9Gi total, 5.0Gi used, 2.9Gi available; swap 4Gi total, 2.0Gi uzyty.**
|
||||
> 41 kontenerow Up (inwentaryzacja 2026-06-30 liczyla 40; wszystkie nadal biega).
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: audit
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-06
|
||||
as_of: 2026-07-06
|
||||
links: []
|
||||
---
|
||||
|
||||
# Prometheus liveness cutover — recon starego toru (2026-07-06)
|
||||
|
||||
Read-only recon przed cutoverem liveności floty na Prometheus `up{}`. Mapa obecnego
|
||||
|
|
@ -300,7 +290,7 @@ renderuje `health`/`status`/`last_seen`), `/unhealthy` (`:302-334`), `/summary`,
|
|||
chelsty/chelsty-infra (exporter DOWN po LTE, 2026-06-26).
|
||||
- Reguła `NodeDown` (`rules/liveness.yml:21-28`): `up{node=~"vps|piha"} == 0`,
|
||||
`for: 5m`, severity critical. solaria/lustro świadomie wykluczone (`:12-16` —
|
||||
planned power-off; docelowo anomaly detection, `kb/phases/backlog.md:390-406`).
|
||||
planned power-off; docelowo anomaly detection, `docs/backlog.md:390-406`).
|
||||
Bez Alertmanagera by design (`:3-7`) — delivery = brain-watchdog poll `/api/v1/alerts`.
|
||||
- node_exporter w repo tylko dla VPS (`services/node_exporter/`, network_mode: host,
|
||||
`hosts/vps/services.yaml:36-43`). Exportery na piha/solaria/lustro **nie mają
|
||||
|
|
@ -348,7 +338,7 @@ chelsty-infra, chelsty-ha, lustro.
|
|||
| lustro | TAK (`:50-52`) | NIE | TAK (bez stability-agenta) | jw. |
|
||||
| **chelsty-infra** | **NIE** (`:57-60`, exporter DOWN po LTE) | NIE | **TAK** (remote TTL 900/3600, `liveness.py:43-50`) | **tylko stary tor — cutover totalny zostawiłby go bez liveności** |
|
||||
| chelsty-ha | NIE | NIE | NIE (`hosts/chelsty-ha/services.yaml:6-12`, `monitor: false`) | już dziś bez liveności (pośrednio przez MQTT chelsty-infra) — cutover nic nie zmienia |
|
||||
| saturn | NIE (`:55`, laptop) | NIE | NIE (brak `hosts/saturn/services.yaml`, `kb/phases/backlog.md:423`) | już dziś bez liveności — cutover nic nie zmienia |
|
||||
| saturn | NIE (`:55`, laptop) | NIE | NIE (brak `hosts/saturn/services.yaml`, `docs/backlog.md:423`) | już dziś bez liveności — cutover nic nie zmienia |
|
||||
|
||||
**Chelsty offline ~34 dni — jak traktuje go stara rura:** eventy buforują się lokalnie
|
||||
(rsync fail = non-fatal, `node_agent.py:560-566`), `last_seen` na VPS zamrożone sprzed
|
||||
|
|
@ -361,7 +351,7 @@ totalnym chelsty-infra nie miałby żadnej liveności i żadnego przejścia offl
|
|||
Dodatkowo docs sygnalizują konflikt IP w komentarzach `prometheus.yml:57` vs
|
||||
`hosts/chelsty-infra/host.yaml:12` — do wyjaśnienia przy ewentualnym dodawaniu scrape.
|
||||
*(rzeczywisty bieżący stan chelsty — do weryfikacji na żywo; ostatni zapis:
|
||||
UNREACHABLE, `kb/subsystems/fleet-inventory-verify.md:17,151`)*
|
||||
UNREACHABLE, `docs/infra/inventory-verify-2026-07-02.md:17,151`)*
|
||||
|
||||
**Wniosek twardy:** cutover NIE może być globalny. Docelowa architektura to
|
||||
**hybryda per-node**: `up{}` dla scrape'owanych (vps, piha, solaria, lustro),
|
||||
|
|
@ -460,7 +450,7 @@ z `last_seen` — rzadszy heartbeat przy niezmienionych TTL-ach = fałszywe degr
|
|||
- chelsty-infra: zbadać exporter-over-LTE (`prometheus.yml:57-60` + konflikt IP
|
||||
z `hosts/chelsty-infra/host.yaml:12`); do tego czasu zostaje na torze eventowym.
|
||||
- NodeDown dla solaria/lustro: świadomie odroczone do anomaly detection
|
||||
(`kb/phases/backlog.md:390-406`) — nie wciągać do cutoveru.
|
||||
(`docs/backlog.md:390-406`) — nie wciągać do cutoveru.
|
||||
- Watchdog na sam Prometheus (D.2 pkt 5) — mały task przy etapie 3.
|
||||
- saturn / chelsty-ha: świadomie poza monitoringiem — status quo.
|
||||
|
||||
|
|
@ -468,7 +458,7 @@ z `last_seen` — rzadszy heartbeat przy niezmienionych TTL-ach = fałszywe degr
|
|||
|
||||
**Seria.** Minimalnie trzy taski implementacyjne + weryfikacje między nimi:
|
||||
(1) etap 1 shadow-read; (2) etap 3 flaga per-node (po tygodniu etapu 2);
|
||||
(3) watchdog-na-Prometheusa + aktualizacja `kb/subsystems/observer.md`.
|
||||
(3) watchdog-na-Prometheusa + aktualizacja `docs/observer-runtime.md`.
|
||||
Etap 0 to czynność operatorska (runtime, nie repo). Etap 5 to niezależny backlog.
|
||||
|
||||
---
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-15
|
||||
links: []
|
||||
---
|
||||
|
||||
# Prometheus cutover — Etap 2: analiza zgodności shadow-run (2026-07-15)
|
||||
|
||||
Analiza READ-ONLY logów `SHADOW_LIVENESS_MISMATCH` observera (parallel-run od
|
||||
|
|
@ -164,7 +155,7 @@ Skutek uboczny do zaakceptowania świadomie: syntetyczne `node_stale`/
|
|||
wyłączeniu **wcześniej, ale nie liczniej** — te eventy już dziś powstają co noc
|
||||
(21:32/21:39 dla lustro, każdorazowo dla solaria). Cutover nie zwiększa wolumenu
|
||||
alertów. Docelowe wyciszenie planowych okien off to wątek anomaly-detection
|
||||
z backlogu (`kb/phases/backlog.md:390-406`) — **niezależny od cutoveru i nieblokujący**;
|
||||
z backlogu (`docs/backlog.md:390-406`) — **niezależny od cutoveru i nieblokujący**;
|
||||
`NodeDown` dla solaria/lustro słusznie pozostaje wyłączony.
|
||||
|
||||
### Rekomendowany mapping (potwierdzenie rekomendacji z recon F/Etap 2)
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: audit
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-27
|
||||
as_of: 2026-07-27
|
||||
links: []
|
||||
---
|
||||
|
||||
# Audyt niezarządzanych stacków na VPS — 2026-07-27
|
||||
|
||||
Recon read-only przed konsolidacją do GitOps. Zebrane przez `ssh vps` (user `oskar`,
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: service
|
||||
visibility: private
|
||||
status: deprecated
|
||||
updated: 2026-04-15
|
||||
links: []
|
||||
superseded_by: "brak katalogu services/joplin/ w repo — patrz contradicts w kb/subsystems/repo-operating-contract.md"
|
||||
---
|
||||
|
||||
# Joplin Server
|
||||
|
||||
## Description
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-16
|
||||
links: []
|
||||
---
|
||||
|
||||
# Eval-set: pilot retrieval (faza 2, krok 7) — 2026-07-16
|
||||
|
||||
Stan bazy: document_chunk = 2683 chunki (bge-m3, dim 1024), 160 dokumentów z 186 kopert paperless.
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-26
|
||||
links:
|
||||
- ../decisions/kb-log-decyzji.md
|
||||
---
|
||||
|
||||
# Baza wiedzy — przegląd i log decyzji (homelab-codex · KB)
|
||||
|
||||
> Master-dokument inicjatywy. Stoi ponad dokumentami per-projekt (`kb-01-email-design.md`, …).
|
||||
|
|
@ -92,6 +82,21 @@ Dwa dolne tiery są per-filar i neutralne. Dwa górne są wspólne dla wszystkic
|
|||
|
||||
---
|
||||
|
||||
## Decyzje — zamknięte vs otwarte
|
||||
|
||||
**Zamknięte:**
|
||||
- Spine: Postgres + pgvector (nie Qdrant).
|
||||
- Embed: **bge-m3** (multilingual, długi kontekst — pod polski lepszy niż multilingual-e5).
|
||||
- Załączniki: indeksowane w **II turze** (MVP najpierw czysty tekst).
|
||||
- Warstwa 3 startuje jako **cienki graf encji**; federacja przy zapytaniu dochodzi później (docelowo hybryda).
|
||||
- Dokumenty: Nextcloud + Paperless-ngx.
|
||||
|
||||
**Otwarte:**
|
||||
- **Transakcje:** agregator vs CSV, pokrycie mBanku, Revolut, koszt (filar #4).
|
||||
- **Maile §design:** sizing archiwum / node (ile waży Gmail), unifikacja adaptera (jeden IMAP dla obu vs JMAP+IMAP osobno).
|
||||
|
||||
---
|
||||
|
||||
## Tor równoległy (nie tutaj)
|
||||
|
||||
Hardening homelabu / stabilizacja control-plane — osobny wątek.
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-06
|
||||
links: []
|
||||
---
|
||||
|
||||
# Filar maili — projekt (homelab-codex · KB · projekt #1)
|
||||
|
||||
> Pierwszy filar. Wzorzec referencyjny dla pozostałych (archiwum, embeddingi na SOLARIA, szkielet agenta, deploy).
|
||||
|
|
@ -27,22 +18,9 @@ Realna potrzeba to najpierw **archiwum**, nie „RAG nad mailami". Dwie warstwy,
|
|||
|
||||
Gmail **nie jest porzucany** — zostaje jako konto śmieciowe / loginy / 2FA. Stąd maile mają dwa żywe wejścia:
|
||||
|
||||
- **Fastmail** — `source: fastmail`, adapter **IMAP** (hasło aplikacji). Primary: tu ląduje sensowna poczta na przyszłość.
|
||||
- **Fastmail** — `source: fastmail`, adapter **JMAP** (read-only token). Primary: tu ląduje sensowna poczta na przyszłość.
|
||||
- **Gmail** — `source: gmail`, adapter **IMAP** (protokół, nie Gmail API → przenośność). Ciągły sync żywej skrzynki.
|
||||
|
||||
> **Korekta 2026-08-06 — Fastmail przez IMAP, nie JMAP.** Do tej daty ten dokument (i §7, §9
|
||||
> oraz §10 planu fazy mailowej) przewidywał dla Fastmaila **JMAP** i osobny `jobs/fastmail-poller`.
|
||||
> Zapis historyczny: *„Fastmail — adapter JMAP (read-only token)"*, 2026-06-24.
|
||||
>
|
||||
> Decyzja z 2026-08-06 (recon `kb/audits/mail-sync-2026-08-06.md` Decyzja (b), zatwierdzona
|
||||
> przez operatora) domyka otwartą od czerwca decyzję „unifikacja adaptera" z §9 na rzecz
|
||||
> **jednego wspólnego IMAP-a dla obu kont**, w jednym jobie `jobs/mail-imap-sync`. Powody:
|
||||
> JMAP synchronizuje po `state` — elegancko i niepotrzebnie przy jednym ticku na godzinę
|
||||
> i ~37 mailach na dobę, skoro UIDVALIDITY/UIDNEXT rozwiązuje ten sam problem i tak trzeba go
|
||||
> zaimplementować dla Gmaila; jeden adapter to jeden zestaw testów, jedna klasa błędów i jedna
|
||||
> ścieżka hardeningu 8-bitowych nagłówków. JMAP nie jest zamknięty na zawsze — koperta
|
||||
> i archiwum są protokołowo obojętne, więc wymiana transportu nie dotyka danych.
|
||||
|
||||
Plus jednorazowy **bulk historyczny Gmaila** (eksport „All Mail" / Takeout → surowy dump do archiwum). Operacja odwracalna i niezależna od reszty pipeline'u — robimy pierwsza. Urgency spadła (konto żyje), ale historia warta zassania od razu.
|
||||
|
||||
---
|
||||
|
|
@ -94,9 +72,7 @@ Załączniki: **II tura** (MVP = czysty tekst + nagłówki).
|
|||
## 7. Deploy
|
||||
|
||||
- Wszystko w `homelab-codex`, przez Git na SATURN, konwencja override `hosts/<node>/runtime/<svc>/`.
|
||||
- Usługi: `mail-imap-sync` (Fastmail + Gmail, jeden job — korekta 2026-08-06; wcześniej
|
||||
planowane jako osobne `jmap-poller` + `imap-poller`), `indexer`, embed (ollama na SOLARIA),
|
||||
`postgres+pgvector`, `mail-agent`; bulk importer jako one-shot job.
|
||||
- Usługi: `jmap-poller` (Fastmail), `imap-poller` (Gmail), `indexer`, embed (ollama na SOLARIA), `postgres+pgvector`, `mail-agent`; bulk importer jako one-shot job.
|
||||
- Deploy skryptem czytającym `inventory/topology.yaml`.
|
||||
|
||||
---
|
||||
|
|
@ -105,10 +81,8 @@ Załączniki: **II tura** (MVP = czysty tekst + nagłówki).
|
|||
|
||||
1. ✅ Zamroź kopertę + postaw Postgres+pgvector. *(2026-06-17)*
|
||||
2. ✅ **Bulk Gmail historyczny → archiwum** — `jobs/gmail-bulk-import/` — **KOD GOTOWY** *(2026-06-24)*; nie uruchomiony (Takeout ~27 GB na SOLARIA, do transferu na PIHA).
|
||||
3. ~~Fastmail JMAP live ingest~~ → **Fastmail IMAP live sync** → archiwum. *(korekta
|
||||
2026-08-06; kod gotowy, pierwszy żywy run po stronie operatora —
|
||||
`kb/runbooks/mail-sync-run.md`)*
|
||||
4. Gmail IMAP live sync → archiwum. *(j.w. — ten sam job `jobs/mail-imap-sync`)*
|
||||
3. Fastmail JMAP live ingest → archiwum.
|
||||
4. Gmail IMAP live sync → archiwum.
|
||||
5. Filtr archiwum→indeks.
|
||||
6. Indexer (parse → chunk → embed bge-m3) → pgvector.
|
||||
7. Cienki agent maili + tool dla warstwy 4.
|
||||
|
|
@ -117,12 +91,8 @@ Załączniki: **II tura** (MVP = czysty tekst + nagłówki).
|
|||
|
||||
## 9. Decyzje otwarte (do przyklepania przed/w trakcie startu)
|
||||
|
||||
- ✅ **Sizing Gmaila** — ZAMKNIĘTE: 225 030 kopert, archiwum ~27 GB na PIHA (Etap B, 2026-08-06).
|
||||
- ✅ **Unifikacja adaptera** — ZAMKNIĘTE 2026-08-06 na rzecz **jednego IMAP-a** dla obu kont
|
||||
(Decyzja (b) reconu, uzasadnienie w §2 wyżej).
|
||||
- **Sizing Fastmaila** — OTWARTE, i celowo: przesądza o tym, czy ciągniemy historię konta czy
|
||||
tylko przyrost. Rozstrzyga pomiar `mail-imap-sync --measure`, nie zgadywanie —
|
||||
`kb/runbooks/mail-sync-run.md` §5.
|
||||
- **Sizing Gmaila** — ile realnie waży „All Mail"? (przesądza node/dysk archiwum).
|
||||
- **Unifikacja adaptera** — jeden wspólny IMAP dla Fastmail + Gmail (mniej kodu) vs JMAP dla Fastmail + IMAP dla Gmail (JMAP bogatszy)?
|
||||
- **Reguły filtra** — startowa lista blacklist domen/nagłówków.
|
||||
- Vector store: pgvector **przyklepane** (spine).
|
||||
- Embed model: bge-m3 **przyklepane**.
|
||||
|
|
@ -1,18 +1,9 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-01
|
||||
links: []
|
||||
---
|
||||
|
||||
# KB filar #2 — Dokumenty (Nextcloud + Paperless) — design
|
||||
|
||||
> Dokument-master filaru dokumentow. Stoi pod `kb-00-overview.md`.
|
||||
> Cel: kazda sesja / Claude Code startuje z pelnym kontekstem decyzji.
|
||||
> Status: ARCHITEKTURA ZAMKNIETA (2026-07-01), implementacja modulowa czeka.
|
||||
> Moduly implementacyjne: `kb/phases/kb-m*.md` — puszczane CC jeden po drugim.
|
||||
> Moduly implementacyjne: `docs/kb/modules/0X-*.md` — puszczane CC jeden po drugim.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-02
|
||||
links: []
|
||||
---
|
||||
|
||||
# Modul 0 — Odchudzic PIHA (prerekwizyt filaru dokumentow)
|
||||
|
||||
> Prerekwizyt modulow 2/4 (Paperless/Nextcloud na PIHA). Bez tego PIHA nie ma
|
||||
|
|
@ -15,7 +6,7 @@ links: []
|
|||
## STATUS: prerekwizyt RAM SPELNIONY (2026-07-02)
|
||||
|
||||
Faza 1 (audyt read-only) + faza 2 (egzekucja po review Oskara) wykonane —
|
||||
szczegoly: `kb/audits/piha-slim-2026-07-02.md` (sekcja "Korekta po review
|
||||
szczegoly: `docs/infra/piha-slim-audit-2026-07-02.md` (sekcja "Korekta po review
|
||||
+ egzekucja").
|
||||
|
||||
- **Kryterium >= 1.5Gi available: SPELNIONE.** Przed egzekucja: 2.8Gi available
|
||||
|
|
@ -37,7 +28,7 @@ Zwolnic RAM na PIHA (dzis: 3.1Gi available, swap 2G uzyty) tak, by lekki Paperle
|
|||
serwis wszedl z zapasem, nie na styku swap.
|
||||
|
||||
## Wymogi
|
||||
- Audyt 33 shadow-kontenerow (lista w `kb/subsystems/fleet-inventory.md`).
|
||||
- Audyt 33 shadow-kontenerow (lista w `docs/infra/inventory-2026-06-30.md`).
|
||||
- Zidentyfikowac kandydatow do usuniecia/przeniesienia/wylaczenia:
|
||||
- **elasticsearch 1Gi** — kto tego uzywa? (wikijs? diskover?) — jesli martwy, ubic
|
||||
- **diskover** — jednorazowy indekser? czy chodzi ciagle bez potrzeby?
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: decision
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-01
|
||||
links: []
|
||||
---
|
||||
|
||||
# Modul 1 — SSO Forgejo-OIDC (decyzja + wzorzec wpiecia)
|
||||
|
||||
> Fundament tozsamosci dla filaru dokumentow (i szerzej homelaba). Zapisuje decyzje
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-01
|
||||
links: []
|
||||
---
|
||||
|
||||
# Modul 2 — Paperless-ngx serwis (na PIHA)
|
||||
|
||||
> Serwis dokumentow: UI+API+Postgres+Redis. Always-on na PIHA. OCR-worker OSOBNO
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-01
|
||||
links: []
|
||||
---
|
||||
|
||||
# Modul 3 — Paperless OCR-worker (SOLARIA + fallback PIHA)
|
||||
|
||||
> Ciezki OCR odseparowany od serwisu. Worker na SOLARIA (moc), fallback PIHA (wolno).
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-02
|
||||
links: []
|
||||
---
|
||||
|
||||
# Modul 4 — Nextcloud (drive/WebDAV + OIDC)
|
||||
|
||||
> Drugi adapter dokumentow: zamiennik Google Drive, dowolne pliki + sync. Zrodlo
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-02
|
||||
links: []
|
||||
---
|
||||
|
||||
# Modul 5 — Ingest dokumentow -> koperta KB
|
||||
|
||||
> Adapter obu zrodel (Paperless API + Nextcloud WebDAV) -> koperta KB. Domyka filar #2
|
||||
|
|
@ -1,32 +1,16 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-06
|
||||
links: []
|
||||
---
|
||||
|
||||
# Moduł 5, faza mailowa — treść maili w retrievalu (RECON + PLAN)
|
||||
|
||||
> Status (2026-08-06): Kroki 0-5 WYKONANE na żywo (chunker wydzielony, hybrid
|
||||
> retrieval, Etap A apply na żywej bazie, bramka jakościowa **PASS** — patrz §8).
|
||||
> **Etap B (Krok 6) ZAMKNIĘTY 2026-08-06**: pełny korpus gmail jest zchunkowany
|
||||
> i zembedowany (389 012 chunków, zero nie-excluded bez wektora) — patrz §9
|
||||
> „Wynik Etapu B". **Krok 7 (przyrostówka IMAP) — IN PROGRESS 2026-08-06**:
|
||||
> recon `kb/audits/mail-sync-2026-08-06.md` wykonany, decyzje operatora (a)-(g)
|
||||
> **zatwierdzone w całości 2026-08-06**, implementacja w repo (§10 „Zakres
|
||||
> wdrożony"). **Kod nie łączył się z żywym kontem** — pierwszy sync, pomiar
|
||||
> Fastmaila i aktywacja timera to kroki operatora wg
|
||||
> `kb/runbooks/mail-sync-run.md`.
|
||||
> Status (2026-07-23): Kroki 0-4 WYKONANE na żywo (chunker wydzielony, hybrid
|
||||
> retrieval, Etap A apply na żywej bazie), Krok 5 (bramka jakościowa) **PASS**
|
||||
> — patrz §8 dla liczb i werdyktu. Etap B (pełne archiwum) i Krok 7 (recon
|
||||
> IMAP/JMAP) wciąż przed nami.
|
||||
>
|
||||
> Kontynuacja `05-faza4-plan.md` (faza 4: `packages/kb-retrieval` wydzielone,
|
||||
> serwis `kb-query` z UI działa na PIHA — „KB po raz pierwszy odpowiada przez
|
||||
> HTTP", 2026-07-22, `docs/sessions/2026-07-22.md`). Faza mailowa = odpowiedź na
|
||||
> feedback operatora z POC wyszukiwarki: **„mało danych, brak połączeń"**.
|
||||
> W punkcie wyjścia (2026-07-22) 225 030 kopert gmail miało w bazie tylko
|
||||
> nagłówki — treści leżały wyłącznie w archiwum .eml na PIHA (stan zamknięty
|
||||
> Etapem B, §9). Ta faza wprowadza treści maili do `document_chunk`
|
||||
> 225 030 kopert gmail ma dziś w bazie tylko nagłówki — treści leżą wyłącznie
|
||||
> w archiwum .eml na PIHA. Ta faza wprowadza treści maili do `document_chunk`
|
||||
> i udostępnia je w retrievalu. **To nadal wyszukiwarka, nie chat** — synteza,
|
||||
> Drive Takeout i backfill 70k załączników PDF pozostają poza zakresem (§11).
|
||||
|
||||
|
|
@ -468,8 +452,7 @@ i idempotencji (drugi przebieg = zero insertów). Definition of Done z CLAUDE.md
|
|||
`hybrid_query` analogicznie do `cascade_query` (jeden embed zapytania).
|
||||
- `kb-query`: `mode` pattern `^(cascade|flat|hybrid)$`; **domyślny `mode`
|
||||
przełączany na `hybrid` dopiero po PASS bramki (§8)** — do tego czasu
|
||||
hybrid dostępny jawnie. *(Wykonane 2026-08-06: default = `hybrid`, patrz
|
||||
DoD (d) w §12.)* Wyniki gmail w UI już obsłużone (faza 4: subject/from
|
||||
hybrid dostępny jawnie. Wyniki gmail w UI już obsłużone (faza 4: subject/from
|
||||
z headers + „Kopiuj Message-ID").
|
||||
- Testy jednostkowe na mockach (merge, pusta gałąź summary, pusta gałąź mail).
|
||||
|
||||
|
|
@ -584,8 +567,7 @@ płonił bramki co uruchomienie).
|
|||
|
||||
**`kb-query` domyślny `mode`**: przełączenie na `hybrid` jako follow-up (poza
|
||||
zakresem tego zamknięcia bramki — `kb-query`'s `mode` param zmiana to osobna,
|
||||
mała zmiana w serwisie, nie w `packages/kb-retrieval`). **Wykonane 2026-08-06**
|
||||
po regresji na pełnym korpusie (Etap B) — szczegóły przy DoD (d) w §12.
|
||||
mała zmiana w serwisie, nie w `packages/kb-retrieval`).
|
||||
|
||||
## 9. Krok 6 — Etap B: pełne archiwum
|
||||
|
||||
|
|
@ -607,139 +589,8 @@ po regresji na pełnym korpusie (Etap B) — szczegóły przy DoD (d) w §12.
|
|||
|
||||
**Szacunek: 1 sesja (run w tle).**
|
||||
|
||||
### Decyzje operatora do Etapu B (2026-08-04) — przed runem
|
||||
|
||||
Recon przed Etapem B (mirror archiwum na SOLARII żyje: 225 057 plików / 27 GB; RTT
|
||||
SOLARIA→PIHA 0,83 ms; PIHA 140 GB wolne, baza 397 MB; M1 — `NODE_TYPE=lte_node`
|
||||
na node-agencie SOLARII — zdeployowane, więc kontener Ollamy nie zniknie po
|
||||
zatrzymaniu) wykazał dwie rzeczy do rozstrzygnięcia. Decyzje:
|
||||
|
||||
1. **Run w plastrach po 50k** (`--limit 50000 --offset 0/50k/100k/150k/200k`),
|
||||
log per plaster, `nice`/`ionice`. Powód: brak checkpointu (restart = ponowny
|
||||
parse od początku listy, ~1 h) + nocne wyłączanie SOLARII. Plaster ≈ 25–40 min.
|
||||
Tempo kolejnych plastrów po obserwacji PIHA po pierwszym.
|
||||
2. **Circuit breaker w jobie: TAK** — `--max-embed-failures` (domyślnie 5),
|
||||
abort z kodem wyjścia 2 po N kolejnych nieudanych batchach embed. Powód:
|
||||
parse jest jednowątkowy i wyprzedza GPU, więc martwa Ollama (4 incydenty)
|
||||
zamieniłaby 2-godzinny przebieg w 200k+ `chunks_errors` bez ani jednego
|
||||
zapisu. Licznik zeruje się po udanym batchu.
|
||||
3. **Dry-run całości pomijamy** — idempotencja i odwracalność flag newsletterowych
|
||||
wystarczają; ewentualna kalibracja heurystyki na dekadzie 2010–2015 po fakcie,
|
||||
na już zapisanych flagach.
|
||||
4. Przełączenie domyślnego `mode` kb-query na `hybrid` (DoD (d)) — **poza zakresem
|
||||
Etapu B**, osobny task po PASS regresji. *(Wykonany 2026-08-06 — DoD (d) w §12.)*
|
||||
|
||||
### Hardening toru embed przed Etapem B (2026-08-05)
|
||||
|
||||
Recon pod kątem batchingu potwierdził, że batch `/api/embed` (Krok 1) działa zgodnie
|
||||
z §1.4 — ale wykazał w torze backfillu **błąd blokujący dla Etapu B** i dwie luki:
|
||||
|
||||
1. **BUG (naprawiony)**: `flush_embed_buffer` łapał wyłącznie `aiohttp.ClientError`, a
|
||||
wyczerpanie `ClientTimeout(total=...)` rzuca goły `builtins.TimeoutError`, który **nie**
|
||||
jest jego podklasą (zweryfikowane empirycznie na aiohttp 3.14.3). Zawieszona Ollama —
|
||||
czyli dokładnie jej udokumentowany failure mode, „przyjmuje połączenie i milczy", nie
|
||||
„odmawia" — wywalała cały run nieobsłużonym wyjątkiem: **bez breakera i bez flushu
|
||||
threadingu**. Na plastrze 50k oznaczało to utratę już zarobionej pracy. Klasy przejściowe
|
||||
nazwane teraz jawnie w `kb_retrieval.embed.TRANSIENT_EMBED_ERRORS`.
|
||||
2. **Brak retry** — jeden blip sieciowy spisywał na straty cały batch (64 chunki). Dodane:
|
||||
`--embed-retries` (default 2) z backoffem wykładniczym.
|
||||
3. **Brak obsługi błędu częściowego** — `/api/embed` jest all-or-nothing, więc jeden trujący
|
||||
chunk zabijał batch w kółko i mógł wywalić breaker przy **żywym** backendzie. Dodana
|
||||
bisekcja po nieudanych retry, ale tylko gdy `/api/tags` potwierdza, że backend żyje;
|
||||
przy martwym batch od razu „gives up" (bisekcja martwego backendu kosztowałaby 2n-1
|
||||
żądań i opóźniała breaker). Porażka częściowa **nie** przesuwa już breakera.
|
||||
|
||||
Decyzja 2 (circuit breaker) obowiązuje w zaostrzonej formie: licznik liczy **give-upy**
|
||||
(backend padł), nie dowolne nieudane batche. Fallback SOLARIA→PIHA dla backfillu **świadomie
|
||||
nie powstaje** — 271k chunków × 790 ms CPU ≈ 60 h na 8 GB PIHA dzielonym z HA i Paperlessem;
|
||||
właściwą odpowiedzią na martwy backend jest exit 2 i wznowienie plastra. Tor online (`kb-query`
|
||||
→ `embed_router`) ma fallback i tak zostaje — te dwie ścieżki są rozdzielone celowo.
|
||||
|
||||
Doszedł też `mail-body-ingest-bench` — sweep batch size na realnych chunkach (read-only),
|
||||
żeby liczby z §1.4 dało się odtworzyć po zmianie GPU albo wersji Ollamy.
|
||||
|
||||
### Wynik Etapu B (ZAMKNIĘTY, 2026-08-06)
|
||||
|
||||
Pełny korpus gmail jest zchunkowany i zembedowany: **389 012 chunków**
|
||||
`document_chunk`, z czego **0 nie-excluded bez embeddingu** — weryfikacja
|
||||
przebiegła idempotentnymi plastrami 0-4 (`--offset 0/50k/100k/150k/200k
|
||||
--limit 50000 --batch-size 64`, wszystkie EXIT 0) plus fix bajtu NUL (`4ec0b78`).
|
||||
|
||||
Cross-tab na żywej bazie (kb-postgres@PIHA):
|
||||
|
||||
| Miara | Wartość |
|
||||
|---|---|
|
||||
| `document_chunk` total | **389 012** |
|
||||
| nie-excluded **bez** embeddingu | **0** |
|
||||
| nie-excluded z wektorem | 187 025 |
|
||||
| `newsletter`-flagged bez wektora | 201 849 (odwracalne, Decyzja 4) |
|
||||
| excluded **z** wektorem | 138 (artefakt kolejności flagowania, nieszkodliwy) |
|
||||
|
||||
**Korpus był w pełni zembedowany jeszcze przed plastrami z 2026-08-06** —
|
||||
zapamiętany stan „6,4k embedded z Etapu A, ~225k kopert do backfillu" był
|
||||
nieaktualny, wcześniejsze przebiegi pokryły całość. Dzisiejsze runy to pełna,
|
||||
idempotentna weryfikacja. Źródło mylącego odczytu: licznik
|
||||
`chunks_already_embedded` liczy **istnienie wiersza w DB** (w tym chunków
|
||||
newsletter-flagged bez wektora), a nie obecność wektora.
|
||||
|
||||
**Bug NUL (naprawiony, `4ec0b78`)**: bajt `0x00` w treści maili z 2007 (Sony
|
||||
Ericsson, 3 chunki, plaster offset 50k) wywalał insert
|
||||
(`asyncpg.CharacterNotInRepertoireError` — PostgreSQL nie przyjmuje `0x00`
|
||||
w `text`). Fix: strip `\x00` przed chunkowaniem i embedem + liczniki
|
||||
`nul_bytes_stripped` / `mails_nul_sanitized`. Re-run plastra 1: EXIT 0,
|
||||
3 chunki dobrane. Znany follow-up (osobny task): `jobs/gmail-header-backfill`
|
||||
i `jobs/gmail-bulk-import` mają tę samą latentną podatność na NUL w nagłówkach
|
||||
zapisywanych do `jsonb`.
|
||||
|
||||
Szczegóły runu: `docs/sessions/2026-08-06-kb-etapb-backfill.md`.
|
||||
|
||||
Uwaga do czytania wyników: na pełnym korpusie `exit 1` jest spodziewany
|
||||
(pojedyncze `parse_errors` — §1.5 dokumentuje ~9 maili na fallbacku compat32).
|
||||
Werdyktem jest bilans i liczniki w linii `summary`, nie kod wyjścia. `exit 2`
|
||||
oznacza co innego: backend embed padł, trzeba wznowić plaster po naprawie Ollamy.
|
||||
|
||||
## 10. Krok 7 — IMAP/JMAP przyrostówka (zarys; szczegóły = osobny recon)
|
||||
|
||||
> **Stan: IN PROGRESS (2026-08-06).** Recon:
|
||||
> `kb/audits/mail-sync-2026-08-06.md`; decyzje (a)-(g) zatwierdzone przez
|
||||
> operatora 2026-08-06 w całości, implementacja opisana niżej. Zarys
|
||||
> poniżej pochodzi z 2026-07-22 i zachowuję go jako zapis intencji. Recon
|
||||
> rozstrzyga inaczej dwa jego punkty: (1) **Fastmail przez IMAP, nie JMAP**
|
||||
> (unifikacja adaptera — jeden `jobs/mail-imap-sync` zamiast
|
||||
> `fastmail-poller` + `gmail-imap-poller`), (2) spoiwem z torem body nie jest
|
||||
> `--since`, tylko kolejka „koperty bez chunków". Reszta zarysu (poll zamiast
|
||||
> IDLE, reuse `save_eml`/`insert_envelope`, sekrety w `/opt/homelab/config/`)
|
||||
> się potwierdziła.
|
||||
|
||||
### Zakres wdrożony (2026-08-06)
|
||||
|
||||
| # | Element | Gdzie |
|
||||
|---|---|---|
|
||||
| 1 | Adapter IMAP, jeden na oba konta — EXAMINE + `BODY.PEEK[]` (nigdy nie ustawia `\Seen`), wybór folderu po atrybucie SPECIAL-USE | `packages/kb-mail/src/kb_mail/imap.py` |
|
||||
| 2 | Model stanu synca: `plan_folder_sync` (pierwszy tick / przyrost / unieważnienie UIDVALIDITY) + `contiguous_last_uid` (kursor tylko po nieprzerwanym ciągu sukcesów) | `packages/kb-mail/src/kb_mail/sync_state.py` |
|
||||
| 3 | Migracja `005_mail_sync_state.sql`, klucz `(account, folder)` — Decyzja (f) | `services/kb-postgres/init/` |
|
||||
| 4 | Job przyrostówki: fetch → `save_eml` → `insert_envelope(entities=[headers, attachment…])` → kursor. **Nie chunkuje i nie embeduje** | `jobs/mail-imap-sync/` |
|
||||
| 5 | Ekstrakcja wspólnego parse'u (`parse_headers`, `message_id`, `parse_date`, `parse_attachments`) do `kb-mail` — klucz dedup z jednej implementacji | `packages/kb-mail/src/kb_mail/{headers,message}.py` |
|
||||
| 6 | `--sources` + `--only-unchunked` w `mail-body-ingest`; pre-fetch kluczy chunków zawężony do zbioru roboczego | `jobs/mail-body-ingest/` |
|
||||
| 7 | `DEFAULT_SUMMARYLESS_SOURCES += "fastmail"` — Decyzja (g), w tym samym commicie co źródło | `packages/kb-retrieval/` |
|
||||
| 8 | Etap mailowy w `kb-ingest` (kolejka „koperty bez chunków") + takt timera 03:30 → co 2 h — Decyzja (d) | `jobs/documents-ingest/` |
|
||||
| 9 | Jednostki systemd (**nieaktywowane**) + deklaracja jednostek host-level | `jobs/mail-imap-sync/systemd/`, `hosts/piha/jobs.yaml` |
|
||||
| 10 | Metryki `.prom` per konto + reguła `KbMailSyncStale` | `services/fleet-prometheus/rules/kb-mail-sync.yml` |
|
||||
| 11 | `env.example` z placeholderami; poświadczenia wyłącznie ze środowiska — Decyzja (c) | `jobs/mail-imap-sync/env.example` |
|
||||
| 12 | Runbook pierwszego uruchomienia + checklista punktów `[do weryfikacji na żywo]` z reconu | `kb/runbooks/mail-sync-run.md` |
|
||||
|
||||
Dokumentacja serwisu: `kb/services/job-mail-imap-sync.md`.
|
||||
|
||||
**Poza zakresem tej implementacji, świadomie:** pierwszy żywy sync, pomiar
|
||||
`STATUS (MESSAGES)` na Fastmailu i wynikająca z niego **decyzja o historii
|
||||
Fastmaila** (Decyzja (e) — recon celowo jej nie podejmuje, bo zależy od liczby,
|
||||
której nikt jeszcze nie zna), oraz aktywacja timera. Wszystko to robi operator
|
||||
wg runbooka. Alert „cisza w skrzynce" **odrzucony** (decyzja operatora, zgodna
|
||||
z reconem §3.4): zero nowych maili to legalny stan skrzynki, a alert zapalający
|
||||
się na zdrowym systemie zostaje wyciszony — i przestaje działać wtedy, gdy jest
|
||||
potrzebny. Jedyny alert to `KbMailSyncStale` („czy poller w ogóle działa"),
|
||||
który fałszywych trafień nie ma.
|
||||
|
||||
Zakotwiczone w kb-00 jako etapy 3–4 (`jobs/fastmail-poller`,
|
||||
`jobs/gmail-imap-poller`). Zarys decyzji do tamtego reconu:
|
||||
|
||||
|
|
@ -766,24 +617,20 @@ Zakotwiczone w kb-00 jako etapy 3–4 (`jobs/fastmail-poller`,
|
|||
| `mail_ui_url` (klikalny link do maila w UI) | kb-00 etap 6, pole zarezerwowane w kb-query | z modułem mail-UI |
|
||||
| Graf wątków / entity_link z `entities[type=threading]` | ta faza tylko zapisuje surowiec (Decyzja 10) | przyszła faza „połączenia" |
|
||||
| Streszczenia selektywne maili (hybryda Haiku) | Decyzja 6 — odłożona | po ocenie trybu hybrid w praktyce |
|
||||
| ~~IMAP/JMAP przyrostówka — implementacja~~ | Krok 7 | **wykonane 2026-08-06** (§10 „Zakres wdrożony"); pierwszy żywy sync po stronie operatora |
|
||||
| Historia Fastmaila (pełny zaciąg vs tylko przyrost) | Decyzja (e) reconu | po pomiarze `mail-imap-sync --measure` — runbook §5 |
|
||||
| Alert per konto na wiek najnowszego maila (`kb_mail_sync_last_message_ts`) | recon §3.4 | po miesiącu obserwacji; próg z pomiaru, nie z góry |
|
||||
| Etykiety Gmaila w `entities` (`X-GM-LABELS`) | recon §2.3 | odłożone — `X-GM-EXT-1` przywiązuje kod do Google, wprost wbrew „protokół, nie provider" |
|
||||
| IMAP/JMAP przyrostówka — implementacja | Krok 7 (zarys) | osobny recon + pakiet |
|
||||
|
||||
## 12. Plan implementacji (kolejność = zależności)
|
||||
|
||||
| # | Krok | Zależy od | Szacunek | Stan | Dowód (2026-08-04) |
|
||||
|---|---|---|---|---|---|
|
||||
| 0 | Chunker → `packages/kb-mail` | — | 0,5 sesji | **WYKONANE** | `348ce10`; `packages/kb-mail/src/kb_mail/chunking.py` + `tests/test_chunking.py` |
|
||||
| 1 | `embed_batch` w kb-retrieval | — | 0,5 sesji | **WYKONANE** | `51998fd`; `kb_retrieval/embed.py:61` (`embed_batch`) + `tests/test_embed.py` |
|
||||
| 2 | Job `mail-body-ingest` | 0, 1 | 2 sesje | **WYKONANE** | `ad0ef40` (job), `a95524c` (README), `fc5c698` (fix html_to_text); `jobs/mail-body-ingest/` + `tests/test_ingest.py` |
|
||||
| 3 | Tryb hybrid (kb-retrieval + kb-query) | — (równolegle z 2) | 1 sesja | **WYKONANE** | `a640cf1`; `kb_retrieval/retrieval.py:112` (`hybrid_retrieve`), `:195` (`hybrid_query`), `kb-query/app/main.py:119` (`mode` pattern). Domyślny `mode` przełączony na `hybrid` 2026-08-06 (follow-up z §8, DoD (d) niżej) |
|
||||
| 4 | rsync + Etap A (12 mies.) + kalibracja | 2 | 1 sesja | **WYKONANE** | §7 „Wynik Etapu A" (run na żywo 2026-07-23); potwierdzone na żywej bazie 2026-08-04: `document_chunk` gmail = 33 871 (6 398 z embeddingiem + 27 473 `newsletter`) — zgodne co do sztuki z tabelą §7 |
|
||||
| 5 | Bramka jakościowa (eval mailowy + regresja) | 3, 4 + zapytania od operatora | 1 sesja | **WYKONANE** (PASS) | §8 „Wynik bramki"; `56f64e9` (eval + queries.yaml dla hybrid), `bce635c` (`mail_hit@3`, próg N2, werdykt PASS), `71eb264` (`--transport http`) |
|
||||
| 6 | Etap B (pełne archiwum) + regresja + obserwacja PIHA | 5 = PASS | 1 sesja | **WYKONANE** (2026-08-06) | §9 „Wynik Etapu B"; żywa baza: 389 012 chunków, 0 nie-excluded bez embeddingu. Weryfikacja plastrami 0-4 (wszystkie EXIT 0) + fix NUL `4ec0b78`; `docs/sessions/2026-08-06-kb-etapb-backfill.md` |
|
||||
| 7 | Recon przyrostówki IMAP/JMAP | — (po 6) | 1 sesja (poza DoD fazy) | **WYKONANE** (2026-08-06) | `kb/audits/mail-sync-2026-08-06.md`. Ustalenia: korpus urywa się 2026-06-19 (dziura 48 dni ≈ 1 800 maili), zero kodu IMAP w repo, brak modelu stanu synca; cały nowy kod to jeden `jobs/mail-imap-sync` + 4 drobne zmiany w istniejącym torze. Decyzje (a)-(g) zatwierdzone przez operatora 2026-08-06 |
|
||||
| 8 | Implementacja przyrostówki IMAP | 7 + decyzje (a)-(g) | 2 sesje (poza DoD fazy) | **IN PROGRESS** (2026-08-06) | Zakres w §10 „Zakres wdrożony". Testy zielone: 80 (`mail-imap-sync`) + 111 (`kb-mail`) + 191 (`documents-ingest`) + 127 (`mail-body-ingest` / `kb-retrieval`). **Nie uruchomione na żywym koncie** — pierwszy sync, pomiar Fastmaila i aktywacja timera po stronie operatora (`kb/runbooks/mail-sync-run.md`) |
|
||||
| # | Krok | Zależy od | Szacunek |
|
||||
|---|---|---|---|
|
||||
| 0 | Chunker → `packages/kb-mail` | — | 0,5 sesji |
|
||||
| 1 | `embed_batch` w kb-retrieval | — | 0,5 sesji |
|
||||
| 2 | Job `mail-body-ingest` | 0, 1 | 2 sesje |
|
||||
| 3 | Tryb hybrid (kb-retrieval + kb-query) | — (równolegle z 2) | 1 sesja |
|
||||
| 4 | rsync + Etap A (12 mies.) + kalibracja | 2 | 1 sesja |
|
||||
| 5 | Bramka jakościowa (eval mailowy + regresja) | 3, 4 + zapytania od operatora | 1 sesja |
|
||||
| 6 | Etap B (pełne archiwum) + regresja + obserwacja PIHA | 5 = PASS | 1 sesja |
|
||||
| 7 | Recon przyrostówki IMAP/JMAP | — (po 6) | 1 sesja (poza DoD fazy) |
|
||||
|
||||
**Kryterium ukończenia fazy mailowej:** (a) pełny korpus gmail zchunkowany
|
||||
(bilans domknięty, `parse_errors` na poziomie pojedynczych sztuk jak
|
||||
|
|
@ -792,28 +639,11 @@ hybrid, (c) bramka §8 PASS wraz z regresją po Etapie B, (d) `kb-query`
|
|||
domyślnie odpowiada trybem hybrid na `kb.kapala.org`, (e) koperty gmail mają
|
||||
`entities[type=threading]`.
|
||||
|
||||
**(d) SPEŁNIONE w repo 2026-08-06** — domyślny `mode` w `/search` przełączony
|
||||
`cascade` → `hybrid` (`services/kb-query/app/main.py`, walidator `Query`;
|
||||
frontend przestał wysyłać `mode` przy odznaczonym „tryb flat", więc UI
|
||||
dziedziczy domyślny tryb API). Podstawa: eval na **pełnym** korpusie
|
||||
(187 025 zembedowanych chunków mailowych w HNSW, §9) —
|
||||
kryterium 1 (regresja paperless) **PASS**: żaden istniejący hit nie
|
||||
zdegradował ani we flat, ani w hybrid; mailowe **hit@3 = 5/5**; koszt
|
||||
hybrydy to jedno dodatkowe zapytanie SQL na wyszukiwanie. Surowe wyniki:
|
||||
`eval-http-2026-08-06.json` i `eval-direct-2026-08-06.json`
|
||||
w `~/kb/mail/ingest-logs` na PIHA (celowo niecommitowane — artefakt runu).
|
||||
Jawne `?mode=cascade` / `?mode=flat` działają bez zmian.
|
||||
**Deploy na PIHA robi operator z mastera po mergu** — do tego czasu
|
||||
`kb.kapala.org` nadal odpowiada kaskadą.
|
||||
|
||||
## 13. Szacunki zbiorcze
|
||||
|
||||
- **Dane**: +~496k wierszy `document_chunk` (~271k z embeddingiem, ~225k
|
||||
flagowanych `newsletter`); baza 250 MB → ~5–7 GB (dysk PIHA: 144 GB wolne,
|
||||
zapas >20×). HNSW rośnie inkrementalnie przy insertach — bez rebuildu.
|
||||
**Wykonanie (2026-08-06, §9): 389 012 wierszy — 187 025 z embeddingiem,
|
||||
201 849 flagowanych `newsletter`.** Mniej niż ekstrapolacja z §1.3, bo
|
||||
quote-strip (Decyzja 2) realnie ucina objętość, co §1.3 zapowiadał.
|
||||
- **GPU/czas runów**: Etap A <1 h e2e; Etap B: parse ~0,5–1 h (24 rdzenie)
|
||||
+ embed ~1–1,5 h (batch 64, zmierzone 8–18 ms/chunk) + inserty do PIHA.
|
||||
- **Koszty zewnętrzne: 0 USD** (bez streszczeń — Decyzja 6).
|
||||
|
|
@ -827,11 +657,6 @@ Jawne `?mode=cascade` / `?mode=flat` działają bez zmian.
|
|||
|
||||
## 14. Podsumowanie dla Oskara
|
||||
|
||||
> **Uwaga (2026-08-06):** poniższe to podsumowanie z chwili reconu (2026-07-22),
|
||||
> zachowane jako zapis intencji. Plan został wykonany — treści 225k maili są
|
||||
> w bazie i w retrievalu (§9 „Wynik Etapu B"); otwarty jest już tylko Krok 7
|
||||
> (przyrostówka IMAP/JMAP).
|
||||
|
||||
Treści Twoich 225 tysięcy maili leżą dziś martwe w 27 GB archiwum na PIHA —
|
||||
w bazie są tylko nagłówki, a wyszukiwarka z fazy 4 słusznie skarży się „mało
|
||||
danych". Ten plan wprowadza je do retrievalu w ~7 sesji i za 0 USD: ponowny
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-13
|
||||
links: []
|
||||
---
|
||||
|
||||
# Modul 5, faza 2 — koperta dokumentow + embeddingi + cross-source (RECON + PLAN)
|
||||
|
||||
> Status: RECON ZAKONCZONY (2026-07-13), architektura DO ZATWIERDZENIA. Zaden kod nie
|
||||
|
|
@ -545,7 +536,7 @@ zeby dalo sie uruchomic partiami i zweryfikowac progres bez czekania na cale 225
|
|||
rzedu dziesiatek-set chunkow/s. Caly pilot (2–3k chunkow) → **rzedu minut**, nie wymaga
|
||||
specjalnego batchowania/partii.
|
||||
- **Skala docelowa (70k zalacznikow z maili)**: modul 5 faza-1 to swiadomie **probka, nie
|
||||
bulk** (`kb/phases/kb-m5-documents-ingest-fazy.md` — decyzja architektoniczna). Realny wolumen
|
||||
bulk** (`jobs/documents-ingest/README.md` — decyzja architektoniczna). Realny wolumen
|
||||
ktory trafi do embeddingu zalezy od (a) throughput OCR-workera na SOLARII (modul 3) —
|
||||
**to jest waskie gardlo skalowania, nie embedding** — oraz (b) filtra selektywnosci
|
||||
(decyzja #6). Sam embedding bge-m3 nie bedzie bottleneckiem nawet przy tysiacach
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-27
|
||||
links: []
|
||||
---
|
||||
|
||||
# Moduł 5, faza 3 — warstwa kompilacji (RECON + PLAN)
|
||||
|
||||
> Status: RECON ZAKOŃCZONY (2026-07-16), plan DO ZATWIERDZENIA. Zero kodu, zero migracji,
|
||||
|
|
@ -448,7 +439,7 @@ Próbka 25 dokumentów (stratyfikowana: polisy, faktury, umowy, urzędowe, FLL/s
|
|||
- **kompletność faktów kluczowych** (kwoty, daty, strony, numery),
|
||||
- **jakość tagów** (trafność + zgodność ze słownikiem).
|
||||
|
||||
Wynik do `kb/phases/kb-m5-faza3-pilot-streszczen.md`: tabela per dokument + wnioski.
|
||||
Wynik do `docs/kb/modules/05-faza3-pilot-streszczen.md`: tabela per dokument + wnioski.
|
||||
**Kryterium „lokalny wystarcza na skalę mailową"**: mediana wierności = 2 (zero tolerancji
|
||||
dla przekręconych kwot — to trafia do wiki) i kompletność ≥ 80% punktów API. Jeśli lokalny
|
||||
nie daje rady → decyzja o skali mailowej rozważa API z polityką eskalacji fazy 5 (koszt
|
||||
|
|
@ -505,7 +496,7 @@ aktywnych chunków, N większe niż liczba kopert, no-summaries short-circuit)
|
|||
pakietu przechodzi.
|
||||
|
||||
**Eval-set utrwalony**: `jobs/documents-ingest/eval/queries.yaml` (7 zapytań z pilota
|
||||
07-16, 1:1 z `kb/phases/kb-m5-eval-retrieval-pilot.md`, ten plik pozostał nietknięty —
|
||||
07-16, 1:1 z `docs/kb/eval/retrieval-pilot-2026-07-16.md`, ten plik pozostał nietknięty —
|
||||
`queries.yaml` to jego wersjonowana kopia robocza). Skrypt bramki (read-only, integracyjny,
|
||||
**nie wchodzi do pytest**): `jobs/documents-ingest/eval/retrieval_eval.py`.
|
||||
|
||||
|
|
@ -560,19 +551,10 @@ Adapter i embed są już idempotentne — nowość to wyłącznie orkiestracja i
|
|||
`/etc/systemd/system/`, `systemctl enable --now kb-ingest.timer`). Pierwszy
|
||||
systemd-timer w repo — świadomie host-level, nie kontener (joby potrzebują jednocześnie
|
||||
LAN, DB i plików hosta; konteneryzacja nic tu nie daje).
|
||||
- **Harmonogram**: ~~`OnCalendar=*-*-* 03:30`~~ → **`OnCalendar=0/2:00:00` (co 2 h) od
|
||||
2026-08-06**, `Persistent=true` (nadgania po reboocie). Zmiana wynika z Decyzji (d) reconu
|
||||
przyrostówki (`kb/audits/mail-sync-2026-08-06.md` §3.3): o 03:30 SOLARIA prawie na pewno
|
||||
śpi (potwierdzone odczytem `kb_ingest_embed_skipped 1`), a od tej daty tick dostaje też
|
||||
etap mailowy — ~60 nowych chunków na dobę pomijanych każdej nocy zapaliłyby
|
||||
`KbEmbedBacklogGrowing` na stałe. Co 2 h zamiast stałej godziny dopasowanej do nawyków
|
||||
operatora: probe Ollamy sam wybiera okno, więc któryś tick w nie trafi niezależnie od tego,
|
||||
o której SOLARIA wstaje w danym tygodniu.
|
||||
- **Harmonogram**: `OnCalendar=*-*-* 03:30`, `Persistent=true` (nadgania po reboocie).
|
||||
- **Sekwencja skryptu**: adapter `--apply` → chunk_embed `--apply`
|
||||
(`OLLAMA_URL=http://solaria:11434`) → **mail_body_ingest `--only-unchunked`** (dodane
|
||||
2026-08-06 — konsument kolejki, którą wypełnia `jobs/mail-imap-sync`; import miękki, więc
|
||||
venv bez tego pakietu pomija etap zamiast wywracać wrapper) → summarize → embed-summaries.
|
||||
Log do `/opt/homelab/logs/kb-ingest/run-YYYYMMDD.log`.
|
||||
(`OLLAMA_URL=http://solaria:11434`) → (po decyzji z pilota, rozszerzenie później:
|
||||
summarize nowych dokumentów). Log do `/opt/homelab/logs/kb-ingest/run-YYYYMMDD.log`.
|
||||
- **Tolerancja na SOLARIĘ offline** (`availability_target: medium`): wrapper odróżnia
|
||||
„Ollama nieosiągalna" (probe `GET /api/tags` przed embedem; brak → pomiń embed,
|
||||
odnotuj, **to nie jest fail** — nadrobi następny run, bo embed jest idempotentny) od
|
||||
|
|
@ -628,17 +610,6 @@ Adapter i embed są już idempotentne — nowość to wyłącznie orkiestracja i
|
|||
> półprodukt kompilacji) i PO filtrze śmieciowych chunków. Pełna wiki po fazie mailowej
|
||||
> (przyrostówka) — wcześniej kompilat byłby fotografią przeszłości.
|
||||
|
||||
**Inwariant 7 (dodany 2026-08-27 — rozszerzenie, nie zmiana inwariantów 1–6
|
||||
powyżej, które pozostają NIE podlega zmianie):** decyzja (f),
|
||||
`kb/audits/wiki-kompilat-recon-2026-08-26.md` §10, zatwierdzona przez operatora
|
||||
w całości 2026-08-27. Kompilacja strony wiki **nigdy** nie czyta `source='wiki'`
|
||||
jako dowodu (`exclude_sources=('wiki',)` w `cascade_retrieve`/`hybrid_retrieve`) —
|
||||
retrieval na potrzeby kompilacji zawsze wyklucza wiki, czyta wyłącznie warstwę
|
||||
dowodową (mail, paperless). Tylko `/search` (warstwa użytkownika, po zbudowaniu
|
||||
syntezy odpowiedzi — inwariant 5, faza 5) widzi wiki w kaskadzie. Mitygacja
|
||||
self-citation/citogenesis przy źródle retrievalu, nie tylko przez lint (inwariant
|
||||
3) po fakcie — patrz audyt §7 dla pełnego rozumowania.
|
||||
|
||||
### 8.2 Rozwinięcie wykonawcze (szczegóły, decyzje architektoniczne bez zmian)
|
||||
|
||||
**Repozytorium** (rozstrzygnięcie punktu 6 szkicu): osobne repo `kb-wiki` — decyzja 7,
|
||||
|
|
@ -720,7 +691,7 @@ streszczeń). Kandydaci na pierwsze strony (encje z pilota): PZU/WARTA (polisy),
|
|||
## 9. Poza zakresem fazy 3
|
||||
|
||||
Granice planu — wszystko poniżej jest świadomie odłożone, z istniejącym miejscem w
|
||||
roadmapie (`kb/subsystems/kb-overview.md` „Stan etapów/Backlog", sesja
|
||||
roadmapie (`docs/kb/kb-00-overview.md` „Stan etapów/Backlog", sesja
|
||||
`docs/sessions/2026-07-16.md`, backlog operatora):
|
||||
|
||||
| Temat | Gdzie zakotwiczone | Kiedy |
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: phase
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-22
|
||||
links: []
|
||||
---
|
||||
|
||||
# Moduł 5, faza 4 — kb-query + UI (RECON + PLAN)
|
||||
|
||||
> Status: RECON ZAKOŃCZONY (2026-07-22), plan DO ZATWIERDZENIA. Zero kodu, zero
|
||||
|
|
@ -68,7 +59,7 @@ links: []
|
|||
|
||||
### 1.3 PIHA — budżet RAM (istotne dla decyzji D2 / lokalnego fallbacku)
|
||||
|
||||
- Audyt `kb/audits/piha-slim-2026-07-02.md`: po "bezpiecznym usuń"
|
||||
- Audyt `docs/infra/piha-slim-audit-2026-07-02.md`: po "bezpiecznym usuń"
|
||||
(elasticsearch+diskover+stary llm-gateway) available rosło z **2.9Gi do ~3.8Gi**
|
||||
(na 8 GB total, +4 GB swap). To jest **jedyna zweryfikowana liczba w repo i ma
|
||||
3 tygodnie** — od tego dnia na PIHA doszły (GitOps, z `mem_limit`): paperless+db+broker
|
||||
|
|
@ -97,7 +88,7 @@ domeny `*.kapala.org`:
|
|||
(cert #51 *.okit.pl, cert osobny dla *.kapala.org — sesje ją tylko konfigurują
|
||||
ręcznie/SQL-em, nigdy nie deployują z repo). kb-query dogania się do tego samego
|
||||
wzorca: nowy vhost w `npm@PIHA`, TLS z **już istniejącego** wildcard `*.kapala.org`
|
||||
(pokrywa `paper.`/`cloud.`/`vikunja.kapala.org` — `kb/decisions/kb-dokumenty-otwarte.md`
|
||||
(pokrywa `paper.`/`cloud.`/`vikunja.kapala.org` — `docs/kb/modules/DECYZJE-do-podjecia.md`
|
||||
#6) — **żaden nowy certyfikat nie jest potrzebny**.
|
||||
- **DNS — dwie warstwy, obie trzeba dotknąć** (lekcja `okit-cloudflare-migracja.md`
|
||||
§"WAZNE: split-horizon DNS"): (a) Cloudflare rekord A → Tailscale IP PIHA
|
||||
|
|
@ -261,7 +252,7 @@ przed backfillem" z fazy 3 §3.1 (zmierz, obejrzyj, dopiero wtedy zaufaj progowi
|
|||
### Decyzja 3 — Linki do źródeł: paperless vs gmail
|
||||
|
||||
**Paperless**: URL do dokumentu — **do zweryfikowania na żywym Paperless przed
|
||||
implementacją** (repo nie ma zapisanego przykładu, `kb/phases/kb-m2-paperless.md`
|
||||
implementacją** (repo nie ma zapisanego przykładu, `docs/kb/modules/02-paperless-service.md`
|
||||
dokumentuje tylko subdomenę, nie ścieżkę). Kandydat wg konwencji paperless-ngx UI:
|
||||
`https://paper.kapala.org/documents/<id>/details` (Angular routing) — `envelope_id`
|
||||
`paperless:<id>` już niesie surowy `<id>` do wstawienia. Krok implementacji: jeden
|
||||
|
|
@ -471,7 +462,7 @@ Jedna strona (Jinja2 template + vanilla JS + CSS, serwowane z tego samego FastAP
|
|||
- Pole zapytania + submit (Enter albo przycisk).
|
||||
- Wyniki grupowane po `envelope_id` (dokument), w obrębie dokumentu chunki
|
||||
posortowane po `dist`.
|
||||
- Kolorowanie progów (progi z fazy 3, `kb/phases/kb-m5-faza3.md` §1.2,
|
||||
- Kolorowanie progów (progi z fazy 3, `docs/kb/modules/05-faza3-plan.md` §1.2,
|
||||
zweryfikowane bramką): `dist < 0.45` zielony, `0.45–0.55` żółty, `> 0.55` —
|
||||
**nie renderować wyniku**, tylko komunikat "brak odpowiedzi w KB" (żółta/czerwona
|
||||
strefa nadal renderuje wynik z ostrzeżeniem wizualnym; czerwona = brak sensownego
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: decision
|
||||
visibility: private
|
||||
status: planned
|
||||
updated: 2026-07-09
|
||||
links: []
|
||||
---
|
||||
|
||||
# Decyzje do podjęcia — filar dokumentów (moduły 2/3/4)
|
||||
|
||||
> Zbiorcza lista decyzji z przygotowania configów (2026-07-06, branch
|
||||
|
|
@ -63,7 +54,7 @@ zdecydować, czy podbić concurrency, czy zostawić zapas na ollama/AI.
|
|||
rsync/borg → SOLARIA (2 TB, ta sama LAN). Retencja: 7 dziennych +
|
||||
4 tygodniowe + 6 miesięcznych. Offsite (np. restic → chmura) zostaje jako
|
||||
future-note, poza zakresem tego etapu. Cron/skrypt deployowy powstaje przy
|
||||
deployu modułu 2, nie teraz. Szczegóły: `kb/services/paperless.md`.
|
||||
deployu modułu 2, nie teraz. Szczegóły: `services/paperless/README.md`.
|
||||
|
||||
- **4. Redis brokera: `requirepass`.** Broker (6380) dostaje hasło —
|
||||
`PAPERLESS_REDIS_PASSWORD` w `.env` po obu stronach (paperless@PIHA,
|
||||
|
|
@ -76,7 +67,7 @@ zdecydować, czy podbić concurrency, czy zostawić zapas na ollama/AI.
|
|||
+ SOLARIA po NFS) świadomie zaakceptowane — indeks jest odtwarzalny
|
||||
(`document_index reindex`), oryginałom nic nie grozi. Bez zmian w
|
||||
configu; fallback-worker na PIHA zostaje. Szczegóły:
|
||||
`kb/services/paperless-worker.md`.
|
||||
`services/paperless-worker/README.md`.
|
||||
|
||||
- **6. Domeny: `kapala.org` (mesh, prywatne).** `paper.kapala.org`
|
||||
(Paperless), `cloud.kapala.org` (Nextcloud) — potwierdzone, `*.okit.pl`
|
||||
|
|
@ -99,7 +90,7 @@ zdecydować, czy podbić concurrency, czy zostawić zapas na ollama/AI.
|
|||
maintainerów paperless-ngx (nieoficjalnie wspierany): ten sam obraz,
|
||||
`command: celery --app paperless worker`, wspólny Redis+Postgres+storage,
|
||||
identyczne ścieżki kontenerowe i numeryczny UID po obu stronach. Pełny
|
||||
wynik badania + ryzyka: `kb/services/paperless-worker.md`.
|
||||
wynik badania + ryzyka: `services/paperless-worker/README.md`.
|
||||
- Storage dokumentów na PIHA; NFS export → SOLARIA po LAN
|
||||
(192.168.31.5 → 192.168.31.70), nie Tailscale.
|
||||
- AOF w Redis brokera (kolejka przeżywa restart — zero utraty zadań).
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-05-11
|
||||
links:
|
||||
- ../runbooks/service-operational-recovery.md
|
||||
---
|
||||
|
||||
# Service Lifecycle and Recovery
|
||||
|
||||
This document defines the lifecycle of a service in the homelab and the procedures for operational recovery.
|
||||
|
|
@ -35,6 +25,25 @@ This document defines the lifecycle of a service in the homelab and the procedur
|
|||
- `docker compose down`.
|
||||
- Archive `/opt/homelab/data/<service>` if necessary.
|
||||
|
||||
## Operational Recovery
|
||||
|
||||
### 1. Container Failure
|
||||
If a service is unhealthy:
|
||||
- Check `docker compose logs`.
|
||||
- Restart: `docker compose restart`.
|
||||
- Recreate: `docker compose up -d --force-recreate`.
|
||||
|
||||
### 2. Node Failure
|
||||
If a host node fails:
|
||||
- Services with `owner_node` matching the failed node must be recovered on a backup node or the node must be restored.
|
||||
- Persistence data must be restored from backups to `/opt/homelab/data/<service>`.
|
||||
|
||||
### 3. Dependency Recovery
|
||||
If a dependency fails:
|
||||
- Services depending on it might report unhealthy status.
|
||||
- Recover the dependency first.
|
||||
- Re-verify dependent services.
|
||||
|
||||
## Persistent Data Conventions
|
||||
|
||||
- **Data**: `/opt/homelab/data/<service>` - Primary persistent state.
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: deprecated
|
||||
updated: 2026-04-15
|
||||
links: []
|
||||
superseded_by: "kb/subsystems/fleet-inventory.md + hosts/<node>/capabilities.yaml (stub z 2026-04-15, sprzed floty)"
|
||||
---
|
||||
|
||||
# Networking
|
||||
|
||||
## Description
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: runbook
|
||||
visibility: public
|
||||
status: active
|
||||
updated: 2026-05-12
|
||||
links: []
|
||||
---
|
||||
|
||||
# Node Onboarding Workflow
|
||||
|
||||
This document describes the process of onboarding a new Linux machine into the homelab platform.
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: public
|
||||
status: active
|
||||
updated: 2026-06-17
|
||||
links: []
|
||||
---
|
||||
|
||||
# Observer Runtime
|
||||
|
||||
The Observer Runtime is a lightweight agent responsible for synthesizing the operational world state of the homelab from raw events, logs, and state files.
|
||||
65
docs/questions.md
Normal file
65
docs/questions.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Unknowns and Clarification Questions
|
||||
|
||||
## Description
|
||||
|
||||
This page lists information that is missing or unclear from the current homelab documentation.
|
||||
|
||||
## Current configuration
|
||||
|
||||
The currently documented configuration is limited to:
|
||||
|
||||
- Raspberry Pi 5 as the main server.
|
||||
- Docker, Portainer, and Nginx Proxy Manager as the core stack.
|
||||
- NAT with forwarded ports:
|
||||
- `80-81` to `4480-4481`
|
||||
- `443` to `4443`
|
||||
- Public access through Nginx Proxy Manager with Let's Encrypt HTTPS.
|
||||
- Private access through Tailscale.
|
||||
- Hetzner VPS handoff:
|
||||
- Hostname: `ubuntu-4gb-hel1-1`
|
||||
- Tailscale IP: `100.95.58.48`
|
||||
- Public IPv4: `135.181.153.108`
|
||||
- Public IPv6: `2a01:4f9:c014:98f0::1`
|
||||
- Running container: `npm`
|
||||
- Joplin files created but not running.
|
||||
|
||||
## Known facts
|
||||
|
||||
- The homelab is documented only from the known facts above.
|
||||
- Anything not listed as known remains unconfirmed.
|
||||
|
||||
## Unknown / needs clarification
|
||||
|
||||
1. What operating system and version is running on the Raspberry Pi 5?
|
||||
2. What is the Raspberry Pi 5 RAM size?
|
||||
3. What storage devices are used, and where is persistent service data stored?
|
||||
4. What is the Raspberry Pi 5 LAN IP address?
|
||||
5. Is the Raspberry Pi 5 using DHCP or a static IP address?
|
||||
6. What router or firewall performs NAT and port forwarding?
|
||||
7. Is the WAN IP static, dynamic, or behind CGNAT?
|
||||
8. Does external port `80` map to internal port `4480`, and does external port `81` map to internal port `4481`?
|
||||
9. Are the forwarded ports TCP only, UDP only, or both?
|
||||
10. Are any other ports forwarded?
|
||||
11. What domain names or subdomains point to the homelab?
|
||||
12. What are the Nginx Proxy Manager proxy hosts?
|
||||
13. Which services are public, and which are private-only?
|
||||
14. Is HTTP-to-HTTPS redirection enabled in Nginx Proxy Manager?
|
||||
15. Are Nginx Proxy Manager access lists used?
|
||||
16. How are Docker, Portainer, and Nginx Proxy Manager deployed?
|
||||
17. Are Docker Compose files, Portainer stacks, or other manifests available?
|
||||
18. What containers are currently running?
|
||||
19. What Docker networks and volumes exist?
|
||||
20. What is the Tailscale device name for the Raspberry Pi 5?
|
||||
21. Does the Raspberry Pi 5 advertise Tailscale subnet routes?
|
||||
22. Is the Raspberry Pi 5 configured as a Tailscale exit node?
|
||||
23. Is Tailscale SSH enabled?
|
||||
24. What backup system exists, if any?
|
||||
25. What monitoring or alerting exists, if any?
|
||||
26. Is the Hetzner VPS part of the homelab documentation scope, a separate system, or both?
|
||||
27. What is the operating system version on `ubuntu-4gb-hel1-1`?
|
||||
28. Is public Nginx Proxy Manager admin access on port `81` intentionally reachable on `135.181.153.108`?
|
||||
29. Has DNS record `joplin.okit.pl -> 135.181.153.108` been created?
|
||||
30. Has optional AAAA record `joplin.okit.pl -> 2a01:4f9:c014:98f0::1` been created?
|
||||
31. Has `POSTGRES_PASSWORD=CHANGE_ME_STRONG_PASSWORD` been changed before first Joplin production start?
|
||||
32. Has the Nginx Proxy Manager proxy host for `joplin.okit.pl` been created?
|
||||
33. Are ports `80` and `443` publicly reachable on the Hetzner VPS for Let's Encrypt HTTP validation?
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: public
|
||||
status: active
|
||||
updated: 2026-05-11
|
||||
links: []
|
||||
---
|
||||
|
||||
# Service Model and Healthchecks
|
||||
|
||||
This document defines the normalized service model for the homelab.
|
||||
|
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: subsystem
|
||||
visibility: private
|
||||
status: deprecated
|
||||
updated: 2026-04-15
|
||||
links: []
|
||||
superseded_by: "kb/subsystems/fleet-inventory.md + hosts/<node>/capabilities.yaml (stub z 2026-04-15, sprzed floty)"
|
||||
---
|
||||
|
||||
# Services
|
||||
|
||||
## Description
|
||||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-05-27
|
||||
links: []
|
||||
---
|
||||
|
||||
# SESSION: Budowa planner-agent — LLM-based diagnostics
|
||||
|
||||
**DATA:** 2026-05-27
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-05-27
|
||||
links: []
|
||||
---
|
||||
|
||||
# SESSION: Stabilizacja systemu wieloagentowego homelabu
|
||||
|
||||
**DATE:** 2026-05-27
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-09
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-08 — onboarding LUSTRO (RPi4 / Magic Mirror / KEN)
|
||||
|
||||
## Cel
|
||||
|
|
@ -90,7 +81,7 @@ przez Tailscale działa bezhasłowo. Verify czysty (arch=aarch64).
|
|||
|
||||
## Learnings
|
||||
|
||||
(odzwierciedlone też w `kb/runbooks/node-onboarding-tool.md`)
|
||||
(odzwierciedlone też w `scripts/onboard/README.md`)
|
||||
|
||||
- mDNS `.local` zawodny do automatyzacji → `first_contact` przez IP lub tailscale, nie `.local`
|
||||
- istniejący node z userem uid=1000: użyj go zamiast tworzyć `oskar` (kolizja uid)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-09
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-09 — flota recovery + LUSTRO register
|
||||
|
||||
## Cel
|
||||
|
|
@ -130,4 +121,4 @@ Docelowo: osobny worktree per task.
|
|||
|
||||
## Tech-debt złapany w sesji
|
||||
|
||||
→ wpisany do `kb/phases/backlog.md`
|
||||
→ wpisany do `docs/backlog.md`
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-11
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-10/11 — lustro SSH shipping fix + ha-diag-agent piha
|
||||
|
||||
## Cel
|
||||
|
|
@ -79,13 +70,13 @@ solaria / piha / chelsty to wciąż **stare root kontenery** node-agenta
|
|||
(piha Created 2026-05-27, uid 0). Ich mount `/root/.ssh` działa tylko dlatego,
|
||||
że kontenery są sprzed `user: "1000:1000"`. Pierwszy `--force-recreate` / reboot
|
||||
hosta / update obrazu przełączy je na uid 1000 i shipping padnie jak na lustrze.
|
||||
**NIE RECREATE bez fixu.** Szczegóły i fix: `kb/phases/backlog.md`.
|
||||
**NIE RECREATE bez fixu.** Szczegóły i fix: `docs/backlog.md`.
|
||||
|
||||
---
|
||||
|
||||
## Tech-debt złapany w sesji
|
||||
|
||||
→ wpisany do `kb/phases/backlog.md` (flota-bomba, ha-diag-agent blocked,
|
||||
→ wpisany do `docs/backlog.md` (flota-bomba, ha-diag-agent blocked,
|
||||
poison-quarantine review, `--omit-dir-times`, stale komentarz node_agent.py,
|
||||
shipping success na `logger.debug`, event-bloat lustro na VPS).
|
||||
|
||||
|
|
@ -96,8 +87,8 @@ fa59625 docs(ha-diag-agent): replace curl verify commands with docker exec
|
|||
d7e0d31 fix(ha-diag-agent): remove host port mapping for 8087
|
||||
|
||||
### Files changed
|
||||
kb/runbooks/ha-diag-agent-deploy.md | 4 ++--
|
||||
kb/services/ha-diag-agent.md | 4 ++--
|
||||
services/ha-diag-agent/DEPLOY.md | 4 ++--
|
||||
services/ha-diag-agent/README.md | 4 ++--
|
||||
services/ha-diag-agent/docker-compose.yml | 3 ---
|
||||
services/ha-diag-agent/service.yaml | 3 ---
|
||||
4 files changed, 4 insertions(+), 10 deletions(-))
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-17
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-17 — KB foundations (etap 1 maili)
|
||||
|
||||
## Cel
|
||||
|
|
@ -120,14 +111,14 @@ services/kb-postgres/service.yaml
|
|||
services/kb-postgres/env.example
|
||||
services/kb-postgres/healthcheck.sh
|
||||
services/kb-postgres/init/001_envelope.sql
|
||||
kb/services/kb-postgres.md
|
||||
services/kb-postgres/README.md
|
||||
hosts/solaria/runtime/kb-postgres/docker-compose.override.yml
|
||||
hosts/solaria/services.yaml
|
||||
inventory/topology.yaml
|
||||
packages/kb-mail/pyproject.toml
|
||||
packages/kb-mail/src/kb_mail/{__init__,envelope,db,archive}.py
|
||||
packages/kb-mail/tests/{conftest,test_envelope,test_archive,test_db,test_migration}.py
|
||||
kb/subsystems/kb-overview.md (etap 1 done, konwencja packages/)
|
||||
docs/kb/kb-00-overview.md (etap 1 done, konwencja packages/)
|
||||
CLAUDE.md (sekcja Shared Python Libraries)
|
||||
docs/sessions/2026-06-17-kb-foundations.md
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-17
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-17 — Vikunja OIDC+GitOps · Observer heartbeat-TTL · panel-source
|
||||
|
||||
## Zrobione i wdrożone
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-22
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-22 — KB spine relokowany na PIHA + przygotowanie hosta
|
||||
|
||||
## Cel
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-22
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-22 — decyzja: Prometheus jako źródło prawdy dla liveness floty
|
||||
|
||||
## Cel
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-24
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-24 — KB etap 2: importer Gmail (kod gotowy)
|
||||
|
||||
## Cel
|
||||
|
|
@ -171,7 +162,7 @@ jobs/gmail-bulk-import/pyproject.toml
|
|||
jobs/gmail-bulk-import/tests/test_importer.py
|
||||
hosts/piha/capabilities.yaml
|
||||
.gitignore
|
||||
kb/subsystems/kb-overview.md (etap 2 gotowy, konwencja jobs/)
|
||||
kb/subsystems/kb-mail-pillar.md (§8 krok 2 = kod gotowy)
|
||||
docs/kb/kb-00-overview.md (etap 2 gotowy, konwencja jobs/)
|
||||
docs/kb/kb-01-email-design.md (§8 krok 2 = kod gotowy)
|
||||
docs/sessions/2026-06-24-kb-gmail-importer.md
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-24
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-24 — fleet-prometheus etap 1: scaffold + deploy-node hostname fix
|
||||
|
||||
## Cel
|
||||
|
|
@ -108,7 +99,7 @@ docker compose \
|
|||
|
||||
---
|
||||
|
||||
## Nowe tech-debty (dodane do `kb/phases/backlog.md`)
|
||||
## Nowe tech-debty (dodane do `docs/backlog.md`)
|
||||
|
||||
1. **Rozjazd stanu Docker Compose na VPS** — serwisy `node-agent`, `control-plane` i inne
|
||||
stworzone innym `project-name` niż `deploy-node.sh` oczekuje; Recreate pada na stale
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-26
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-25 — KB etap 2: bulk import Gmail uruchomiony
|
||||
|
||||
## Cel
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-25
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-25 — fleet-prometheus etap 1: uruchomienie na VPS + incydent mózgu
|
||||
|
||||
## Cel
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-26
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-26 — fleet-prometheus etap 2: targety floty + zamknięcie buga deploy.sh vps
|
||||
|
||||
## Cel
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-30
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-30 — Inwentaryzacja floty + gaszenie dysku SATURN
|
||||
|
||||
## Cel
|
||||
|
|
@ -19,7 +10,7 @@ architekturą dokumentów KB. Start od weryfikacji stanu faktycznego wszystkich
|
|||
- CC (Sonnet 4.6) w worktree `fleet-inventory` zebrał stan faktyczny (docker ps +
|
||||
free/df/nproc) z 4 dostępnych nodów: PIHA, VPS, SOLARIA, SATURN. LUSTRO+CHELSTY
|
||||
offline (timeout :22) -> oznaczone UNREACHABLE.
|
||||
- Wynik: `kb/subsystems/fleet-inventory.md` — 23 zpriorytetyzowane rozjazdy.
|
||||
- Wynik: `docs/infra/inventory-2026-06-30.md` — 23 zpriorytetyzowane rozjazdy.
|
||||
- Kluczowe ustalenia:
|
||||
- **forgejo** biega na PIHA (always-on), ale `service.yaml owner_node=saturn` — rozjazd
|
||||
- **mosquitto** biega na VPS, `service.yaml owner=piha`, na PIHA go nie ma
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-30
|
||||
links: []
|
||||
---
|
||||
|
||||
# 2026-06-30 — Migracja kapala.org → Cloudflare + wildcard DNS-01, HA i Immich na mesh
|
||||
|
||||
## Cel
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-06-30
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-06-30 — fleet-prometheus liveness: reguły NodeDown + wpięcie watchdog→Prometheus
|
||||
|
||||
## Cel
|
||||
|
|
|
|||
|
|
@ -1,16 +1,7 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-02
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-02 — Modul 0 (odchudzenie PIHA) + migracja Forgejo/Vikunja na kapala.org
|
||||
|
||||
## Modul 0 kb-02 — WYKONANY (faza 1 audyt + faza 2 egzekucja)
|
||||
- Audyt CC (read-only): kb/audits/piha-slim-2026-07-02.md
|
||||
- Audyt CC (read-only): docs/infra/piha-slim-audit-2026-07-02.md
|
||||
- Review Oskara skorygowal audyt: llm-gateway = WLASNY kod (FastAPI-router LLM,
|
||||
/opt/llm-gateway, proxy do Ollama@SOLARIA) — NIE martwy; immich MUSI byc 24/7
|
||||
na PIHA (SOLARIA sesyjna) — rekomendacja przeniesienia wykreslona.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-02
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-02 — recon-weryfikacja inwentaryzacji + rozbrojenie trzech min
|
||||
|
||||
## Cel
|
||||
|
|
@ -21,7 +12,7 @@ Sesja tylko-recon + minimalne fixy; bez deployów nowych feature'ów.
|
|||
|
||||
### Recon-weryfikacja inwentaryzacji floty (commit `57a6dff`, read-only)
|
||||
|
||||
Wynik: `kb/subsystems/fleet-inventory-verify.md`.
|
||||
Wynik: `docs/infra/inventory-verify-2026-07-02.md`.
|
||||
|
||||
**Bilans 23 rozjazdów z audytu 2026-06-30**:
|
||||
- **20 wciąż aktualnych** — nic się samo nie naprawiło.
|
||||
|
|
@ -100,7 +91,7 @@ Po jednej linii per plik; `owner_node` nie występował nigdzie indziej w repo.
|
|||
nie rezolwuje z SOLARII; brak formalnego override mem_limit fleet-prometheus
|
||||
w `hosts/vps/runtime/` (siedzi w bazowym compose — kosmetyka).
|
||||
|
||||
Wpisy dodane do `kb/phases/backlog.md` w tej sesji.
|
||||
Wpisy dodane do `docs/backlog.md` w tej sesji.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-06
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-06 — cutover liveności na Prometheus: recon starego toru + Etap 0 udowodniony w boju
|
||||
|
||||
## Cel
|
||||
|
|
@ -22,7 +13,7 @@ Prometheus → brain-watchdog → Telegram, którego brakowało od 2026-06-30.
|
|||
|
||||
### Recon cutoveru — wmergowany (commit `d94bb38`)
|
||||
|
||||
Wynik: `kb/audits/prometheus-cutover-2026-07-06.md` (517 linii, read-only,
|
||||
Wynik: `docs/infra/prometheus-cutover-recon-2026-07-06.md` (517 linii, read-only,
|
||||
zero zmian w kodzie). Kluczowe ustalenia:
|
||||
|
||||
- **Cutover to podmiana klasyfikacji liveności w JEDNYM miejscu** —
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-07
|
||||
links: []
|
||||
---
|
||||
|
||||
# Session log 2026-07-07 — Immich upload fix / pimain cleanup (kontynuacja 2026-07-03)
|
||||
|
||||
## Kontekst
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-07
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-07 — okit.pl Faza 1 (wildcard cert) + przepiecie 9 hostow
|
||||
|
||||
## Kontekst
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-09
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-09 — KB configi (9 decyzji) + wzorzec dzielenia plikow (Nextcloud twierdza + Gokapi)
|
||||
|
||||
## Kontekst
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-10
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-10 — Deploy 1 Paperless (DZIALA) + swap PIHA + npm-API tool w akcji
|
||||
|
||||
## Glowne osiagniecie: Paperless serwis LIVE
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-12
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-12 — Deploy 2: split-host OCR-worker (DZIALA) + decyzja kierunku KB
|
||||
|
||||
## Deploy 2 — OCR-worker na SOLARIA przez NFS: DZIALA end-to-end
|
||||
|
|
@ -46,7 +37,7 @@ maila = ta sama encja, DOWOD zasady kb-00 #7), (3) interfejs pytan (RAG) — pie
|
|||
realnej uzytecznosci. Dopiero POTEM dopelniac importy (reszta Takeout, zdjecia, transakcje).
|
||||
|
||||
## TODO nastepne
|
||||
- MODUL 5 (koperta + ingest + embeddingi + cross-source) — kb/phases/kb-m5-documents-ingest.md
|
||||
- MODUL 5 (koperta + ingest + embeddingi + cross-source) — docs/kb/modules/05-documents-ingest.md
|
||||
- Import probki zalacznikow z maili (kilkaset, nie 70k) — do zbudowania RAG
|
||||
- Interfejs pytan / RAG — warstwa uzytkowa
|
||||
- Deploy 3 (Nextcloud), Deploy 4 (Gokapi) — configi gotowe, czekaja
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-15
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-15 — Prometheus cutover Etap 2 (analiza GO) + domknięcie rodziny bugów event-pipeline/checkpoint
|
||||
|
||||
## Kontekst
|
||||
|
|
@ -27,12 +18,12 @@ parsowany z nazwy `evt-<node>-<ts>-...` (fallback mtime, **nigdy 0** dla
|
|||
istniejącego pliku — 0 = leksykalne "starszy niż checkpoint" = dokładnie ten
|
||||
poison), migracja starych path-checkpointów przy starcie. Zdeployowany na VPS
|
||||
(observer `StartedAt` 07-14). Zweryfikowany dziś jako kompletny i zdeployowany.
|
||||
Szczegóły: `kb/phases/backlog.md` (sekcja "Bug: checkpoint observera po ścieżce
|
||||
Szczegóły: `docs/backlog.md` (sekcja "Bug: checkpoint observera po ścieżce
|
||||
leksykalnej").
|
||||
|
||||
### 2. docs(infra) analiza Etapu 2 shadow-run (Fable, `8fec62d`)
|
||||
|
||||
`kb/phases/prometheus-cutover-etap2.md` — 165 mismatchy
|
||||
`docs/infra/prometheus-shadow-etap2-analiza-2026-07-15.md` — 165 mismatchy
|
||||
`SHADOW_LIVENESS_MISMATCH` solaria/lustro w dobie 2026-07-14 WYJAŚNIONE: dwa
|
||||
nocne wyłączenia węzłów (lustro 21:30 UTC — regularny power-off, solaria
|
||||
21:34 UTC). Wzorzec `event=fresh prom=down` to **nie** "żywy węzeł niewidziany
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-16
|
||||
links: []
|
||||
---
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -31,7 +22,7 @@ links: []
|
|||
|
||||
### 1. RECON lustro shipping (Fable, commit `542bba4`)
|
||||
|
||||
`kb/audits/lustro-shipping-2026-07-16.md` — 1507 mismatchy `lustro event=dead prom=up` w trwałym logu WYJAŚNIONE: (a) wczorajszy kontrolowany test (node-agent stał 3h20m, nie 15 min jak zakładano) + (b) poranny boot-race 56s. **Werdykt: shipping lustro działa, ZERO recurring problemu.** Prometheus 0 pomyłek w 48h — wzmacnia rekomendację GO dla Etapu 3 cutoveru.
|
||||
`docs/infra/lustro-shipping-recon-2026-07-16.md` — 1507 mismatchy `lustro event=dead prom=up` w trwałym logu WYJAŚNIONE: (a) wczorajszy kontrolowany test (node-agent stał 3h20m, nie 15 min jak zakładano) + (b) poranny boot-race 56s. **Werdykt: shipping lustro działa, ZERO recurring problemu.** Prometheus 0 pomyłek w 48h — wzmacnia rekomendację GO dla Etapu 3 cutoveru.
|
||||
|
||||
Znaleziska poboczne: lustro biega na obrazie sprzed 5 tyg (deploy-node bez `--build` — patrz fix #2 niżej); fake-hwclock boot-race (RPi bez RTC — pierwszy event po boocie ma stary stempel, dropnięty przez timestamp checkpoint).
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-20
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-17/18 — KB faza 3: kroki 2-5 DOMKNIĘTE
|
||||
|
||||
## Krok 2 — migracja 004 + pilot streszczeń A/B (17.07)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-22
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-17…21 — wątek KB: faza 3 kroki 3-5 DONE (+ incydenty)
|
||||
|
||||
**Krok 3 — kaskada summary→chunk:** bramka **PASS at N=10, k=5** (N-sweep {1..20}: N=5 to zmierzona podłoga, N=10 niesie 2× margines); kaskada nie degraduje niczego, na 186 dok nie poprawia (test architektury pod skalę mailową, zgodnie z przewidywaniem planu); koszt +1 SQL, zero dodatkowych embedów. cascade_query = domyślna ścieżka kb-query; flat_query zostaje jako baseline. Eval przepisany do wersjonowanego eval/queries.yaml + retrieval_eval.py.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-22
|
||||
links: []
|
||||
---
|
||||
|
||||
# 2026-07-22 — HA: incydent dwóch mózgów, cutover ken, archiwum legacy
|
||||
|
||||
## Odkrycie
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-22
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-22 — KB faza 4: recon + kroki 1-2 (kb-query LIVE na PIHA)
|
||||
|
||||
**Recon+plan fazy 4** (05-faza4-plan.md, 603 linie): D1 wydzielenie packages/kb-retrieval (documents-ingest ciągnie anthropic+CLI — nie do obrazu serwisu); D2 fallback embed z pełną maszyną stanów, ale gate'owany kalibracją RAM na żywym PIHA (audyt nieaktualny, ~3.8Gi zajęte — plan daje kryteria i alternatywę: jawna degradacja 503 zamiast łamania inwariantu modelu); OIDC wbudowane w apkę (authlib, wzorzec repo — nigdy forward-auth); gmail: envelope_id już JEST Message-ID.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-23
|
||||
links: []
|
||||
---
|
||||
|
||||
# 2026-07-22/23 — Control-plane: dziura w operator_ui + pierwszy pełny cykl remediacji bez SSH
|
||||
|
||||
Zamknięcie wielosesyjnego wątku "czemu mózg nie leczy floty". Dwa dni pracy,
|
||||
|
|
@ -70,7 +61,7 @@ approval → executor → node-agent → docker restart → completed.
|
|||
test E2E padł: agent rzucał `[Errno 13] Permission denied:
|
||||
/opt/homelab/actions/dispatch` co cykl. Root cause to ZNANY, POWRACAJĄCY
|
||||
(już 4. raz — patrz sekcja "Tech-debt: globalny porządek uid/gid/uprawnień
|
||||
we flocie" w `kb/phases/backlog.md`) motyw
|
||||
we flocie" w `docs/backlog.md`) motyw
|
||||
uid/gid na PIHA: oskar ma uid 1004, kontener agenta biega jako uid 1000
|
||||
(= user `pi` na hoście). `/opt/homelab/actions` było `oskar:oskar
|
||||
drwxr-xr-x` (utworzone w maju), podczas gdy DZIAŁAJĄCY wzorzec to
|
||||
|
|
@ -111,7 +102,7 @@ approval → executor → node-agent → docker restart → completed.
|
|||
Pierwszy w historii systemu pełny cykl remediacji end-to-end potwierdzony w
|
||||
produkcji (PIHA), bez SSH z control-plane do węzłów. Publiczna dziura
|
||||
autoryzacji na `operator_ui.py:18180` zamknięta (bind ograniczony do
|
||||
Tailscale). Otwarte follow-upy — patrz `kb/phases/backlog.md` (retry-w-nieskończoność
|
||||
Tailscale). Otwarte follow-upy — patrz `docs/backlog.md` (retry-w-nieskończoność
|
||||
zepsutego JSON, uprawnienia `actions/` na innych węzłach, brak twardego checka
|
||||
`.env`/`TAILSCALE_BIND_IP` w `deploy-local.sh`, brak autoryzacji w
|
||||
`operator_ui.py`, zapchana approval queue przez `alert_only`, brak
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-23
|
||||
links: []
|
||||
---
|
||||
|
||||
# 2026-07-22/23 — HA: adapter api, import ken, deploy.sh, otwarcie fazy 1
|
||||
|
||||
## Wykonane
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-23
|
||||
links: []
|
||||
---
|
||||
|
||||
# 2026-07-23 (cd.) — HA: klima E2E, audyt Fable, fix-pack 1
|
||||
|
||||
## Klima salonowa — pierwsza automatyzacja LLM przez repo (E2E)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,6 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-23
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-23 — KB faza 4: ingress kb.kapala.org (krok 5/§8)
|
||||
|
||||
**Zakres**: wyłącznie ingress (`kb/phases/kb-m5-faza4.md` §8, krok 5) —
|
||||
**Zakres**: wyłącznie ingress (`docs/kb/modules/05-faza4-plan.md` §8, krok 5) —
|
||||
frontend i `/search` już LIVE na PIHA (port 8230) od sesji 2026-07-22. Zero
|
||||
zmian w kodzie kb-query w tej sesji.
|
||||
|
||||
|
|
@ -70,8 +61,8 @@ w tej samej sesji, osobnym przebiegiem po zgłoszeniu przez operatora:
|
|||
Potwierdzone w repo (zgodnie z `05-faza4-plan.md` §1.4): **brak wzorca
|
||||
forward-auth/reverse-proxy-level auth** — NPM community edition go nie ma
|
||||
(sprawdzone: brak `oauth2-proxy`/`authelia`/`forward_auth` w kodzie repo poza
|
||||
wzmiankami "przyszła opcja" w `kb/subsystems/kb-documents-pillar.md` i
|
||||
`kb/nodes/vps.md`). Wszystkie 3 precedensy (paperless/nextcloud/vikunja)
|
||||
wzmiankami "przyszła opcja" w `docs/kb/kb-02-documents-design.md` i
|
||||
`hosts/vps/README.md`). Wszystkie 3 precedensy (paperless/nextcloud/vikunja)
|
||||
robią OIDC **wewnątrz aplikacji**. kb-query nie ma dziś żadnego logowania.
|
||||
|
||||
Zgodnie z instrukcją zadania: **nie budowano** nowego komponentu auth.
|
||||
|
|
@ -122,7 +113,7 @@ username collision, `docs/sessions/2026-07-10-paperless-deploy.md`).
|
|||
|
||||
## Pliki repo zmienione
|
||||
|
||||
- `kb/services/kb-query.md` — sekcja "Ingress" (co żyje, co nie, dlaczego
|
||||
- `services/kb-query/README.md` — sekcja "Ingress" (co żyje, co nie, dlaczego
|
||||
auth odłożone) zastępuje starą notatkę "not wired up yet".
|
||||
- `docs/sessions/2026-07-23-kb-f4-ingress.md` — ten dokument.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-27
|
||||
links: []
|
||||
---
|
||||
|
||||
# 2026-07-27 — HA: legacy zamkniete, kasacje, pimirror, sonda Zigbee
|
||||
|
||||
## Wykonane
|
||||
- Klima: 4 dni autonomii bez interwencji (sunset shutdown 26.07 21:05
|
||||
zadzialal naturalnie; dzis poprawny brak startu — prog 27 > salon 24.4,
|
||||
fix faa2e2a dziala w praktyce).
|
||||
- Legacy: kontener homeassistant5 juz nie istnial (podejrzenie: cleanup
|
||||
policy node-agenta — do wyjasnienia kiedys; kolizja "stop bez rm dla
|
||||
rollbacku" vs auto-cleanup). Katalog /home/pi/homeassistant nietkniety
|
||||
(config z 22.07). Strata zerowa — archiwum w gicie.
|
||||
- Kasacje pkt 10 audytu przez API (6 automatyzacji: para Tymka, powitanie
|
||||
test, notify router, para prototyp) + drift commit 36b43e5. Incydent:
|
||||
petla DELETE odpalona z placeholderami ID1..ID6 przed identyfikacja —
|
||||
bez szkod (404), lekcja: operator nie podstawia, tor przez repo
|
||||
(deploy.sh --delete w backlogu).
|
||||
- task/ha-porzadki (09624e0): pimirror graceful shutdown odtworzony
|
||||
z ken-legacy (23:28 graceful przed twardym 23:35; legacy button
|
||||
unavailable — przepisane na pimirror2 po registry, entity_id wg nowej
|
||||
konwencji), sekcja "Konwencje automatyzacji" w DESIGN.md, backlog:
|
||||
deploy.sh --delete, trigger na zmiane tolerancji klimy. Deploy 1/0/0.
|
||||
|
||||
## Sonda Zigbee (read-only) — diagnoza awarii "2026-07-17"
|
||||
last_changed w HA bezuzyteczne po restarcie (277 encji ze stemplem 26.07 =
|
||||
odcisk restartu, nie fala). Log z2m: tydzien bez przejsc offline/online
|
||||
(padly wczesniej). Availability z MQTT (retained): ~20 urzadzen offline,
|
||||
w tym WSZYSTKIE TRZY ROUTERY (routerIKEA/Salon/Sypialnia) + urzadzenia
|
||||
sieciowe (ledTV, zbLampkiRegal, zbSwitchBlatZasilanie, listwy LED).
|
||||
Wniosek: to nie baterie i nie koordynator — urzadzenia zasilane sieciowo
|
||||
sa fizycznie bez pradu, mesh sie zapadl kaskadowo (bateryjne koncowki
|
||||
poza zasiegiem: mdHeli, thHeli, mdSypialnia, mdUbikacja, mdWejscie,
|
||||
waterLeakWc, 4button, heaterLazienkaTRV07). Geografia strat = dziury po
|
||||
routerach.
|
||||
|
||||
## Nastepne kroki
|
||||
1. FIZYCZNIE: sprawdzic zasilanie 3 routerow + wtyczek LED; po powrocie
|
||||
odczekac dobe; re-pairing tylko dla tego, co nie wroci samo. Przy
|
||||
okazji: zasilanie puryfikatorow zhimi (WiFi, osobny tor xiaomi_miot).
|
||||
2. Po odbudowie mesh: ponowna sonda availability + drift/re-import;
|
||||
duplikat mdwejscie/mdWejscie do sprzatniecia w z2m.
|
||||
3. Wieczorem 23:28: pierwszy zywy test pimirror graceful shutdown (trace).
|
||||
4. Nastepna sesja: projekt Fable "tryby domu" (night/sleep/empty/on_leave)
|
||||
— prompt gotowy w historii; TRV guard ~09; przepiecie ha-diag-agent.
|
||||
|
||||
## Dogrywka: zigbee.okit.pl -> zigbee.kapala.org (mesh-only)
|
||||
|
||||
Runtime (poza gitem, log tutaj): npm@PIHA proxy host #36
|
||||
(zigbee.kapala.org -> 192.168.31.5:8087, cert #49 wildcard, websocket ON,
|
||||
advanced puste — pulapka kapala.org), przez scripts/npm/npm_api.py
|
||||
(dry-run -> --apply). Pi-hole custom.list: 192.168.31.5 zigbee.kapala.org
|
||||
(split-horizon jak kb). Cloudflare kapala.org: A zigbee -> 100.108.208.3
|
||||
(Tailscale piha, DNS only — wzorzec forgejo). Test: dig OK, curl 200.
|
||||
Sprzatniecie: rekord zigbee.okit.pl usuniety z Cloudflare, vhost z NPM@VPS.
|
||||
Frontend z2m bez auth_token — publiczna ekspozycja na okit.pl [TODO operator:
|
||||
status auth przed migracja + ew. przeglad access logow NPM@VPS].
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-30
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-07-27 — KB faza 4: fallback embed SOLARIA→PIHA (krok 3, ostatni element rdzenia)
|
||||
|
||||
> **Dopisek redakcyjny (2026-07-30, dedup — `kb/phases/kb-m5-faza4-fallback-dedup.md`):**
|
||||
> implementacja kodu z tej sesji (`app/fallback.py`, branch `task/kb-f4-fallback`, 3d4ee38)
|
||||
> została **porzucona** — do mastera weszła równoległa, szersza implementacja tego samego
|
||||
> kroku planu (e7625cd, `app/embed_router.py`, 2026-07-29) i to ona biega na PIHA. Ten log
|
||||
> wciągnięto do repo, bo dokumentuje fakty operacyjne niezależne od porzuconego kodu:
|
||||
> znalezisko i wyłączenie osieroconego natywnego `ollama.service` na PIHA (§3, z backlogiem
|
||||
> odinstalowania ≈2026-08-10), kalibrację live ollama-piha z werdyktem GO (§4 — konfiguracja
|
||||
> kontenera identyczna na masterze, pomiar przenosi się) oraz metodologię i baseline bramki
|
||||
> §9 (§5, Δ~3e-4). Wyniki bramki i testu sol-down dotyczyły kodu z brancha — na wdrożonym
|
||||
> masterze wymagają powtórki (raport dedup, follow-up (b)). Sekcje o deployu (§6) i
|
||||
> "Do zrobienia przez operatora" pkt 1–2 opisują stan sprzed merge'a e7625cd — historyczne.
|
||||
> Z delty brancha uratowano ponadto: `retrieval_eval.py --transport http` (plan §2 D6/§9)
|
||||
> i luki testowe T1/T2 przeniesione do `test_embed_router.py`.
|
||||
|
||||
**Zakres**: `kb/phases/kb-m5-faza4.md` §2 decyzja 2 / §5 — aktywny fallback
|
||||
embedu, ostatni brakujący element rdzenia fazy 4 (frontend i ingress LIVE od
|
||||
2026-07-22/23, `docs/sessions/2026-07-23-kb-f4-ingress.md`). Zero zmian w schemacie
|
||||
DB, zero zmian w `kb_retrieval`'s retrieval logice — wyłącznie warstwa embed + health.
|
||||
|
||||
Praca wykonana w task worktree (`task/kb-f4-fallback`, `.claude/skills/worktree-aware`).
|
||||
Zgodnie z ustaleniem na starcie sesji (patrz "Ustalenia proceduralne" niżej): kod
|
||||
napisany i przetestowany lokalnie w worktree, produkcyjne kroki (kalibracja, deploy,
|
||||
live-test) wykonane po jawnej zgodzie operatora, z osobnym potwierdzeniem przed
|
||||
każdym kolejnym krokiem dotykającym PIHA/SOLARIĘ.
|
||||
|
||||
## Ustalenia proceduralne
|
||||
|
||||
Zadanie wprost wymagało kroków produkcyjnych (kalibracja RAM/latencji na żywym PIHA,
|
||||
symulacja sol-down dotykająca SOLARII, deploy, push) — sprzeczne z ogólną dyscypliną
|
||||
`worktree-aware` ("nigdy nie uruchamiaj deployów/healthchecków przeciw produkcji z
|
||||
worktree"). Zamiast rozstrzygać to samodzielnie, zapytano operatora:
|
||||
1. Recon read-only (bez zmian stanu) — zgoda bez pytania.
|
||||
2. Właściwe kroki produkcyjne (kalibracja, deploy, live-test, push) — operator
|
||||
potwierdził jawnie ("Yes, proceed with all of it") po zobaczeniu pełnego zakresu.
|
||||
|
||||
## 1. Kod (warstwa embed + health, zero zmian retrievalu/DB)
|
||||
|
||||
- **`packages/kb_retrieval/embed.py`**: `embed_chunk` dostał opcjonalny `timeout_s`
|
||||
(domyślnie `None`, zero zmiany zachowania istniejących wołań) — potrzebny do
|
||||
twardego 3 s timeoutu na nodze SOLARIA bez zmiany zachowania nogi PIHA.
|
||||
- **`services/kb-query/app/fallback.py`** (nowy): `SolCircuitBreaker` (cache 30 s,
|
||||
zegar wstrzykiwalny do testów) + `resolve_sol_status` (probe `/api/tags`, 500 ms) +
|
||||
`embed_with_fallback` (SOLARIA z twardym 3 s timeoutem → jednorazowe przełączenie na
|
||||
PIHA **w tym samym requeście** przy timeout/błędzie → PIHA bez dodatkowego
|
||||
timeoutu). Dokładnie maszyna stanów z planu §2 decyzja 2.
|
||||
- **`app/search.py`**: `run_search` liczy embedding raz przez `embed_with_fallback`,
|
||||
potem woła `flat_retrieve`/`cascade_retrieve`/`hybrid_retrieve` (niskopoziomowe
|
||||
funkcje `kb_retrieval`, biorą gotowy wektor) zamiast `flat_query`/`cascade_query`/
|
||||
`hybrid_query` (które embedują same) — dzięki temu decyzja fallbacku żyje wyłącznie
|
||||
w warstwie HTTP kb-query, zero zmiany w `kb_retrieval`. `sol_status` w odpowiedzi to
|
||||
teraz realny wynik, nie zahardkodowane `"up"`.
|
||||
- **`app/main.py`**: `/healthz` i `/search` dzielą jeden `SolCircuitBreaker`
|
||||
(`app.state.sol_breaker`) — oba endpointy zawsze zgadzają się co do aktualnego
|
||||
stanu. Nowy env `OLLAMA_PIHA_URL` (domyślnie `http://localhost:11434` — celowo
|
||||
"inertny" placeholder, fail-closed, dopóki operator nie ustawi realnego adresu).
|
||||
- **Inwariant modelu**: **nie dodano** drugiego, per-request sprawdzenia w DB —
|
||||
`EMBED_MODEL` to jedna stała wątkowana przez obie nogi `embed_with_fallback`,
|
||||
więc startowy check (`app/startup.py`, niezmieniony) pokrywa obie ścieżki z
|
||||
konstrukcji. Dodanie drugiego DB-checka chroniłoby przed scenariuszem, który nie
|
||||
może wystąpić (CLAUDE.md: nie dodawaj walidacji dla scenariuszy, które nie mogą się
|
||||
zdarzyć) — zamiast tego nowy test (`test_both_legs_use_identical_embed_model`)
|
||||
strukturalnie dowodzi, że obie nogi w tym samym requeście dostają identyczny
|
||||
`embed_model`.
|
||||
- **`jobs/documents-ingest/eval/retrieval_eval.py`**: dodano `--transport
|
||||
{direct,http}` + `--base-url` (plan §2 decyzja 6 / §9) — dotąd nieistniejące (tylko
|
||||
ręczny smoke-test, `docs/sessions/2026-07-23-kb-f4-ingress.md` follow-up). Tryb
|
||||
`http` woła trzy `GET /search` (flat/cascade/hybrid) na żywym kb-query zamiast
|
||||
embedować+odpytywać lokalnie; `envelope.source` do kryterium 4 bierze się z pola
|
||||
`source` w odpowiedzi JSON, nie z osobnego zapytania do DB. Nie da się swipe'ować
|
||||
N przez HTTP (kb-query serwuje jeden N per request) — tryb http raportuje tylko
|
||||
przy `--gate-n`.
|
||||
|
||||
## 2. Nowy serwis `services/ollama-piha`
|
||||
|
||||
Klon wzorca `services/ollama` (`owner_node: piha` zamiast `solaria`, bez rezerwacji
|
||||
GPU — PIHA to arm64 bez akceleracji), `OLLAMA_KEEP_ALIVE=0` (model ładowany tylko na
|
||||
czas requestu). `mem_limit: 2560m` (tentatywny wg planu, potwierdzony pomiarem —
|
||||
patrz §3). Wpisany do `hosts/piha/services.yaml` (`depends_on.local` kb-query →
|
||||
`[kb-postgres, ollama-piha]`, fallback nie jest twardą zależnością na starcie).
|
||||
|
||||
## 3. Znalezisko: osierocony natywny `ollama.service` na PIHA
|
||||
|
||||
Podczas pierwszej próby deployu `ollama-piha` (bind `127.0.0.1:11434`) — konflikt
|
||||
portu. Okazało się, że PIHA ma **natywny (nie-Docker) systemd `ollama.service`**
|
||||
(v0.6.1, `enabled`, działający od 2026-06-22, PATH env wskazujący na użytkownika
|
||||
`/home/pi/...`), o którym nic nie wiadomo w repo — plan §1.2 wprost zakładał "PIHA:
|
||||
brak Ollamy", co okazało się nieaktualne/błędne. To realna sprzeczność planu z
|
||||
rzeczywistością → STOP, pytanie do operatora zamiast cichej decyzji.
|
||||
|
||||
Weryfikacja przed jakąkolwiek akcją: `journalctl -u ollama --since "7 days ago"` —
|
||||
**tylko własne, właśnie wykonane** zapytania probe (`/api/version`, `/api/tags`),
|
||||
`total blobs: 0` od startu (nigdy nic nie pobrano). Operator potwierdził: martwy
|
||||
balast, `sudo systemctl disable --now ollama.service` (**disable, nie uninstall** —
|
||||
odwracalne). Port 11434 zwolniony, `ollama-piha` wystartował normalnie.
|
||||
|
||||
**Backlog**: PIHA host-level shadow — natywny `ollama.service` wyłączony
|
||||
2026-07-27; odinstalować binarkę/unit po ~2 tygodniach jeśli nic się nie posypie.
|
||||
|
||||
## 4. Kalibracja (plan §5, gate) — **werdykt: GO**
|
||||
|
||||
Zmierzone na żywym PIHA pod normalnym obciążeniem (kb-postgres, paperless, Immich,
|
||||
HA, Forgejo działające, nie okno nocnej ciszy), 3 kolejne wywołania `/api/embeddings`
|
||||
po `ollama pull bge-m3`:
|
||||
|
||||
| Wywołanie | Latencja |
|
||||
|---|---|
|
||||
| 1 (pierwsze, zimny start) | 5.25 s |
|
||||
| 2 | 4.41 s |
|
||||
| 3 | 4.16 s |
|
||||
|
||||
Brak przyspieszenia między wywołaniami — zgodnie z projektem (`OLLAMA_KEEP_ALIVE=0`
|
||||
zwalnia model po każdym requeście, `ollama ps` pokazuje zero rezydentnych modeli
|
||||
między wywołaniami).
|
||||
|
||||
RAM: baseline idle ~66 MiB, szczyt podczas burst ~983 MiB (`docker stats`, próbkowane
|
||||
co 0.3 s w trakcie 3 wywołań) — komfortowo w granicach ceilingu `2560m`. `free -h`
|
||||
systemowe: `available` nie spadło poniżej ~1.3 GiB w trakcie, osiadło na ~4.2 GiB po
|
||||
(dla porównania: przed startem eksperymentu `available` = 3.7 GiB).
|
||||
|
||||
**Werdykt**: oba kryteria planu spełnione (latencja pojedyncze sekundy, nie
|
||||
dziesiątki; RAM ze sporym zapasem) → **włączony jako domyślny fallback**, bez flagi
|
||||
`KB_QUERY_LOCAL_FALLBACK_ENABLED`.
|
||||
|
||||
## 5. Bramka jakościowa (plan §9)
|
||||
|
||||
Wszystko uruchomione z `~/kb/venv` na PIHA (istniejący venv z poprzednich sesji,
|
||||
`aiohttp`/`asyncpg`/`yaml` już obecne) przeciw żywej bazie + żywemu kb-query.
|
||||
|
||||
**HTTP-equivalence** (`--transport http` vs `--transport direct`, SOLARIA up, ten sam
|
||||
`--gate-n 10`): oba PASS, **0 rozbieżności** w `dist` na wszystkich zapytaniach
|
||||
(`flat_top1_dist`, `hybrid_top1_dist`, `cascade[10].top1_dist`) — identyczne bit w
|
||||
bit, jak wymagał plan (nie ±epsilon, bo to ten sam kod, HTTP to tylko opakowanie).
|
||||
|
||||
**Live sol-down fallback test**: symulacja przez `OLLAMA_URL=http://solaria:1`
|
||||
(zły port, zgodnie z rekomendacją planu — zero dotknięcia SOLARII/innych
|
||||
konsumentów Ollamy) w `.env` kb-query, restart kontenera. `/healthz` →
|
||||
`sol_status: "down"`. `/search` → 200, wyniki z PIHA, ~4.3 s (zgodnie z kalibracją).
|
||||
Pełna bramka `retrieval_eval.py --transport http` z SOLARIA-down: **PASS** —
|
||||
identyczny wzorzec hit@3 co na SOLARII, `dist` w granicach epsilon:
|
||||
|
||||
| Zapytanie | dist (SOLARIA) | dist (PIHA fallback) | Δ |
|
||||
|---|---|---|---|
|
||||
| 1 | 0.341780 | 0.341509 | 0.000271 |
|
||||
| 2 | 0.324808 | 0.324858 | 0.00005 |
|
||||
| 3 | 0.428898 | 0.429184 | 0.000286 |
|
||||
| 4 | 0.448199 | 0.447903 | 0.000296 |
|
||||
| 5 | 0.386901 | 0.386816 | 0.000085 |
|
||||
| N (negative control) | 0.598301 | 0.598017 | 0.000283 |
|
||||
| N2 (negative control borderline) | 0.529772 | 0.529530 | 0.000242 |
|
||||
|
||||
Maksymalna rozbieżność: **~3e-4** — rząd wielkości mniejszy niż oczekiwany przez plan
|
||||
(1e-3–1e-2), kolejność top-k identyczna, wynik bramki (`gate.passed`) identyczny.
|
||||
Kb-query przywrócony do normalnej konfiguracji po teście (`.env` z prawdziwym
|
||||
`OLLAMA_URL`, restart), `/healthz` z powrotem `sol_status: "up"`.
|
||||
|
||||
## 6. Deploy
|
||||
|
||||
Kod nie był jeszcze zmergowany do `master` (dyscyplina worktree: agent nigdy nie
|
||||
mergeuje/pushuje `master`) — deploy przez standardowy `deploy-node.sh`
|
||||
niedostępny bez mastera. Zamiast tego: `rsync` zmienionych plików
|
||||
(`packages/kb-retrieval`, `services/kb-query`, `services/ollama-piha`,
|
||||
`hosts/piha/runtime/ollama-piha`, `hosts/piha/services.yaml`,
|
||||
`jobs/documents-ingest/eval/retrieval_eval.py` + README) do żywego checkoutu
|
||||
`~/homelab-codex-ws` na PIHA (bez zmiany brancha — working tree pozostaje na
|
||||
`master` z niescommitowanym diffem 1:1 identycznym z tą gałęzią), potem
|
||||
standardowy `docker compose ... up -d --build` z tego miejsca. Efekt: realny,
|
||||
działający deploy, ale **repo na PIHA ma dziś dirty working tree** — wymaga domknięcia
|
||||
(patrz "Do zrobienia przez operatora" niżej).
|
||||
|
||||
Zweryfikowane: `kb-query` (healthy), `ollama-piha` (healthy, `bge-m3` w wolumenie),
|
||||
`curl https://kb.kapala.org/healthz` → `200 {"sol_status":"up"}`,
|
||||
`curl https://kb.kapala.org/search?q=test` → `200`.
|
||||
|
||||
## Stan na koniec sesji
|
||||
|
||||
| Element | Status |
|
||||
|---|---|
|
||||
| `packages/kb-retrieval` — `embed_chunk(timeout_s=...)` | ✅ kod + testy |
|
||||
| `services/kb-query/app/fallback.py` — maszyna stanów | ✅ kod + testy (38/38 kb-query, 25/25 kb-retrieval) |
|
||||
| `services/ollama-piha` — nowy serwis GitOps | ✅ zdefiniowany, ✅ LIVE na PIHA |
|
||||
| Natywny `ollama.service` na PIHA (osierocony) | ✅ wyłączony (nie odinstalowany) |
|
||||
| Kalibracja RAM/latencja | ✅ zmierzone — werdykt GO |
|
||||
| `retrieval_eval.py --transport http` | ✅ zaimplementowane, ✅ PASS na żywo |
|
||||
| Live sol-down fallback test | ✅ PASS, Δ~3e-4 |
|
||||
| Deploy kb-query + ollama-piha na PIHA | ✅ LIVE, working tree PIHA dirty (patrz niżej) |
|
||||
| Merge do `master` | ⛔ nie wykonany (dyscyplina worktree — operator) |
|
||||
|
||||
## Do zrobienia przez operatora
|
||||
|
||||
1. **Merge** `task/kb-f4-fallback` → `master` (`scripts/dev/agent.sh merge` albo
|
||||
ręcznie) — branch popchnięty do `origin/task/kb-f4-fallback` (patrz commit poniżej).
|
||||
2. Na PIHA: `cd ~/homelab-codex-ws && git status` będzie dirty (diff identyczny z tym
|
||||
commitem, bo już wdrożony ad-hoc przez `rsync` w tej sesji) — po mergu do mastera,
|
||||
`git checkout -- .` (working tree już ma dokładnie tę treść) albo zwyczajnie
|
||||
`git pull` po mergu powinien wylądować "already up to date"/no-op, bo pliki na
|
||||
dysku już są zgodne z tym co przyjdzie z mastera. **Zweryfikować** `git diff` jest
|
||||
puste po pull, nie zakładać.
|
||||
3. Backlog: natywny `ollama.service` na PIHA wyłączony `systemctl disable --now`
|
||||
2026-07-27 (§3 wyżej) — jeśli nic się nie posypie przez ~2 tygodnie, odinstalować
|
||||
binarkę/unit całkiem.
|
||||
4. OIDC dla kb-query nadal odłożone (decyzja z 2026-07-23) — nie w zakresie tej sesji.
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-28
|
||||
links: []
|
||||
---
|
||||
|
||||
# Session log 2026-07-28
|
||||
|
||||
## Session 21:59
|
||||
|
||||
### Commits
|
||||
```
|
||||
905ad96 docs(architecture): plan naprawy subsystemu A (control-plane) 2026-07-28
|
||||
e8aa3e3 docs(architecture): recon multiagent 2026-07-27
|
||||
```
|
||||
|
||||
### Files changed
|
||||
```
|
||||
kb/phases/subsystem-a-naprawa.md | 60 +++
|
||||
kb/subsystems/recon-multiagent.md | 551 +++++++++++++++++++++++
|
||||
2 files changed, 611 insertions(+)
|
||||
```
|
||||
|
||||
### Deploys
|
||||
None recorded
|
||||
|
||||
### Narrative
|
||||
> _user-provided summary_
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-07-30
|
||||
links: []
|
||||
---
|
||||
|
||||
# 2026-07-30 — HA: legacy zamknięte, MCP read-only (faza 2a)
|
||||
|
||||
## Legacy — finał
|
||||
- Tydzień obserwacji czysty; kontener homeassistant5 już nie istniał przy
|
||||
próbie rm — usunięty przez nieustalony mechanizm (node-agent cleanup?
|
||||
remediation?). Katalog /home/pi/homeassistant NIETKNIĘTY (fałszywy alarm:
|
||||
2>/dev/null maskował Permission denied — lekcja). Backlog: ustalić, co
|
||||
usunęło kontener (granice autonomii agentów); katalog do kasacji przy
|
||||
porządkach piha.
|
||||
|
||||
## Faza 2a — własny MCP server (decyzja operatora: własny > hass-mcp)
|
||||
- services/ha-mcp: 7 tools read-only strukturalnie (klient bez metod
|
||||
mutujących, WS allowlista, test grepujący za call_service), stdio,
|
||||
reuse ha_api/ha_ws, rejestracja w .mcp.json. 42 testy offline, smoke
|
||||
na żywym ken (1647 encji, 115 automatyzacji = zgodne z repo).
|
||||
- Wtopa wdrożeniowa: venv żył w worktree, zginął przy merge-cleanup;
|
||||
fix: odtworzenie w głównym checkoucie. Backlog: run.sh bootstrap venva.
|
||||
- Test bojowy (świeży CC, zero kontekstu): MCP wołany natywnie (5 calls),
|
||||
synteza hybrydowa MCP+repo — pełna mapa salonu z odwróconym indeksem
|
||||
encja→automatyzacje. Ujawniona luka: brak find_automations_using_entity
|
||||
(cross-ref robiony grepem). Backlog: dodać tool przed fazą 2b.
|
||||
|
||||
## Znaleziska testu bojowego (klasy audytowej)
|
||||
- Choinka/lampki (tasmota_12, zblampkiregal, tasmota_8) sterowane wyłącznie
|
||||
przez device_id — niewidoczne dla grep po entity_id; martwe od 26-29.07,
|
||||
automatyzacje cicho nie działają.
|
||||
- DRUGA FALA martwych urządzeń 26-29.07 (switche choinki/regału + pilot
|
||||
4button ponownie) — osobna od awarii 17.07; tłumaczy trend unavailable
|
||||
305→377. Diagnoza sprzętowa: priorytet podniesiony, dwie daty padów.
|
||||
- Task porządkowy device_id→entity_id (checklista pkt 17) dostał twardy
|
||||
dowód zasadności — do wykonania PO diagnozie sprzętowej.
|
||||
|
||||
## Następne
|
||||
- Diagnoza sprzętowa (fizyczna): dwie fale, z2m pokazuje część urządzeń
|
||||
żywych (baterie OK) — podejrzenie na most z2m↔HA / integrację.
|
||||
- Faza 2b: propose_change/dry_run/request_approval przez kolejkę
|
||||
control-plane (pending→approved→executed) + Telegram. Osobna sesja.
|
||||
- Tool find_automations_using_entity + bootstrap venva w run.sh.
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-04
|
||||
links: []
|
||||
---
|
||||
|
||||
# Session log 2026-07-31 — KB Faza 4: zamknięcie + pilot narty27
|
||||
|
||||
## Zakres
|
||||
Finalne domknięcie fazy 4 subsystemu B (KB) — **na twardo** — oraz podsumowanie
|
||||
pilota fazy 5 (narty27), wykonanego równolegle.
|
||||
|
||||
## Faza 4 — zamknięcie (na twardo)
|
||||
|
||||
### Dedup podwójnej implementacji fallbacku embed
|
||||
- Rozstrzygnięcie: master (`e7625cd`, `app/embed_router.py`) = źródło prawdy.
|
||||
- Z porzuconego brancha uratowano S1–S4 (`cb8a19d`):
|
||||
- `retrieval_eval.py --transport http`
|
||||
- session log 27.07
|
||||
- testy luk T1–T3
|
||||
- komentarz kalibracji progów dist.
|
||||
- Pełny rozbiór obu implementacji: `kb/phases/kb-m5-faza4-fallback-dedup.md`.
|
||||
|
||||
### Deploy na PIHA (z mastera)
|
||||
- `ollama-piha`: named volume `ollama_piha_models`, model bge-m3, `KEEP_ALIVE=0`.
|
||||
- `kb-query`: port 8230, bind 192.168.31.5, `EMBED_FALLBACK_URL` ustawione.
|
||||
|
||||
### Test sol-down — PASS na żywej produkcji
|
||||
Przebieg: pause ollama@SOLARIA → cache 30 s trzyma `up` → zapytanie przełącza się
|
||||
one-shot na PIHA (`embed_backend: piha`, wyniki poprawne) → `sol_status: down` →
|
||||
unpause → powrót `up` w ≤35 s.
|
||||
|
||||
Dodatkowo zaobserwowano **samoistne, jednorazowe zadziałanie breakera na produkcji** —
|
||||
przyczyna nieustalona, zachowanie zgodne z projektem (przełączenie i powrót bez
|
||||
utraty odpowiedzi).
|
||||
|
||||
### Progi dist (skalibrowane, obowiązują)
|
||||
`<0.45` hit / `0.45–0.55` szara strefa / `>0.55` brak odpowiedzi.
|
||||
|
||||
## Pilot fazy 5 — narty27 (POC, zostaje na stałe)
|
||||
|
||||
Publiczna wystawka `narty27.kapala.org` zbudowana **pełnym wzorcem docelowym fazy 5
|
||||
w miniaturze**:
|
||||
|
||||
markdown+frontmatter (OKF v0.1) → walidator konformancji (`check_okf`) → generatory
|
||||
(graf cytoscape, karty HTML, tabela porównawcza, zdjęcia z filtrem percepcyjnym,
|
||||
landing) → statyczny hosting (PIHA nginx + named volume) → publiczny ingress
|
||||
(NPM VPS + Let's Encrypt).
|
||||
|
||||
Źródło treści: `~/narty-2027/saalbach-kb` — lokalny git na SOLARII, **celowo poza repo
|
||||
infry**. Infra: `services/narty27`.
|
||||
|
||||
### Wnioski do przeniesienia na fazę 5 (wiki-kompilat)
|
||||
|
||||
1. **OKF v0.1 działa w praktyce**, a pinowanie wersji okazało się słuszne — spec
|
||||
ewoluuje (v0.2: `timestamp` → `generated: {by, at}`, provenance first-class;
|
||||
migracja = jedna zamiana pola). Przy fazie 5 rozważyć start od razu na v0.2 albo
|
||||
pin v0.1 z zaplanowaną migracją.
|
||||
2. **Walidator-lint jako stały element pipeline'u** wiki, nie jednorazowy skrypt.
|
||||
3. **Bug upstreamu `knowledge-catalog`**: generator grafu pomija linki od `/` wbrew
|
||||
§5.1 własnej spec → napisany własny generator. Kandydat na issue/PR do
|
||||
`GoogleCloudPlatform/knowledge-catalog`.
|
||||
4. **Reserved files**: `index.md` bez frontmattera (poza `okf_version` w root) —
|
||||
walidator to łapie.
|
||||
5. **Warstwa prezentacji z generatorów** — tani, użyteczny wzorzec do reużycia nad
|
||||
wiki-kompilatem KB.
|
||||
6. **Każdy artefakt ma mieć dom w gicie; sesje mają logi.**
|
||||
|
||||
## Otwarte po sesji
|
||||
|
||||
1. **`expected_envelope` w `mail_queries`** (`jobs/documents-ingest/eval/queries.yaml`)
|
||||
— nadal `null` (placeholdery, artefakt danych, nie kodu); kryterium 4 bramki na
|
||||
stubie nie przechodzi wyłącznie z tego powodu.
|
||||
2. **`hosts/solaria/runtime/ollama/docker-compose.override.yml` — brak w repo**
|
||||
(rozjazd repo↔runtime na SOLARII).
|
||||
3. **R1–R3 node-agent** (incydent `kb/incidents/2026-07-30-ollama-solaria-vanish.md`)
|
||||
— **niezrobione**: R1 `_prune_stopped_containers` nie może kasować kontenerów
|
||||
zarządzanych, R2 rate-limit dla `ai_node`/`standard`, R3 logowanie usuniętych
|
||||
zasobów. Przyczyna nadal aktywna → blokuje/warunkuje fazę mailową
|
||||
(M1 = mitygacja doraźna na czas backfillu).
|
||||
|
||||
## Następne kroki
|
||||
|
||||
1. Faza mailowa: batching Ollamy → backfill ~225k kopert → IMAP przyrostówka →
|
||||
PDF-y (~336) → GDrive Takeout.
|
||||
2. Blokada/ryzyko: node-agent R1–R3 (unfiltered prune) — eskalacja do subsystemu A;
|
||||
mitygacja M1 na SOLARII na czas backfillu.
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-05
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-08-05 — batching embed (start fazy mailowej)
|
||||
|
||||
## Weryfikacja zaległości
|
||||
|
||||
- **Uninstall natywnego `ollama.service` na PIHA — POTWIERDZONY.** Unit i binarka nie
|
||||
istnieją.
|
||||
- **Session log fazy 4 istniał już na masterze** (`e619c00`). Zgłoszony brak był fałszywym
|
||||
alarmem z niedociągniętego working tree na SOLARII.
|
||||
- **`task/prune-fix` (R1–R3 + M1 VPS) gotowy do review** w subsystemie A — `0526af1`,
|
||||
285 insertions, z testami.
|
||||
|
||||
## Task `kb-mail-batching`
|
||||
|
||||
Zmergowany do mastera: `02a0079` + `75116ad`.
|
||||
|
||||
### Bug blokujący Etap B (znaleziony i naprawiony)
|
||||
|
||||
Goły `builtins.TimeoutError` z wyczerpanego `aiohttp` `ClientTimeout` **nie był łapany**
|
||||
przez `flush_embed_buffer` — obsługa łapała wyłącznie `ClientError`. Skutek: zawieszona
|
||||
Ollama (failure mode „przyjmuje połączenie i milczy") wywalała cały run, bez breakera
|
||||
i bez flushu. Klasy przejściowe wyliczone są teraz jawnie w `TRANSIENT_EMBED_ERRORS`.
|
||||
|
||||
### `embed_batch_resilient()`
|
||||
|
||||
- Retry z backoffem wykładniczym.
|
||||
- Po wyczerpaniu retry — probe `/api/tags`:
|
||||
- backend **żywy** → bisekcja izolująca trujący chunk,
|
||||
- backend **martwy** → `gave_up`, bez bisekcji.
|
||||
|
||||
### Semantyka breakera (zmiana)
|
||||
|
||||
Breaker liczy **give-upy** (backend down wg probe), nie nieudane batche. Porażka częściowa
|
||||
nie przesuwa licznika. Zmiana znaczenia `--max-embed-failures` opisana w
|
||||
`kb/phases/kb-m5-faza-mailowa.md` §9.
|
||||
|
||||
### Parametryzacja i metryki
|
||||
|
||||
- Flagi: `--batch-size`, `--embed-retries`, `--embed-backoff`, `--embed-timeout`
|
||||
(env `MAIL_INGEST_*`).
|
||||
- Metryka `embed_ms_per_chunk`.
|
||||
- Wiersze zembedowane w umierającym batchu są commitowane przed abortem.
|
||||
|
||||
### Decyzja: brak fallbacku SOLARIA→PIHA dla backfillu
|
||||
|
||||
Świadomie **nie powstaje** — 790 ms/embed na CPU × 271k ≈ 60 h na współdzielonym nodzie.
|
||||
Tor online (`embed_router`) zachowuje fallback. Rozdział torów udokumentowany w docstringu
|
||||
`embed.py` / `embed_batch` oraz w `kb/services/job-mail-body-ingest.md`.
|
||||
|
||||
### Benchmark `mail-body-ingest-bench`
|
||||
|
||||
Read-only (SELECT + inferencja). Wyniki na SOLARII (bge-m3, GPU), próbka 640 chunków
|
||||
(avg 1559 znaków):
|
||||
|
||||
| batch | ms/chunk |
|
||||
|------:|---------:|
|
||||
| 32 | 22.70 |
|
||||
| 64 | 16.57 |
|
||||
| 128 | 15.82 |
|
||||
|
||||
**Default batch 64 POTWIERDZONY** — zysk z 128 to ~4.5%, a przy 64 koszt bisekcji jest
|
||||
mniejszy. Ekstrapolacja na 271k chunków: **~1.25 h GPU** vs ~11 h przy per-request.
|
||||
|
||||
### Testy
|
||||
|
||||
117 testów zielonych.
|
||||
|
||||
## Wyjątki procesowe
|
||||
|
||||
Żadnych. Commit z PIHA nie zaistniał — CC nie dopisał wskaźnika, uznano za zbędne.
|
||||
|
||||
## TODO wynikające
|
||||
|
||||
- **Rotacja hasła `kb-postgres`** — poszło do historii shella i na screeny sesji.
|
||||
- **`POSTGRES_PASSWORD` plaintext w `services/*/service.yaml`** — kandydat na backlog
|
||||
sekretów.
|
||||
- **`kb-site` `robots.txt` blokuje fetch Claude** (`ROBOTS_DISALLOWED`) — fix: `X-Robots-Tag
|
||||
noindex` zamiast `Disallow` (wariant B), przy okazji taska `kb-site`.
|
||||
- **Backfill 271k** — dopiero po merge + deploy `task/prune-fix` (subsystem A).
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-05
|
||||
links: []
|
||||
---
|
||||
|
||||
# Session log 2026-08-05
|
||||
|
||||
## Session 22:47
|
||||
|
||||
Redeploy node-agenta R1/R2/R3 na flotę (supervised, checkpointy zatwierdzane przez
|
||||
operatora). Incydent źródłowy: `kb/incidents/2026-07-30-ollama-solaria-vanish.md`.
|
||||
|
||||
### Commits
|
||||
|
||||
Ta sesja **nie wprowadziła żadnych commitów** poza niniejszym logiem — deploy runtime,
|
||||
zero zmian w kodzie. Granica wyznaczona fallbackiem 24 h (poprzedni log sesji nie używa
|
||||
nagłówków `## Session HH:MM`), więc poniższa lista obejmuje też wcześniejszą pracę
|
||||
operatora z tego samego okna, niezwiązaną z tym deployem:
|
||||
|
||||
```
|
||||
bb3792d docs(kb-site): przekaz ACCESS_TOKEN generatorowi w procedurze publikacji
|
||||
f5c6f3b fix(kb-site): token bramki w kazdym linku wewnetrznym generatora
|
||||
04251b5 docs(sessions): log sesji 2026-08-05 — batching embed (start fazy mailowej)
|
||||
75116ad docs(kb-retrieval): rozdzial torow embed takze w docstringu embed_batch
|
||||
02a0079 feat(kb-mail-batching): retry + izolacja trujacego chunka w torze embed + benchmark
|
||||
71eaab0 feat(supervisor): duty-cycle nodes — liveness transitions logged, not actioned
|
||||
19548d8 fix(kb-site): wycofaj robots.txt — blokowal legalny fetch z tokenem
|
||||
67e49a0 docs(kb-site): przepisz nieaktualne kb.okit.pl na kb-e2a24af3.okit.pl
|
||||
db81cb1 feat(kb-site): noindex + robots.txt + obscure subdomain jako domyslny base-url
|
||||
7282a5e docs(recon): sciezka redeploy — fix jest w repo od 2026-08-03, nie jest wdrozony
|
||||
```
|
||||
|
||||
### Files changed
|
||||
|
||||
Brak — drzewo robocze czyste przez całą sesję, poza tym plikiem.
|
||||
|
||||
### Deploys
|
||||
|
||||
Recon wykazał, że zakres jest węższy niż zakładano: PIHA i VPS **już** miały kod R1/R2/R3
|
||||
(weryfikacja sha256 pliku w kontenerze vs repo). Realny zakres: SOLARIA i LUSTRO.
|
||||
LUSTRO nie było w pierwotnej liście, a było jedynym nodem faktycznie kasującym bez filtra.
|
||||
|
||||
| Node | Przed | Po | Wynik |
|
||||
|---|---|---|---|
|
||||
| SOLARIA | `c80a711f` (2026-07-22, pre-R1) | `438111e2` | OK, bez rollbacku |
|
||||
| LUSTRO | `460d5cc5` (2026-06-11, 658 linii) | `3260c74a` | OK, bez rollbacku |
|
||||
| PIHA | `9141cc61` — sha == repo HEAD | bez zmian | deploy pominięty (już aktualny) |
|
||||
| VPS | `27be875d` — R1/R2/R3 obecne | bez zmian | deploy pominięty (różnice tylko w komentarzach) |
|
||||
|
||||
Weryfikacja po deployu na obu wdrożonych nodach: kontener `Up (healthy)`, zero tracebacków,
|
||||
sha256 `node_agent.py` w runtime == repo HEAD, brak wykonywalnego `containers.prune()`,
|
||||
`docker ps -a` identyczne z pre-snapshotem, heartbeat świeży. `NODE_TYPE` zachowane
|
||||
(SOLARIA `lte_node` = M1, LUSTRO `sd_card`). Marker `last-docker-cleanup` na LUSTRO
|
||||
nie wyzerował się przy recreate.
|
||||
|
||||
Test e2e na SOLARII (kanarek `restart=unless-stopped` bez labela compose): zatrzymany,
|
||||
przeżył 151 s (>2× CHECK_INTERVAL) jako `exited`, nie usunięty. Dry-run logiki filtra
|
||||
bez wywołania `remove()`: REMOVE=0 na obu nodach.
|
||||
|
||||
Obrazy sprzed deployu otagowane `node-agent:rollback-pre-r1` na SOLARII i LUSTRO
|
||||
(na SOLARII były dangling — groziło zjedzenie celu rollbacku przez przyszły prune).
|
||||
|
||||
### Narrative
|
||||
|
||||
> _user-provided summary_
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-06
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-08-06 — Etap B: weryfikacja korpusu + fix NUL (ZAMKNIĘTY)
|
||||
|
||||
## Przebieg plastrów
|
||||
|
||||
Plastry 0-4 (`--offset 0/50000/100000/150000/200000 --limit 50000 --batch-size 64`)
|
||||
— **wszystkie EXIT 0** po fixie. Cały korpus 225k kopert przeskanowany
|
||||
idempotentnie, zero strat.
|
||||
|
||||
## Bug znaleziony i naprawiony: NUL byte (0x00) w treści maila
|
||||
|
||||
Plaster offset 50k wywalił się na mailach z 2007 (Sony Ericsson, 3 chunki):
|
||||
bajt NUL w tekście → `asyncpg.CharacterNotInRepertoireError` przy insercie
|
||||
(PostgreSQL nie przyjmuje `0x00` w `text`).
|
||||
|
||||
Fix `4ec0b78`: strip `\x00` przed chunkowaniem i embedem + liczniki
|
||||
`nul_bytes_stripped` / `mails_nul_sanitized`. Re-run plastra 1: **EXIT 0**,
|
||||
3 chunki dobrane.
|
||||
|
||||
## Weryfikacja w DB (kb-postgres@PIHA, `document_chunk`)
|
||||
|
||||
| Miara | Wartość |
|
||||
|---|---|
|
||||
| `document_chunk` total | **389 012** |
|
||||
| nie-excluded **bez** embeddingu | **0** |
|
||||
| nie-excluded z wektorem | 187 025 |
|
||||
| newsletter-flagged bez wektora | 201 849 (odwracalne) |
|
||||
| excluded **z** wektorem | 138 (artefakt kolejności flagowania, nieszkodliwy) |
|
||||
|
||||
## Wniosek
|
||||
|
||||
**Korpus był w pełni zembedowany jeszcze przed dzisiejszymi plastrami.**
|
||||
Zapamiętany stan „6,4k embedded z Etapu A, ~225k kopert do backfillu" był
|
||||
nieaktualny — wcześniejsze przebiegi pokryły całość. Dzisiejsze runy to
|
||||
w praktyce pełna, idempotentna weryfikacja korpusu (plus wykrycie i naprawa
|
||||
buga NUL).
|
||||
|
||||
Źródło mylącego odczytu: licznik `chunks_already_embedded` liczy **istnienie
|
||||
wiersza w DB** (w tym chunków newsletter-flagged bez wektora), a nie obecność
|
||||
wektora — stąd niespójne wrażenie z liczników plastrów.
|
||||
|
||||
## Środowisko
|
||||
|
||||
- venv w głównym repo (`pip install -e` dla `kb-mail` / `kb-retrieval` /
|
||||
`mail-body-ingest`).
|
||||
- tmux `backfill`, logi w `~/kb/mail/ingest-logs/` (poza repo).
|
||||
|
||||
## Follow-upy
|
||||
|
||||
- `jobs/gmail-header-backfill` i `jobs/gmail-bulk-import` używają
|
||||
`sanitize_surrogates` na nagłówkach zapisywanych do `jsonb` — **ta sama
|
||||
latentna podatność na NUL**. Nieruszone w tej sesji, osobny task.
|
||||
- **Rotacja hasła `kb-postgres`** — nadal otwarta (z sesji 2026-08-05).
|
||||
|
||||
## Następny krok fazy mailowej
|
||||
|
||||
**IMAP przyrostówka gmail + fastmail** (`source='fastmail'`) — teraz odblokowana.
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-06
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-08-06 (wieczór) — przyrostówka IMAP na żywo (Krok 7 fazy mailowej DONE)
|
||||
|
||||
## Ścieżka: recon → decyzje → implementacja
|
||||
|
||||
Recon (`75d9695`, `kb/audits/mail-sync-2026-08-06.md`) → decyzje **(a)–(g) zatwierdzone
|
||||
w całości** → implementacja w trzech commitach:
|
||||
|
||||
| Commit | Zawartość |
|
||||
|---|---|
|
||||
| `c65f0f2` | adapter IMAP w `packages/kb-mail`, migracja 005 `mail_sync_state` |
|
||||
| `f056b08` | job `mail-imap-sync` |
|
||||
| `ae16deb` | takt `kb-ingest` co 2h + etap mailowy, korekta `kb-mail-pillar` (JMAP→IMAP) |
|
||||
|
||||
**642 testy.**
|
||||
|
||||
## Pierwsze uruchomienie wg runbooka `mail-sync-run.md` — z incydentami
|
||||
|
||||
### Run #1 — padł na `UID SEARCH ALL`
|
||||
|
||||
Gmail: `UID SEARCH ALL` na **227 900** wiadomości przekroczył `imaplib._MAXLINE`
|
||||
(1 MB) → crash.
|
||||
|
||||
Przyczyna: błąd operatora w `.env` — `MAIL_GMAIL_INITIAL_MODE=full` zamiast `since`,
|
||||
a `FASTMAIL_INITIAL_MODE` w ogóle niewpisany (fastmail zdążył zsynchronizować
|
||||
new-only i zapisać stan).
|
||||
|
||||
> **FOLLOW-UP do CC:** utwardzić search na duże foldery — zakres UID / `SINCE`
|
||||
> zamiast `ALL`, podbicie `_MAXLINE`. Obecnie **tryb `full` na dużym koncie = crash**.
|
||||
|
||||
### Naprawa
|
||||
|
||||
Korekta `.env` + `DELETE` stanu fastmail z `mail_sync_state` → run #2 czysty.
|
||||
|
||||
### Run #2 — initial
|
||||
|
||||
| Konto | Tryb | Seen | Inserted | Dup | Conflict |
|
||||
|---|---|---:|---:|---:|---:|
|
||||
| gmail | initial (SINCE=2026-06-15) | 1 872 | 1 692 | 180 | — |
|
||||
| fastmail | initial-full | 96 | 87 | 1 | 8 (`conflict_other_source`) |
|
||||
|
||||
Razem **1 779 nowych kopert, 0 błędów**. Nakładka `SINCE` zadziałała; cross-account
|
||||
dedup po `Message-ID` działa (te 8 konfliktów to ta sama poczta widziana z drugiego
|
||||
konta).
|
||||
|
||||
### Run #3 — test przyrostowości
|
||||
|
||||
`mode=incremental`: gmail **+5**, fastmail **0**, kursor OK.
|
||||
|
||||
## Drenaż embed
|
||||
|
||||
`mail-body-ingest --only-unchunked`: **698 embeddingów** (baseline 187 163 → **187 861**).
|
||||
|
||||
> **Uwaga (follow-up):** tempo ~570 ms/chunk sugeruje, że `OLLAMA_URL=SOLARIA` nie
|
||||
> przebił się przez `sudo env` i liczyło CPU PIHA. Do weryfikacji przy następnym
|
||||
> dużym drenażu.
|
||||
|
||||
## Test end-to-end — PASS
|
||||
|
||||
Mail wysłany 17:21 (gmail→fastmail), obie kopie wylądowały poprawnie: envelope
|
||||
gmail + `conflict_other_source` fastmail. Ścieżka **sync → ingest → hybrid search**:
|
||||
top-1 dist **0.391**. Decyzja **(g) potwierdzona**.
|
||||
|
||||
## Automat
|
||||
|
||||
- `kb-mail-sync.timer` — **enabled**, tick co ~1 h, pierwszy 18:01.
|
||||
- `kb-ingest.timer` — co 2 h (nowa definicja `0/2:00:00`).
|
||||
- Reguła `fleet-prometheus/kb-mail-sync.yml` wchodzi przy najbliższym deployu
|
||||
fleet-prometheus *(follow-up)*.
|
||||
|
||||
## Poprawki do runbooka (follow-up, nie zrobione)
|
||||
|
||||
- Ręczne runy wymagają `sudo` do odczytu `.env` (`600 root:root`) — podać wariant
|
||||
`sudo bash -c`.
|
||||
- Krok 7: `curl` na `localhost:8230` nie zadziała — `kb-query` binduje na
|
||||
`192.168.31.5`.
|
||||
|
||||
## Otwarte
|
||||
|
||||
- **Rotacja hasła `kb-postgres` (WISI)** — wyciekło 2026-08-05/06 do transkryptu
|
||||
i historii shella. **Trzeci udokumentowany przypadek tej klasy.**
|
||||
- Fix `UID SEARCH` (patrz Run #1).
|
||||
- Załączniki PDF z maili (~336 + nowe).
|
||||
- NUL w nagłówkach `jsonb` (`gmail-header-backfill` / `bulk-import`).
|
||||
|
||||
## Stan fazy mailowej po sesji
|
||||
|
||||
| Element | Status |
|
||||
|---|---|
|
||||
| (a) batching | ✓ |
|
||||
| (b) Etap B | ✓ |
|
||||
| (c) regresja | ✓ |
|
||||
| (d) hybrid default | ✓ |
|
||||
| Krok 7 — przyrostówka | ✓ **ŻYWA** |
|
||||
|
||||
Pozostało: backfill PDF, Google Drive Takeout.
|
||||
|
||||
**Następna duża rzecz wg roadmapy: FAZA 5 — wiki-kompilat.** Materiał mailowy jest
|
||||
kompletny i świeży, więc warunek wejścia spełniony.
|
||||
|
|
@ -1,496 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-06
|
||||
links: []
|
||||
---
|
||||
|
||||
# Session log 2026-08-06
|
||||
|
||||
## Session 13:20
|
||||
|
||||
Produkcyjna weryfikacja pierwszego cyklu safe-cleanup na LUSTRO (R1/R2/R3, deploy
|
||||
2026-08-05) oraz pierwszy pełny cykl HITL: approval → dispatch → wykonanie na nodzie →
|
||||
`action_result` → `completed`. Sesja supervised, checkpointy zatwierdzane przez operatora,
|
||||
approvale wykonywane wyłącznie przez operatora. Incydent źródłowy:
|
||||
`docs/incidents/2026-07-30-ollama-solaria-vanish.md`.
|
||||
|
||||
### Commits
|
||||
|
||||
Ta sesja **nie wprowadziła żadnych commitów poza niniejszym logiem** — zero zmian w kodzie
|
||||
i konfiguracji repo. Cała praca to recon read-only + kontrolowane zapisy runtime na
|
||||
LUSTRO i VPS (kanarki testowe, plik akcji), wszystkie sprzątnięte lub udokumentowane niżej.
|
||||
|
||||
Poprzedni log sesji: `67aa092 docs: session 2026-08-05 22:47` — potwierdzony na
|
||||
`origin/master` (ahead/behind 0/0) na starcie sesji.
|
||||
|
||||
### Files changed
|
||||
|
||||
Brak — poza tym plikiem.
|
||||
|
||||
---
|
||||
|
||||
## KROK 1 — safe-cleanup na LUSTRO: obie gałęzie potwierdzone produkcyjnie
|
||||
|
||||
### Stan wyjściowy
|
||||
|
||||
Marker `/opt/homelab/state/last-docker-cleanup` = `1785925612` (2026-08-05 12:26:52 CEST),
|
||||
**sprzed deployu** (obraz node-agenta utworzony 22:42:05 CEST). Bramka
|
||||
`_cleanup_rate_ok()` (`CLEANUP_INTERVAL_SECS = 86_400`) trzymała pierwszy cykl nowego kodu
|
||||
do 12:26:52 dnia 2026-08-06. Brak linii cleanup w logach do tego momentu był więc
|
||||
zachowaniem poprawnym, nie awarią.
|
||||
|
||||
Kod w runtime zweryfikowany: `md5(/app/src/node_agent.py)` == `md5(repo HEAD)` =
|
||||
`c9ac64e10b42b3e0ed9e4c168579bfaa`, brak wykonywalnego `containers.prune()`,
|
||||
`NODE_TYPE=sd_card`.
|
||||
|
||||
Pułapka interpretacyjna: logi kontenera node-agent są w **UTC**, host w CEST. Pozorna
|
||||
6-godzinna dziura w logach to nocny `halt` (root cron `30 23 * * * /usr/sbin/halt`,
|
||||
boot 06:30) — LUSTRO ma duty cycle jak SOLARIA, zgodnie z `inventory/topology.yaml`
|
||||
(`duty_cycle: nightly`).
|
||||
|
||||
### Test w oknie prune
|
||||
|
||||
Do okna przygotowano trzy kontenery `exited` pokrywające obie gałęzie filtra:
|
||||
|
||||
| Kontener | Polityka / label | Oczekiwane | Wynik |
|
||||
|---|---|---|---|
|
||||
| `prune-disposable` | `restart=no`, brak compose | usunięty | ✅ usunięty |
|
||||
| `prune-canary` | `restart=unless-stopped`, brak compose | zachowany | ✅ przeżył |
|
||||
| `node-exporter` (realny serwis) | `restart=always`, brak compose | zachowany | ✅ przeżył |
|
||||
|
||||
Log z okna (12:27:49 CEST / 10:27:49 UTC):
|
||||
|
||||
```
|
||||
INFO - Pruned dangling images (0 MB reclaimed)
|
||||
WARNING - Removed 1 disposable stopped container(s): prune-disposable (kept 2 managed)
|
||||
```
|
||||
|
||||
Marker zaktualizowany na `1786012069`. `kept 2` = `prune-canary` + `node-exporter`.
|
||||
Wszystkie kontenery produkcyjne nietknięte.
|
||||
|
||||
**Werdykt:** obie gałęzie filtra działają na produkcji — gałąź ochronna (kontener zatrzymany
|
||||
przez operatora z polityką restartu przeżywa prune; dokładny scenariusz incydentu ollamy)
|
||||
oraz gałąź usuwania (filtr nie jest no-opem, faktycznie kasuje jednorazowe resztki).
|
||||
|
||||
### Korekta wniosku z 2026-08-05
|
||||
|
||||
Wczorajszy test kanarka na SOLARII (przeżył 151 s) **nie dowodził działania filtra** —
|
||||
SOLARIA ma `NODE_TYPE=lte_node` (mitygacja M1), gdzie `run_safe_cleanup()` kończy się
|
||||
`return` przed jakimkolwiek prune. Ten test potwierdził M1, nie R1. Dowód dla R1 powstał
|
||||
dopiero dziś na LUSTRO.
|
||||
|
||||
### Stan cleanupu na flocie
|
||||
|
||||
| Node | NODE_TYPE | Cleanup |
|
||||
|---|---|---|
|
||||
| PIHA | `sd_card` | ✅ działa (2026-08-05 16:27 CEST: `kept 0 managed`) |
|
||||
| LUSTRO | `sd_card` | ✅ działa (2026-08-06 12:27:49, jw.) |
|
||||
| SOLARIA | `lte_node` (M1) | wyłączony w całości, brak markera |
|
||||
| VPS | `lte_node` (M1) | wyłączony w całości, brak markera |
|
||||
|
||||
M1 nadal zdjęte do zrobienia na SOLARII i VPS — do tego czasu te nody nie sprzątają
|
||||
Dockera wcale.
|
||||
|
||||
---
|
||||
|
||||
## KROK 2 — dlaczego crash-loop watchtowera nie generował akcji
|
||||
|
||||
Eventy płynęły poprawnie: `evt-lustro-<ts>-containers_not_running-watchtower.json` co ~60 s,
|
||||
11 317 plików w `events/lustro/` na VPS (node-agent rsyncuje z `--remove-source-files`,
|
||||
stąd pusty katalog lokalny). To nie był problem transportu ani emisji.
|
||||
|
||||
**Przyczyna:** `supervisor.reconcile()` iteruje wyłącznie po `desired_state["services"]`
|
||||
ładowanym z `hosts/<node>/services.yaml` (`supervisor.py:380`). `hosts/lustro/services.yaml`
|
||||
deklaruje `node-agent`, `node-exporter`, `piper-tts` — watchtowera tam nie ma. Brak wpisu
|
||||
w desired state ⇒ brak driftu ⇒ brak rekomendacji. Ponad 11 tys. eventów dead-enduje.
|
||||
Observer natomiast **zna** `lustro/watchtower` (incydent `inc-1786007596-lustro-watchtower`,
|
||||
status `unhealthy`) — rozjazd dotyczy wyłącznie supervisora.
|
||||
|
||||
Wykluczone: `shadow_mode` (dotyczy tylko `HA_DIAG_SHADOW_MODE`, ścieżka HA-diag),
|
||||
`duty_cycle` (tłumi wyłącznie liveness node'a), progi/cooldown (`containers_not_running`
|
||||
jest w `CONTAINER_RESTART_TRIGGERS`, dedup po stabilnym ID).
|
||||
|
||||
Konsekwencja druga: akcja wstawiona ręcznie do `pending/` dla serwisu spoza desired state
|
||||
żyje jeden cykl supervisora. `_cancel_resolved_pending_actions()` (`supervisor.py:560`)
|
||||
skasował ją po 15 s z powodem `service_removed_from_desired_state`. Kasowanie dotyczy
|
||||
**wyłącznie `pending/`** — `approved/` i `running/` są z założenia nietykalne. Dlatego
|
||||
akcja ręczna dla watchtowera trafiła ostatecznie prosto do `approved/`.
|
||||
|
||||
---
|
||||
|
||||
## KROK 3 — dwa pełne cykle HITL
|
||||
|
||||
Wszystkie znaczniki UTC (CEST = +2). Pętla executora: 10 s. Pętla node-agenta: 60 s.
|
||||
|
||||
### Cykl 1 — `node-exporter` (ścieżka w pełni organiczna)
|
||||
|
||||
| Etap | Timestamp | Δ |
|
||||
|---|---|---|
|
||||
| `docker stop node-exporter` (trigger) | 10:22:49 | — |
|
||||
| event → observer → incydent `inc-1786011821-lustro-node-exporter` | ~10:23:41 | +52 s |
|
||||
| supervisor: `Generated recommendation` → `pending/` | 10:24:00.66 | +71 s |
|
||||
| approval operatora (`mv` → `approved/`) | ~10:38:5x | — |
|
||||
| executor: `Executing action` → `running/` | 10:39:00.650 | ≤10 s |
|
||||
| executor: `Dispatched … to node-agent on lustro` | 10:39:00.657 | +7 ms |
|
||||
| node-agent: rsync-pull + bramki + `docker restart` | 10:39:23.15 | +22,5 s |
|
||||
| event `action_result` (`success: true`) | 10:39:23.293 | +0,14 s |
|
||||
| executor: `completed` | 10:39:30.752 | +7,5 s |
|
||||
|
||||
**Approval → completed: 30,1 s.**
|
||||
|
||||
### Cykl 2 — `pi-watchtower-1` (akcja utworzona ręcznie, zatwierdzona przez operatora)
|
||||
|
||||
| Etap | Timestamp | Δ |
|
||||
|---|---|---|
|
||||
| operator zapisuje akcję do `approved/` | 11:08:38 | — |
|
||||
| executor: `Executing action` → `running/` | 11:08:40.829 | +2,8 s |
|
||||
| executor: `Dispatched … (container=pi-watchtower-1)` | 11:08:40.838 | +9 ms |
|
||||
| node-agent: `Restarted container 'pi-watchtower-1'` | 11:08:51.002 | +10,2 s |
|
||||
| event `action_result` (`success: true`) | 11:08:51.002 | — |
|
||||
| executor: `completed` | 11:09:00.909 | +9,9 s |
|
||||
|
||||
**Approval → completed: 20,1 s.** Watchtower wrócił do crash-loopa — zgodnie z założeniem;
|
||||
sukcesem było przejście pipeline'u i poprawny `action_result`, nie uzdrowienie kontenera.
|
||||
|
||||
### Bramki agenta
|
||||
|
||||
- **Whitelista typu** i **node scoping** — przeszły; logują się tylko przy odrzuceniu,
|
||||
więc dowodem przejścia jest sama egzekucja.
|
||||
- **Self-restart guard** — nie dotyczył (cel ≠ `node-agent`).
|
||||
- **Idempotencja** — zadziałała na żywo i wielokrotnie:
|
||||
`Action … already processed — skipping (idempotency)`, markery
|
||||
`/opt/homelab/state/processed-actions/<action_id>.done`.
|
||||
|
||||
### Obserwacja uboczna: regeneracja i auto-cancel
|
||||
|
||||
O 10:39:48 (18 s po udanym restarcie) supervisor **wygenerował ponownie** akcję dla
|
||||
node-exportera, bo world state jeszcze pokazywał `unhealthy` (opóźnienie observera).
|
||||
O 10:40:49 sam ją skasował (`drift_resolved_auto`). Podwójnego restartu nie było, ale
|
||||
istnieje ~60-sekundowe okno, w którym po udanej remediacji potrafi powstać duplikat.
|
||||
|
||||
---
|
||||
|
||||
## KROK 4a — rekomendacja ws. poluzowania bramek HITL
|
||||
|
||||
**Rekomendacja: jeszcze nie, ale wąskie poluzowanie jest obronialne po trzech warunkach.**
|
||||
|
||||
Za:
|
||||
- Pipeline przeszedł end-to-end dwukrotnie, w tym raz w pełni organicznie (event →
|
||||
observer → supervisor → approval → executor → node-agent → wynik).
|
||||
- Czas maszynowy to 20–30 s; wąskim gardłem jest wyłącznie człowiek (dziś ~15 i ~30 min).
|
||||
- `container_restart` jest tanie i odwracalne, wykonanie jest scoped do node'a, whitelisty
|
||||
jednego typu akcji i guardu self-restartu; egzekutor nigdy nie wchodzi na node po SSH.
|
||||
- Kolejka sama się czyści: `drift_resolved_auto` kasuje akcje, które przestały być
|
||||
potrzebne, więc opóźniony approval nie powoduje zbędnego restartu.
|
||||
- Idempotencja obroniła się w warunkach bojowych (patrz defekt dispatch niżej).
|
||||
|
||||
Przeciw:
|
||||
- Próbka: 2 wykonania, 1 node, 1 typ akcji, obie ścieżki udane. **Ani razu nie zaobserwowano
|
||||
ścieżki porażki** (`success: false`), timeoutu akcji w `running/`, ani odrzucenia przez
|
||||
bramkę node/whitelisty. Dowód dotyczy szczęśliwej ścieżki.
|
||||
- Restart nie leczy przyczyn źródłowych. Watchtower ma 1000+ restartów dziennie — automat
|
||||
restartowałby go w kółko, maskując problem. Bez budżetu restartów (np. max 3/24 h na
|
||||
serwis, potem eskalacja do `alert_only`) auto-remediacja produkuje pętlę zamiast naprawy.
|
||||
- Otwarty defekt dispatch (niżej) w trybie automatycznym oznacza, że jedynym zabezpieczeniem
|
||||
przed powtórnym wykonaniem jest marker idempotencji per `action_id`. Wystarczy nowy
|
||||
`action_id` na ten sam objaw, by restart poszedł ponownie.
|
||||
- Okno duplikatu (~60 s) po udanej remediacji — dziś skasowane w porę, ale to kwestia
|
||||
wyścigu, nie gwarancji.
|
||||
- `_get_container_name()` po cichu zwraca nazwę serwisu, gdy brak `services/<svc>/docker-compose.yml`.
|
||||
Dla watchtowera dałoby to `watchtower` zamiast `pi-watchtower-1` — akcja wygenerowana
|
||||
organicznie zakończyłaby się `failed`. W trybie automatycznym to stały szum porażek.
|
||||
|
||||
Warunki wstępne do poluzowania:
|
||||
1. Naprawa wycieku plików dispatch (niżej) — inaczej automat stoi na jednej bramce.
|
||||
2. Budżet restartów per serwis + eskalacja do `alert_only` po jego wyczerpaniu.
|
||||
3. Poluzowanie tylko dla `container_restart` i tylko dla serwisów obecnych w desired state;
|
||||
`redeploy` i `disk_cleanup` zostają w pełnym HITL.
|
||||
|
||||
---
|
||||
|
||||
## KROK 4b — root cause crash-loopa watchtowera
|
||||
|
||||
Log kontenera, każde uruchomienie:
|
||||
|
||||
```
|
||||
level=error msg="Error response from daemon: client version 1.25 is too old.
|
||||
Minimum supported API version is 1.40, please upgrade your client to a newer version"
|
||||
```
|
||||
|
||||
`pi-watchtower-1` (`containrrr/watchtower`, obraz `c352868a1654`, kontener utworzony
|
||||
2025-04-15) rozmawia z socketem Dockera przez API 1.25. Demon na LUSTRO wymaga minimum
|
||||
1.40 i odrzuca połączenie, watchtower kończy się `exit 1`, `restart=always` uruchamia go
|
||||
ponownie — cykl ~60 s. Licznik restartów kasuje się przy nocnym `halt`/boot, stąd
|
||||
„973 restarty" to dorobek jednego dnia pracy, a nie narastająca awaria.
|
||||
|
||||
Co by go naprawiło (do backlogu, **nie wykonane w tej sesji**):
|
||||
1. `docker pull containrrr/watchtower:latest` + recreate — aktualne wydania negocjują
|
||||
nowsze API. Najprostsze.
|
||||
2. Obejście: `DOCKER_API_VERSION=1.41` w env kontenera.
|
||||
3. **Preferowane:** usunąć watchtowera z LUSTRO. To relikt spoza GitOps, a automatyczne
|
||||
podmienianie obrazów na edge'owym Pi kłóci się z modelem repo jako źródła prawdy.
|
||||
Jeśli ma zostać — dopisać go do `hosts/lustro/services.yaml`, bo dopiero wtedy stanie
|
||||
się widoczny dla supervisora.
|
||||
|
||||
Efekt uboczny do rozważenia niezależnie: watchtower generuje ~1440 eventów/dobę, które
|
||||
nigdzie nie prowadzą, i jest głównym powodem, dla którego `events/lustro/` ma 11 tys. plików.
|
||||
|
||||
---
|
||||
|
||||
## Follow-upy
|
||||
|
||||
1. **Wyciek plików dispatch (nowy defekt, potwierdzony).** Executor tworzy
|
||||
`actions/dispatch/<node>/` z uprawnieniami **755** (`aerbot:aerbot`), a rsync-pull leci
|
||||
jako `oskar` (grupa `aerbot`) — brak prawa zapisu w katalogu, więc
|
||||
`--remove-source-files` nie kasuje źródła. Dla porównania `dispatch/piha` ma 775.
|
||||
Dodatkowo rsync zwraca wtedy kod 23, który node-agent traktuje jako benign
|
||||
(`returncode not in (0, 23, 24)`) → **cicha porażka, zero ostrzeżeń**. Skutek: LUSTRO
|
||||
re-pulluje te same akcje co 60 s i odbija się od bramki idempotencji — w nieskończoność.
|
||||
Docstring `pull_dispatched_actions()` twierdzi, że plik jest kasowany po pobraniu; nie jest.
|
||||
Fix: `mkdir(mode=0o775)` w executorze + osobna obsługa rc=23 przy `--remove-source-files`.
|
||||
Do czasu naprawy na LUSTRO trwa zombie re-pull dwóch plików co 60 s.
|
||||
2. `_get_container_name()` — cichy fallback na nazwę serwisu przy braku
|
||||
`services/<svc>/docker-compose.yml`; produkuje akcje celujące w nieistniejące kontenery.
|
||||
3. Okno ~60 s, w którym po udanej remediacji powstaje duplikat akcji (opóźnienie observera).
|
||||
4. Root cause watchtowera — patrz KROK 4b.
|
||||
5. Zdjęcie M1 (`NODE_TYPE=lte_node`) na SOLARII i VPS — do tego czasu zero cleanupu Dockera
|
||||
na obu nodach.
|
||||
6. 17 zwietrzałych akcji w `pending/` z czerwca i lipca (16× `alert-*`, `redeploy-vps-gokapi`)
|
||||
— nikt ich nie zamyka, zaśmiecają kolejkę operatora.
|
||||
7. `events/lustro/` — 11 tys. plików, rosnące głównie przez watchtowera.
|
||||
|
||||
## Pominięte / niepewne
|
||||
|
||||
- Bramki node-scoping i whitelisty typu potwierdzone **tylko pośrednio** (przez udaną
|
||||
egzekucję), bez testu negatywnego.
|
||||
- Ścieżka porażki (`action_result` z `success: false`) oraz timeout akcji w `running/`
|
||||
nie zostały przetestowane.
|
||||
- Gałąź prune obrazów wykonała się na zerze — `0 MB reclaimed` przy 0 dangling images
|
||||
przed i po. Potwierdza, że kod się wykonuje, nie że potrafi cokolwiek odzyskać.
|
||||
- Wycieknięte pliki dispatch na VPS usunięte ręcznie przez operatora na koniec sesji.
|
||||
Sam defekt (uprawnienia 755 + połknięty rc=23) pozostaje — wyciek wróci przy następnej
|
||||
akcji dispatchowanej na LUSTRO.
|
||||
- Cykl HITL sprawdzony wyłącznie na LUSTRO. PIHA (jedyny inny node z aktywnym dispatch)
|
||||
nie był testowany.
|
||||
|
||||
## Sprzątanie
|
||||
|
||||
- `prune-canary` — usunięty po weryfikacji (12:29:25).
|
||||
- `prune-disposable` — usunięty przez sam cleanup, zgodnie z zamysłem testu.
|
||||
- Obraz `alpine` (ściągnięty na potrzeby kanarków) — usunięty.
|
||||
- `node-exporter` — działa, podniesiony **przez pipeline HITL**, nie ręcznie.
|
||||
- `actions/dispatch/lustro/` na VPS — opróżniony przez operatora (zombie re-pull ustał).
|
||||
- LUSTRO na koniec: `node-agent` (healthy), `node-exporter` (up), `piper-tts` (up),
|
||||
`pi-watchtower-1` (restarting — bez zmian, świadomie).
|
||||
|
||||
### Narrative
|
||||
|
||||
> _user-provided summary_
|
||||
|
||||
---
|
||||
|
||||
## Session 15:25
|
||||
|
||||
Wdrożenie do runtime dwóch fixów zmergowanych na `master` (supervised, checkpointy
|
||||
zatwierdzane przez operatora): dispatch `0o775` + rc=23 w executorze/node-agencie
|
||||
(`52eca1c`) oraz zdjęcie mitygacji M1 na SOLARII i VPS (`1bab321`). Domyka follow-upy
|
||||
#1 i #5 z sesji 13:20.
|
||||
|
||||
### Commits
|
||||
|
||||
Ta sesja **nie wprowadziła żadnych commitów** poza niniejszym logiem — deploy runtime,
|
||||
zero zmian w kodzie. Wdrożone commity powstały wcześniej, na branchu
|
||||
`task/dispatch-perms-m1`:
|
||||
|
||||
```
|
||||
1bab321 revert(m1): zdjecie NODE_TYPE=lte_node na SOLARII i VPS po wdrozeniu R1
|
||||
52eca1c fix(dispatch): inbox 0o775 + rsync rc=23 przestaje byc cichy
|
||||
```
|
||||
|
||||
W trakcie sesji main checkout przesunął się o `75d9695 docs(recon): przyrostowka IMAP
|
||||
gmail + fastmail` (druga sesja operatora, docs-only). Bez wpływu: `node_agent.py` ma to
|
||||
samo `sha256 aec6cb03` w obu drzewach, więc SOLARIA — zdeployowana jeszcze z `1bab321` —
|
||||
nie rozjechała się z VPS-em deployowanym z `75d9695`.
|
||||
|
||||
### Files changed
|
||||
|
||||
Brak — drzewo robocze czyste przez całą sesję, poza tym plikiem.
|
||||
|
||||
### Deploys
|
||||
|
||||
| Node | Serwis | Przed | Po | Wynik |
|
||||
|---|---|---|---|---|
|
||||
| SOLARIA | node-agent | `node_agent.py` `fee079e8`, obraz `438111e2` | `aec6cb03` == repo HEAD, obraz `bc28a30a` | OK |
|
||||
| VPS | node-agent | `node_agent.py` `c21967d3`, obraz `27be875d` | `aec6cb03` == repo HEAD | OK |
|
||||
| VPS | control-plane | `executor.py` `5ca0490e`, 4 obrazy z 2026-08-05 | `1c3b569f` == repo HEAD, 4 obrazy przebudowane | OK |
|
||||
|
||||
Mechanizm: `scripts/deploy/deploy-service.sh --build-if-needed` dla node-agenta (ta sama
|
||||
ścieżka co 2026-08-05). **Nie** użyto `scripts/deploy/deploy.sh <target>`: jest to
|
||||
dyspozytor Saturn-side po SSH, deployujący *cały* node — na VPS ruszyłby npm, outline,
|
||||
joplin i ai-cluster, czyli daleko poza zakres, a sesja toczyła się z SOLARII (`ssh solaria`
|
||||
to połączenie do samego siebie).
|
||||
|
||||
`NODE_TYPE` po zdjęciu M1: SOLARIA `lte_node` → **`ai_node`** (jawnie w override),
|
||||
VPS `lte_node` → **linia usunięta**, `NODE_TYPE=""` z base compose → `_resolve_node_type()`
|
||||
zwraca `standard`. Log startowy potwierdza jedno i drugie (`type=ai_node`, `type=standard`),
|
||||
czyli przewidywanie z commita `1bab321` co do pustego stringa było trafne.
|
||||
|
||||
Gate testowy: **pytest niedostępny w main checkoucie** (`.venv` bez pytest,
|
||||
`~/.local/bin/pytest` ma zepsuty `_pytest`). Oparto się na wyniku sprzed merge'a
|
||||
(node-agent 70 passed, control-plane 173 passed) plus `docker build` obu stacków przy
|
||||
deployu. Follow-up 15:25/#4.
|
||||
|
||||
### Pierwszy cykl cleanup po zdjęciu M1
|
||||
|
||||
Na obu nodach marker `/opt/homelab/state/last-docker-cleanup` **nie istniał** (M1 blokował
|
||||
zapis od 2026-08-04), więc `_cleanup_rate_ok()` zwrócił `True` i prune poszedł w pierwszym
|
||||
cyklu, ~0,5 s po starcie — zgodnie z ostrzeżeniem w `1bab321`. Dlatego kolejność w każdym
|
||||
kroku była: **najpierw tagi rollback, potem deploy.**
|
||||
|
||||
SOLARIA:
|
||||
```
|
||||
INFO - node-agent starting: node=solaria type=ai_node
|
||||
INFO - Pruned dangling images (0 MB reclaimed)
|
||||
INFO - No disposable stopped containers (kept 0 managed)
|
||||
INFO - Pruned build cache (91 MB reclaimed)
|
||||
```
|
||||
VPS:
|
||||
```
|
||||
INFO - node-agent starting: node=vps type=standard
|
||||
INFO - Pruned dangling images (0 MB reclaimed)
|
||||
INFO - No disposable stopped containers (kept 0 managed)
|
||||
INFO - Pruned build cache (239 MB reclaimed)
|
||||
```
|
||||
|
||||
**Zero ubytków kontenerów na obu nodach** — `docker ps -a` przed vs po, diff nazw pusty
|
||||
(SOLARIA 9/9, VPS 24/24). `humanai-mailer` i `humanai-landing` (bez definicji w repo)
|
||||
nietknięte. Tagi `rollback-*` przeżyły prune.
|
||||
|
||||
Dwie prognozy przedwdrożeniowe wymagały korekty — obie z tego samego powodu, że
|
||||
`docker images` pokazuje **rozmiar pozorny z warstwami współdzielonymi**, a nie realny
|
||||
odzysk:
|
||||
|
||||
* **SOLARIA, „4 dangling ≈ 553 MB":** cztery obrazy faktycznie zniknęły (35 → 32, przy
|
||||
+1 nowym buildzie), ale `SpaceReclaimed` = **0 MB**. Ich warstwy są współdzielone z
|
||||
control-plane i kb-query. Realny odzysk obrazów ≈ 70 MB (17,89 → 17,82 GB); z build
|
||||
cache (606,1 → 510,5 MB) łącznie ≈ 165 MB.
|
||||
* **VPS, „1 dangling 395 MB":** ten obraz to **żywy `outline-postgres-1`** — untagged, ale
|
||||
oznaczony `U` (in use). Docker odmawia usunięcia obrazu używanego przez kontener, więc
|
||||
prune go nie ruszył i **nie miał prawa ruszyć**. Realny odzysk to wyłącznie build cache
|
||||
239 MB (z 250,8 MB reclaimable).
|
||||
|
||||
Kontener `control-plane-ui` na SOLARII stoi w stanie `created` — poza zasięgiem prune'a
|
||||
podwójnie: `_prune_stopped_containers()` listuje wyłącznie `status=exited`, a kontener ma
|
||||
i tak label compose oraz `restart=unless-stopped`.
|
||||
|
||||
### Weryfikacja fixu dispatch end-to-end
|
||||
|
||||
Stan wejściowy zdjęty **przed** deployem control-plane (patrz follow-up 15:25/#1 — inaczej
|
||||
`deploy-local.sh` by go zatarł):
|
||||
|
||||
| ścieżka | mode | |
|
||||
|---|---|---|
|
||||
| `actions/dispatch/piha` | 775 | historycznie działał |
|
||||
| `actions/dispatch/lustro` | **755** | wyciek |
|
||||
| `actions/deploy` | 755 | inbox deploy-runnera, bez podkatalogów |
|
||||
|
||||
**Korekta do sekcji „Sprzątanie" z sesji 13:20.** Zapis „`actions/dispatch/lustro/` na VPS
|
||||
— opróżniony przez operatora (zombie re-pull ustał)" **nie odzwierciedla stanu
|
||||
faktycznego**: o 13:16 oba pliki (z 10:39 i 11:08) nadal leżały w źródle, a LUSTRO
|
||||
re-pullowało je co 60 s aż do 13:18:23. Katalog zdrenował się dopiero w wyniku poniższego
|
||||
testu — i mógł, bo dopiero wtedy miał prawa `775`.
|
||||
|
||||
Mechanizm potwierdzony co do joty: inbox `aerbot:aerbot 755`, a ciągnie z niego
|
||||
`VPS_EVENTS_USER=oskar` — uid **1002**, tylko *członek* grupy `aerbot` (gid 1000). Grupa ma
|
||||
`r-x` bez `w`, więc `--remove-source-files` nie może zrobić unlinku:
|
||||
|
||||
```
|
||||
13:15:13 INFO - Action container-restart-lustro-node-exporter already processed — skipping
|
||||
13:15:13 INFO - Action container-restart-lustro-watchtower already processed — skipping
|
||||
13:16:16 ... (to samo) 13:17:19 ... (to samo)
|
||||
```
|
||||
|
||||
**Test HITL.** Cel zmieniony z `watchtower` na `node-exporter`: watchtower na LUSTRO jest
|
||||
w crash-loopie (137 restartów, `restart=always` — root cause opisany w KROKU 4b sesji
|
||||
13:20), więc nie dałby czystego sygnału. Nowe `action_id`
|
||||
(`container-restart-lustro-node-exporter-dispatchfix`), bo recykling starego odbiłby się od
|
||||
bramki idempotencji na LUSTRO i zawiesiłby akcję w `running` do timeoutu.
|
||||
|
||||
**Ścieżka: przez `approved/`, nie przez approval operatora.** Plik `pending` zapisany
|
||||
13:16:56 UTC został auto-anulowany przez supervisora po **7 sekundach**
|
||||
(`drift_resolved_auto` — `node-exporter` był zdrowy), zanim operator zdążył kliknąć. To
|
||||
dokładnie zjawisko z sekcji „Obserwacja uboczna: regeneracja i auto-cancel" sesji 13:20,
|
||||
tym razem szybsze (7 s vs 15 s). Zgodnie z ustaleniem przed testem druga kopia trafiła
|
||||
prosto do `approved/`; cel testu to mechanika dispatchu, nie ścieżka approvalu. Pole
|
||||
`status` w tej kopii zostało `cancelled` — bez znaczenia, executor kieruje się katalogiem,
|
||||
nie polem.
|
||||
|
||||
Wynik — wszystkie sześć kryteriów spełnione:
|
||||
|
||||
1. **755 → 775** dokładnie w momencie zapisu przez executora (13:17:23) ✅
|
||||
2. plik akcji **zniknął** ze źródła po pobraniu i **nie wrócił** w kolejnych cyklach ✅
|
||||
3. **oba zaległe pliki zdrenowane przy okazji** — `dispatch/lustro/` całkowicie pusty ✅
|
||||
4. spam `already processed — skipping` **ustał**: dwa pełne cykle (13:19:25, 13:20:28) z
|
||||
zerem trafień ✅
|
||||
5. akcja `completed` przez `action_result` po 70 s, `node-exporter` z nowym uptime ✅
|
||||
6. zero ERROR/WARNING w executorze, observerze, supervisorze i node-agentach — poza znanym,
|
||||
niezwiązanym crash-loopem watchtowera na LUSTRO ✅
|
||||
|
||||
`13:18:23 INFO - Restarted container 'node-exporter' for action
|
||||
container-restart-lustro-node-exporter-dispatchfix`
|
||||
|
||||
**Czego ten test NIE dowiódł.** Druga połowa fixu — klasyfikacja rc=23 z WARNING-iem — na
|
||||
LUSTRO nie pojechała: node-agent tam ma nadal `fee079e8` (ten sam sha, który SOLARIA miała
|
||||
przed dzisiejszym deployem). `grep -c "rc=23"` = 0 w logach LUSTRO dlatego, że stary kod
|
||||
fizycznie nie umie tego zalogować, a nie dlatego, że jest dobrze. Follow-up 15:25/#3.
|
||||
|
||||
### Stan tagów rollback
|
||||
|
||||
| Node | Tag | Image ID |
|
||||
|---|---|---|
|
||||
| SOLARIA | `node-agent:rollback-pre-dispatchfix` | `438111e2` |
|
||||
| SOLARIA | `node-agent:rollback-pre-r1` (z 2026-08-05) | `c80a711f` |
|
||||
| VPS | `node-agent:rollback-pre-dispatchfix` | `27be875d` |
|
||||
| VPS | `control-plane-executor:rollback-pre-dispatchfix` | `b878d835` |
|
||||
| VPS | `control-plane-observer:rollback-pre-dispatchfix` | `4b79511d` |
|
||||
| VPS | `control-plane-supervisor:rollback-pre-dispatchfix` | `357b70d1` |
|
||||
| VPS | `control-plane-operator-ui:rollback-pre-dispatchfix` | `e522bbaa` |
|
||||
|
||||
Pierwsze tagi `rollback-*` na VPS w ogóle. Otagowano wszystkie cztery obrazy control-plane,
|
||||
nie tylko executora — `deploy-local.sh` przebudowuje cały stack, więc każdy potrzebuje celu
|
||||
rollbacku.
|
||||
|
||||
### Follow-upy (sesja 15:25)
|
||||
|
||||
1. **`deploy-local.sh` robi rekurencyjny `chown` + `chmod` na całym `/opt/homelab`** — do
|
||||
przeglądu, czy to w ogóle pożądane. Oba branche by dziś zadziałały: `sudo chown -R
|
||||
1000:1000` (wyzwalacz: `events/solaria/evt-solaria-1785946768-node_health-node.json`) i
|
||||
`sudo chmod -R 775` (wyzwalacze: `actions/dispatch/lustro`, `actions/deploy`). Ten drugi
|
||||
ustawiłby `dispatch/lustro` na 775 z zupełnie innego powodu niż fix w executorze,
|
||||
zacierając stan wejściowy testu, i nadałby 775 *plikom* w całym drzewie runtime (stąd
|
||||
`-rwxrwxr-x` na heartbeatach i JSON-ach akcji). **Świadoma decyzja operatora: nie
|
||||
naprawiać ręcznie.** Control-plane zdeployowano samym krokiem compose (`up -d --build
|
||||
--force-recreate`) — bez sudo, więc rozjazdy chown/chmod zostały nietknięte i czekają na
|
||||
przegląd. Dodatkowo `sudo` na VPS wymaga hasła, więc kanoniczna ścieżka i tak kończyłaby
|
||||
się handoffem (exit 5).
|
||||
2. **Lokalny control-plane na SOLARII** (executor/observer/supervisor/ui, `dispatch/solaria/`
|
||||
z 2026-07-22) ma nadal executor z bugiem `0o755`. Nie jest dyspozytorem floty — log to
|
||||
same `Starting executor loop` — ale to drugi, niezdeployowany egzemplarz tego samego
|
||||
kodu. Osobna sesja.
|
||||
3. **LUSTRO i PIHA bez dzisiejszego node-agenta.** LUSTRO: `fee079e8`. Bez fixu rc=23
|
||||
nadal *połykają* nieudany unlink — dziś to nie boli, bo executor już nie tworzy złych
|
||||
inboxów, ale każdy przyszły wyciek o innej przyczynie znowu będzie niewidoczny.
|
||||
4. **Brak pytest w main checkoucie na SOLARII** — `deploy.sh <target>` przewróciłby się na
|
||||
gate (exit 2) bez `--no-gate`. Do naprawy, zanim ktoś sięgnie po kanoniczną ścieżkę
|
||||
deployu z tej maszyny.
|
||||
5. **Duplikat akcji testowej** — `container-restart-lustro-node-exporter-dispatchfix.json`
|
||||
leży jednocześnie w `completed/` (właściwy przebieg) i w `cancelled/` (kopia z
|
||||
auto-anulowania sprzed approvalu). Pozostawione bez zmian — kosmetyka, żaden zapis
|
||||
produkcyjny nie był potrzebny.
|
||||
|
||||
### Narrative
|
||||
|
||||
> _user-provided summary_
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-26
|
||||
links: []
|
||||
---
|
||||
|
||||
# Sesja 2026-08-26 — incydent kb-mail-sync: 20 dni ciszy, wykryte i naprawione
|
||||
|
||||
## Timeline
|
||||
|
||||
- **Wykrycie** — przy sanity checku po 3 tygodniach bez nadzoru (kontynuacja sesji 16:00,
|
||||
patrz `docs/sessions/2026-08-26.md`): `mail-imap-sync` nie zapisał żadnej koperty od
|
||||
2026-08-06 18:01 (pierwszy i jedyny udany tick automatu) mimo że timer tykał godzinowo
|
||||
przez 20 dni. ~548 maili zaległości.
|
||||
- **Diagnoza** — `PermissionError` na `save_eml`: katalogi archiwum `2026/08` powstały
|
||||
`root:root` z ręcznych `sudo`-runów 06.08, timer działa jako `oskar`. Kursor nie
|
||||
przeskakiwał niezapisanej wiadomości (zgodnie z projektem) — stąd cisza bez utraty poczty,
|
||||
ale też bez sygnału.
|
||||
- **Naprawa zapisu** — `chown -R oskar:oskar` na archiwum + dwa ręczne ticki: run#1
|
||||
170 inserted/379 perm-errors (zaległości sprzed chowna), run#2 378 inserted/155
|
||||
archive_exists/0 errors — pełne odzyskanie.
|
||||
- **Reload Prometheusa** — przez CC z SOLARII (SSH na VPS): `promtool check rules` OK,
|
||||
`POST /-/reload`, weryfikacja `/api/v1/rules` (wszystkie trzy grupy żywe), metryka
|
||||
scrape'owana i świeża, `/api/v1/alerts` czyste, `brain-watchdog`→Telegram wpięty
|
||||
poprawnie (`PROMETHEUS_URL` OK, kontener healthy).
|
||||
|
||||
Pełny opis root cause (oba, niezależne) i rekomendacje R1–R3:
|
||||
[kb/incidents/2026-08-26-mail-sync-20-dni-ciszy.md](../../kb/incidents/2026-08-26-mail-sync-20-dni-ciszy.md).
|
||||
|
||||
## Stan
|
||||
|
||||
Przyrostówka **znów żywa** i **po raz pierwszy faktycznie monitorowana** — reguła
|
||||
`KbMailSyncStale` istniała w repo od 06.08, ale dopiero teraz jest realnie załadowana w
|
||||
Prometheusie. Wcześniej alert nie mógł wystrzelić niezależnie od tego, jak długo trwałaby
|
||||
awaria zapisu.
|
||||
|
||||
## Plan sprzed incydentu — przesunięty
|
||||
|
||||
Rotacja haseł (`kb-postgres` + teraz też `TG_TOKEN`, wyciekł w tej sesji przez `cat .env`
|
||||
diagnostyczny — czwarty przypadek tej klasy) i recon Fazy 5 (wiki-kompilat) — obie pozycje
|
||||
przesunięte na następną sesję, nie ruszone dzisiaj poza samą diagnozą.
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
## Session 16:00
|
||||
|
||||
Recon floty po 3 tygodniach bez nadzoru (ostatnia sesja 2026-08-06) + sesja
|
||||
naprawcza: (A) usunięcie watchtowera z LUSTRO jako reliktu spoza GitOps,
|
||||
(B) higiena kolejek `actions/pending` i zawieszonych incydentów `active` na VPS.
|
||||
SUPERVISED — checkpointy A/B/C zatwierdzane przez operatora, backup przed
|
||||
każdą operacją destrukcyjną.
|
||||
|
||||
### Commits
|
||||
|
||||
Ta sesja nie wprowadziła żadnych commitów w kodzie poza niniejszym logiem —
|
||||
wyłącznie operacje na nodach (LUSTRO, VPS). Jeden niepowiązany commit
|
||||
(`003f83d`, kb-publish skill) doszedł na `master` od równoległej sesji
|
||||
operatora w międzyczasie — poza zakresem tej sesji.
|
||||
|
||||
```
|
||||
(brak commitów tej sesji)
|
||||
```
|
||||
|
||||
### Files changed
|
||||
|
||||
Brak zmian w repo.
|
||||
|
||||
### Deploys / operacje na nodach
|
||||
|
||||
**Recon (read-only, wszystkie 4 węzły: SOLARIA, PIHA, VPS, LUSTRO):**
|
||||
- Zero nowych commitów na `origin/master` przez 3 tygodnie.
|
||||
- Soak test R1 (auto-cleanup node-agenta): PIHA 20/20 uruchomień 0 usunięć,
|
||||
VPS 21/21 uruchomień 0 usunięć, SOLARIA 3/3 0 usunięć, LUSTRO 5/5 —
|
||||
1 usunięcie (`prune-disposable`, celowy kanarek testowy z 08-06, zgodnie
|
||||
z zamysłem). Zero ofiar wśród kontenerów chronionych. `rc=23` nie wystąpił
|
||||
ani razu — fix `0o775` z sesji 08-06 trzyma.
|
||||
- Zdiagnozowano ciągłą pętlę restartów `pi-watchtower-1` na LUSTRO (API
|
||||
Docker 1.25 vs wymagane min. 1.40), trwającą nieprzerwanie od co najmniej
|
||||
2026-08-06 04:31.
|
||||
|
||||
**Watchtower LUSTRO — usunięcie (checkpoint A→B, zatwierdzony):**
|
||||
- Backup: `docker inspect` + `compose.yml` + pusty katalog `/home/pi/watchtower`
|
||||
→ `/home/pi/watchtower-removal-backup-2026-08-26/` na LUSTRO.
|
||||
- `docker stop` + `docker rm pi-watchtower-1`, `docker rmi containrrr/watchtower:latest`,
|
||||
`/home/pi/compose.yml` (jedyne źródło autostartu — brak systemd/cron) przeniesiony
|
||||
do backupu jako `.disabled`.
|
||||
- Weryfikacja: `docker ps -a` na LUSTRO czyste; 7 min ciszy zdarzeń
|
||||
`containers_not_running-watchtower` na VPS (wymagane min. 5 min).
|
||||
|
||||
**Higiena kolejek VPS (checkpoint C→wykonanie, zatwierdzony):**
|
||||
- Backup: `tar czf /opt/homelab/backups/actions-incidents-2026-08-26.tgz`
|
||||
(`actions/` + `world/incidents.json`), sha256 `ef9afbfa...`.
|
||||
- 17/18 pending → `cancelled/` (`stale_manual_cleanup`): 16× stare
|
||||
`alert-node-*`/`alert-ha-*` z czerwca + 1× shadow-mode HA-websocket z 13.08
|
||||
(kolizja nazwy pliku z niepowiązanym wpisem z 08-06 — zapisany pod nową
|
||||
nazwą `container-restart-piha-homeassistant-shadowmode-20260813.json`,
|
||||
żeby nie nadpisać cudzej historii).
|
||||
- `redeploy-vps-gokapi` **pozostawiony** — realna luka wdrożeniowa (desired
|
||||
w `hosts/vps/services.yaml`, brak kontenera), nie cruft. Follow-up do
|
||||
sesji deploy.
|
||||
- 5 incydentów w `world/incidents.json` ręcznie przełączonych na `resolved`
|
||||
(`piha-homeassistant`, `solaria-narty27`, `solaria-prune-canary`,
|
||||
`lustro-prune-canary`, `lustro-watchtower`) — wszystkie zdiagnozowane jako
|
||||
trwale osierocone (brak mechanizmu auto-resolve dla zniknionej/przeniesionej
|
||||
usługi, patrz Narrative).
|
||||
- Sekwencja bez wyścigu: `docker stop control-plane-observer` → edycja pliku
|
||||
→ `docker start` → weryfikacja >15 s (kilka cykli flush) — bo `_save_world()`
|
||||
nadpisuje `world/*.json` co 5 s z pamięci procesu, bez merge z dyskiem.
|
||||
- Efekt uboczny własnego restartu: `inc-...-vps-observer` (1 wystąpienie) —
|
||||
rozwiązał się sam w ~60 s (poprawny, nieosierocony przypadek).
|
||||
- Stan końcowy: `active_incidents_count: 0`, `runtime-summary.json status: nominal`.
|
||||
|
||||
### Narrative
|
||||
|
||||
> _user-provided summary_
|
||||
|
||||
## Session 23:00
|
||||
|
||||
Deploy control-plane (observer + supervisor) na VPS: stale-resolve 24h +
|
||||
flagi `resolve-requests` (commit `71a7af5`), unikalny `action_id`
|
||||
`container_restart` z bare-id fallbackiem (commit `91db682`), usunięcie
|
||||
gokapi z desired state (commit `74ff3ee`) — zmerdowane do `master` jako
|
||||
`f155999` przez operatora tuż przed sesją. SUPERVISED — checkpoint A po
|
||||
weryfikacji deployu, checkpoint B po teście ścieżki flagi.
|
||||
|
||||
### Commits
|
||||
|
||||
```
|
||||
(brak commitów kodu tej sesji — wyłącznie deploy + test na produkcji;
|
||||
log sesji poniżej dopisany bez pusha)
|
||||
```
|
||||
|
||||
### Files changed
|
||||
|
||||
Brak zmian w repo poza niniejszym logiem.
|
||||
|
||||
### Deploys / operacje na nodach
|
||||
|
||||
**KROK 0 — sanity:** `git pull` na `~/homelab-codex-ws` (SOLARIA, główny
|
||||
checkout) — już aktualny na `03441a1` (na wierzchu mergu `f155999`).
|
||||
`git status`/`diff HEAD` czyste. Wcześniej w tej samej sesji (przed
|
||||
mergem) `git log -1` pokazywał `4fa10f0` — merge jeszcze nie istniał;
|
||||
zatrzymano się i poczekano na operatora zamiast mergować samodzielnie
|
||||
(worktree-aware: merge to wyłącznie krok człowieka).
|
||||
|
||||
**KROK 1 — deploy control-plane (checkpoint A, zatwierdzony):**
|
||||
- Rollback tagi: `control-plane-{executor,observer,operator-ui,supervisor}
|
||||
:rollback-pre-resolvefix` — ten sam wzorzec co `:rollback-pre-dispatchfix`
|
||||
z 08-06.
|
||||
- `git pull origin master` na VPS (`003f83d` → `03441a1`, fast-forward),
|
||||
`docker compose up -d --build --force-recreate`.
|
||||
- `deploy-local.sh`'s auto-chown krok padł: brak TTY dla hasła sudo,
|
||||
`/opt/homelab/backups` i `/opt/homelab/events/solaria/*` są `oskar:oskar`
|
||||
zamiast `aerbot:aerbot` (1000). Sprawdzone: `actions/`, `world/`,
|
||||
`state/`, `config/` (realna ścieżka zapisu control-plane) już poprawnie
|
||||
`aerbot:aerbot 775` — ominięto self-heal, `docker compose` odpalony
|
||||
bezpośrednio bez sudo. Mismatch na `backups/`/`events/solaria/*`
|
||||
pozostawiony nietknięty (follow-up niżej).
|
||||
- Weryfikacja: 4/4 kontenery `healthy`, 0 linii error/traceback/exception
|
||||
od restartu. sha256 `observer.py` (mount `/repo`, żywy) i `supervisor.py`
|
||||
(wypieczony `/app/src`, wymaga `--build`) == repo HEAD, potwierdzone
|
||||
osobno przez `docker exec` w obu kontenerach.
|
||||
- Po 3 cyklach reconcile: `active_incidents: 0`, `world/resolve-requests/`
|
||||
utworzony przez observera (pusty).
|
||||
- `redeploy-vps-gokapi` (pending od 07-09) auto-cancelled po pierwszym
|
||||
cyklu: `cancelled_reason: "service_removed_from_desired_state"` — bez
|
||||
ponownego wygenerowania. `pending/` pozostał czysty (tylko niezwiązane
|
||||
alerty HA z piha).
|
||||
|
||||
**KROK 2 — test ścieżki flagi na LUSTRO (checkpoint B, zatwierdzony):**
|
||||
- `docker stop node-exporter` na LUSTRO → observer otworzył
|
||||
`inc-1787777894-lustro-node-exporter`.
|
||||
- Flaga: `touch world/resolve-requests/<id>` przez zwykłego SSH
|
||||
usera **odrzucony permission denied** — katalog `755 aerbot:aerbot`,
|
||||
brak zapisu grupowego mimo że `oskar` jest w grupie `aerbot`. Obejście:
|
||||
`docker exec control-plane-observer touch ...` (proces w kontenerze
|
||||
działa jako uid 1000 = właściciel katalogu). Follow-up niżej.
|
||||
- Resolve w **1.01 s** od touch (limit ≤10s), `resolved_reason:
|
||||
manual_operator`, flaga skasowana, `service.incident_id` wyczyszczony,
|
||||
log INFO `"Manually resolving incident ... via resolve-request flag"`.
|
||||
- Drift trwał dalej (kontener wciąż stopped) → supervisor wygenerował
|
||||
`container-restart-lustro-node-exporter-1787777887` — **nowy format
|
||||
id z COMMIT 2 potwierdzony na produkcji** — oraz równolegle
|
||||
`redeploy-lustro-node-exporter` (bare id, ścieżka `unhealthy_service`
|
||||
po wyczyszczeniu `incident_id`).
|
||||
- Za decyzją operatora: `POST /action/mutate` na `127.0.0.1:18180`
|
||||
(ten sam endpoint co UI/Telegram) — zatwierdzono restart, odrzucono
|
||||
redeploy jako nadmiarowy.
|
||||
- Executor zdispatchował realnie do LUSTRO; node-agent wykonał
|
||||
`docker restart node-exporter` (log: `"Restarted container
|
||||
'node-exporter' for action container-restart-lustro-node-exporter
|
||||
-1787777887"`), akcja `completed`.
|
||||
- Drift utrzymał się jeszcze chwilę po zatwierdzeniu → drugi, nowy
|
||||
incydent (`inc-1787777955-...`) i druga, odrębna pending akcja
|
||||
(`...-1787777950`, inny suffix `started_at`) — dokładnie oczekiwane
|
||||
zachowanie "różny id przy nowym incydencie". Po powrocie zdrowia
|
||||
auto-cancelled: `cancelled_reason: "drift_resolved_auto"`, bez
|
||||
interwencji.
|
||||
- Stan końcowy: `node-exporter` na LUSTRO `Up`, oba incydenty
|
||||
`resolved`, `active_incidents: 0`, `pending/` czysty, 4/4 kontenery
|
||||
control-plane nadal `healthy`, 0 error-ish linii w logach.
|
||||
|
||||
### Follow-upy
|
||||
|
||||
- **pytest env zepsuty na SOLARII**: `~/.local/bin/pytest` (brak
|
||||
`_pytest`) i `homelab-codex-ws/.venv` (brak `pytest` w ogóle) oba
|
||||
niedziałające; działa wyłącznie `/home/oskar/anaconda3/bin/pytest`
|
||||
(7.4.4). Użyty do pełnego runu przed force-pushem poprawki COMMIT 2
|
||||
(184 passed control-plane, 70 passed node-agent).
|
||||
- **`world/resolve-requests/` permissions**: `755 aerbot:aerbot` zamiast
|
||||
konwencji `775` używanej w `actions/`/`world/`/`state/`/`config/`.
|
||||
Blokuje operatora SSH przed bezpośrednim `touch` flagi resolve —
|
||||
manualna ścieżka z 71a7af5 ("operator drops a file") w praktyni wymaga
|
||||
`docker exec`. Poprawić `chmod 775` / mode przy `os.makedirs` w
|
||||
observer.py.
|
||||
- **`backups/` i `events/solaria/*` ownership**: `oskar:oskar` zamiast
|
||||
`aerbot:aerbot` — nie blokuje funkcjonalnie (czytelne dla "other"), ale
|
||||
psuje self-heal chown w `deploy-local.sh` (próbuje rekurencyjnego sudo
|
||||
chown całego `/opt/homelab` bez TTY/hasła). Do ręcznego wyczyszczenia
|
||||
z hasłem sudo albo do zmiany self-heal na scoped (tylko katalogi
|
||||
control-plane realnie potrzebuje) zamiast całego drzewa.
|
||||
- **CLAUDE.md doc drift**: opisuje `events/YYYY-MM-DD/<node>/events.jsonl`,
|
||||
rzeczywisty layout na VPS to płaskie `events/<node>/evt-*.json` (jeden
|
||||
plik na zdarzenie, bez partycjonowania po dacie). Do poprawienia przy
|
||||
najbliższej okazji.
|
||||
- `redeploy-vps-gokapi` — zamknięty tym deployem (auto-cancelled), nie
|
||||
wymaga już dalszego follow-upu z sesji 16:00.
|
||||
|
||||
### Narrative
|
||||
|
||||
> _user-provided summary_
|
||||
|
||||
## Session 23:06
|
||||
|
||||
### Commits
|
||||
|
||||
```
|
||||
(brak commitów — sesja bez żadnej pracy poza natychmiastowym zamknięciem)
|
||||
```
|
||||
|
||||
### Files changed
|
||||
|
||||
Brak zmian w repo.
|
||||
|
||||
### Deploys
|
||||
|
||||
None recorded
|
||||
|
||||
### Narrative
|
||||
|
||||
> _user-provided summary_
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
---
|
||||
okf: "0.1"
|
||||
type: session-log
|
||||
visibility: private
|
||||
status: active
|
||||
updated: 2026-08-27
|
||||
links:
|
||||
- ../../kb/phases/kb-m5-faza5-wiki.md
|
||||
- ../../kb/phases/kb-m5-faza3.md
|
||||
- ../../kb/audits/wiki-kompilat-recon-2026-08-26.md
|
||||
---
|
||||
|
||||
# Sesja 2026-08-27 — Faza 5: decyzje + Etap 1 + rekonсyliacja (ZAMKNIĘTY)
|
||||
|
||||
## Timeline
|
||||
|
||||
- **Sanity** — automat mailowy zdrowy po nocy (last_success świeży, gmail
|
||||
max(ts) bieżący).
|
||||
- **Formalizacja decyzji** (2df4f1d i wcześniejsze) — pakiet (a)-(h) audytu
|
||||
`wiki-kompilat-recon-2026-08-26.md` ZATWIERDZONY w całości; inwariant 7
|
||||
dopisany do faza3 §8.1 (izolacja retrievalu kompilacji od `source='wiki'`);
|
||||
`check_okf.py` łapie bajty kontrolne (nowy check #12 + testy) — 4 NUL-e
|
||||
usunięte z samego audytu.
|
||||
- **Etap 1** (`task/wiki-etap1`, autonomiczny run CC ~25 min) — bootstrap +
|
||||
3 strony proof (`sprawy/fll-2025-26`, `podmioty/mbank`,
|
||||
`osoby/pawel-cesar-sanjuan-szklarz`). 40 sources + 47 przypisów
|
||||
zweryfikowanych byte-for-byte, zero degradacji. Alias-resolution: 6 er
|
||||
życia Pawła spójnie + 2 fałszywe pozytywy pod `paweld2.eu` wykryte i
|
||||
udokumentowane jako wynik negatywny (kluczowy test mechanizmu PASS).
|
||||
- **ODKRYCIE** — kb-wiki istniało na Forgejo od 2026-07-21 (5 stron: `pzu`,
|
||||
`warta`, `ubezpieczenie-auto`, `fll-2025-26`, `wspólnota`) — proof
|
||||
wykonany w fazie 3 (wątek "Kontynuacja wątku o KB", decyzja D7),
|
||||
nieodnotowany w `kb/` ani w audycie. Recon 08-26 błędnie stwierdził
|
||||
"remote nie istnieje" — sprawdził repo/pilota/bazę, nie odpytał Forgejo
|
||||
bezpośrednio.
|
||||
- **LEKCJA 1**: fakty wykonania muszą lądować w repo (lekcja 6 narty27 w
|
||||
praktyce — zgubiliśmy całe repo na 5 tygodni).
|
||||
- **LEKCJA 2**: recon zasobów zewnętrznych odpytuje źródło wprost, nie
|
||||
wnioskuje z planów.
|
||||
- **Rekonсyliacja** — porównanie dwóch niezależnych kompilacji `fll-2025-26`
|
||||
(lipiec paperless-only vs sierpień paperless+gmail): komplementarne, zero
|
||||
sprzeczności w faktach wspólnych; konwergencja metodologiczna (obie sesje
|
||||
ten sam chunk 277/278, obie odmówiły potwierdzenia nieczytelnego OCR).
|
||||
Scalenie: 21 envelope, 38 par sources, 45 przypisów re-zweryfikowanych;
|
||||
naprawiony wadliwy lipcowy przypis; konwencja "Brak danych w KB"
|
||||
sformalizowana. Potem mbank+paweł przeniesione do kanonicznego repo,
|
||||
konwencje (d)-(f) scalone, walidator w repo. Stan końcowy kb-wiki@Forgejo
|
||||
`a540a99`: 7 stron, lint 74/74 sources + 150/150 inline zielono.
|
||||
- **kb-wiki remote** przepięty HTTPS→SSH (port 222).
|
||||
- **Kandydat Etapu 2** wykryty SQL-em: `podmioty/future-minds` (organizator
|
||||
FLL, 8+ dokumentów).
|
||||
- **Follow-upy bez zmian**: rotacja sekretów (kb-postgres + `TG_TOKEN` —
|
||||
NADAL WISI), fix UID SEARCH, PDF-y, charset, R1/R2 z incydentu 26.08.
|
||||
|
||||
## Stan Fazy 5
|
||||
|
||||
- Etap 1 ✓ (2026-08-27). Next: Etap 2 — skala do 17+1 encji, lint w kodzie,
|
||||
skan charset/NUL, decyzja o integracji `source='wiki'` w retrievalu
|
||||
(inwariant 7 obowiązuje).
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
## Session 13:49
|
||||
|
||||
### Commits
|
||||
a97cec0 merge: task/drobne-fixy (resolve-requests 775, prune out of health-monitor, events doc, redeploy action_id)
|
||||
1dca438 fix(supervisor): apply started_at suffix to redeploy action_id too
|
||||
40d78ce docs: fix events layout drift in CLAUDE.md
|
||||
9a86843 fix(monitor): remove unfiltered docker container prune from health-monitor.sh
|
||||
89f75c3 fix(observer): make world/resolve-requests/ group-writable
|
||||
|
||||
### Files changed
|
||||
CLAUDE.md | 2 +-
|
||||
scripts/monitor/health-monitor.sh | 110 +++------------------
|
||||
scripts/observer/observer.py | 12 +++
|
||||
services/control-plane/src/supervisor.py | 40 ++++----
|
||||
.../control-plane/tests/test_incident_lifecycle.py | 17 ++++
|
||||
.../tests/test_supervisor_action_id_uniqueness.py | 50 +++++++++-
|
||||
6 files changed, 109 insertions(+), 122 deletions(-)
|
||||
|
||||
### Deploys
|
||||
- control-plane → VPS: tagged 4/4 images `:rollback-pre-drobnefixy`, `git pull` (03441a1→a97cec0) + `docker compose up -d --build --force-recreate` (direct, no deploy-local.sh, per 26.08 precedent). Result: 4/4 healthy, zero error/traceback in logs since restart, sha256 of observer.py and supervisor.py match HEAD, `world/resolve-requests/` mode 775 confirmed, incidents.json stable (md5 unchanged) over 3 observer cycles. 30s permission test: flag `test-perms-123` dropped via plain `ssh` as `oskar` (no docker exec) was picked up and deleted in ~5s with `WARNING - Resolve-request flag for unknown incident test-perms-123 — removing flag` — 775 confirmed working in practice.
|
||||
|
||||
### Narrative
|
||||
> _user-provided summary_
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue