Compare commits

..

No commits in common. "master" and "task/redeploy-fix" have entirely different histories.

370 changed files with 4964 additions and 20732 deletions

View file

@ -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`.

View file

@ -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.

View file

@ -93,7 +93,7 @@ services:
preflight fills `arch`, `ram_mb`, `docker_present`, `mm_runtime` — do NOT guess these. 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
View file

@ -22,8 +22,6 @@ venv/
*.egg-info/ *.egg-info/
packages/*/build/ packages/*/build/
jobs/*/build/ jobs/*/build/
# wyjscie generatorow (scripts/kb/gen_pages.py -> build/kb-site/) — artefakt, nie zrodlo
build/
# Tools # Tools
.aider* .aider*

View file

@ -40,7 +40,7 @@ Pipeline stages: **prepare → validate → deploy → verify → diagnose (on f
## Node Onboarding ## Node Onboarding
New nodes are onboarded via `scripts/onboard/` — an idempotent bash tool driven by 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. the full schema, step status table, and gotchas.
Key fields in `node.yaml`: `ssh_user`, `first_contact` (LAN IP — not `.local`), Key fields in `node.yaml`: `ssh_user`, `first_contact` (LAN IP — not `.local`),
@ -93,7 +93,7 @@ Agent → /opt/homelab/actions/pending/<id>.json
→ Executor dispatches to the target node → completed / failed → Executor dispatches to the target node → completed / failed
``` ```
The executor never connects to a node (deliberate — see kb/phases/backlog.md The executor never connects to a node (deliberate — see docs/backlog.md
"Remediacja floty bez SSH"). It writes a dispatch file that the node collects: "Remediacja floty bez SSH"). It writes a dispatch file that the node collects:
| Action type | Inbox | Executed on the node by | | Action type | Inbox | Executed on the node by |
@ -109,7 +109,7 @@ Agents must never execute destructive actions (restarts, deploys, config changes
## Event System ## 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). Emit via `scripts/lib/events.sh` (shell) or `scripts/lib/events.py` (Python).

View file

@ -31,29 +31,29 @@ Action approval flow: `pending/` → operator approves → `approved/` → execu
## Repository Structure ## Repository Structure
- `docs/`: [Infrastructure Standards](kb/subsystems/standards.md) and [Deployment Conventions](kb/subsystems/deployment.md). - `docs/`: [Infrastructure Standards](docs/standards.md) and [Deployment Conventions](docs/deployment.md).
- `kb/phases/subsystem-a-naprawa.md`: [Current Maintenance Plan (Control Plane)](kb/phases/subsystem-a-naprawa.md). - `docs/architecture/PLAN-subsystem-a-2026-07-28.md`: [Current Maintenance Plan (Control Plane)](docs/architecture/PLAN-subsystem-a-2026-07-28.md).
- `hosts/`: Host-specific configurations and service assignments. - `hosts/`: Host-specific configurations and service assignments.
- `services/`: Reusable Docker Compose service definitions. - `services/`: Reusable Docker Compose service definitions.
- `scripts/`: Deployment and management scripts. - `scripts/`: Deployment and management scripts.
## Getting Started ## Getting Started
1. **Standardization**: Follow the [Infrastructure Standards](kb/subsystems/standards.md). 1. **Standardization**: Follow the [Infrastructure Standards](docs/standards.md).
2. **Deployment**: See [Deployment Conventions](kb/subsystems/deployment.md) for how to roll out changes. 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. 3. **SATURN**: Remember that SATURN is the only node where commits should be made.
## Documentation Index ## Documentation Index
- [Current Maintenance Plan (Control Plane)](kb/phases/subsystem-a-naprawa.md) - [Current Maintenance Plan (Control Plane)](docs/architecture/PLAN-subsystem-a-2026-07-28.md)
- [Infrastructure Standards](kb/subsystems/standards.md) - [Infrastructure Standards](docs/standards.md)
- [Agent Operating Procedures](kb/subsystems/agent-operating-procedures.md) (For AI/Non-Human Agents) - [Agent Operating Procedures](docs/agents.md) (For AI/Non-Human Agents)
- [Deployment Conventions](kb/subsystems/deployment.md) - [Deployment Conventions](docs/deployment.md)
- [Hardware](kb/nodes/legacy-hardware.md) - [Hardware](docs/hardware.md)
- [Networking](kb/subsystems/networking.md) - [Networking](docs/networking.md)
- [Services](kb/subsystems/legacy-services-list.md) - [Services](docs/services.md)
- [Node Capabilities](kb/subsystems/capability-model.md) - [Node Capabilities](docs/capabilities.md)
- [Action Model](kb/subsystems/action-approval-model.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`.* *Note: This repository documents the state of the homelab. Runtime state lives outside the repository in `/opt/homelab`.*

View file

@ -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 # Access
## Description ## Description

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: subsystem
visibility: public
status: active
updated: 2026-05-20
links: []
---
# Agent Operating Procedures # 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. 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. 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. 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. 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 ## 2. Agent Roles

View file

@ -1,17 +1,8 @@
---
okf: "0.1"
type: decision
visibility: private
status: active
updated: 2026-07-29
links: []
---
# Architektura — decyzje obowiązujące # Architektura — decyzje obowiązujące
Stan decyzji na 2026-07-28. Podstawa dowodowa: Stan decyzji na 2026-07-28. Podstawa dowodowa:
[RECON-multiagent-2026-07-27.md](../subsystems/recon-multiagent.md); plan wykonawczy: [RECON-multiagent-2026-07-27.md](RECON-multiagent-2026-07-27.md); plan wykonawczy:
[PLAN-subsystem-a-2026-07-28.md](../phases/subsystem-a-naprawa.md). Zmiana którejkolwiek [PLAN-subsystem-a-2026-07-28.md](PLAN-subsystem-a-2026-07-28.md). Zmiana którejkolwiek
decyzji wymaga aktualizacji tego pliku z nową datą. decyzji wymaga aktualizacji tego pliku z nową datą.
## Dwa subsystemy (2026-07-28) ## Dwa subsystemy (2026-07-28)
@ -42,7 +33,7 @@ Stack ai-cluster na vps (openclaw, codex-worker, planner-worker, service-ops-wor
redis, mosquitto) jest **wygaszany, nie migrowany**. Bus `codex/*` martwy od redis, mosquitto) jest **wygaszany, nie migrowany**. Bus `codex/*` martwy od
2026-06-09 (zero nowych połączeń). Branch `task/ai-cluster-solaria` zostaje 2026-06-09 (zero nowych połączeń). Branch `task/ai-cluster-solaria` zostaje
**niezmergowany** — pełni rolę dokumentacji. Kontenery na vps zostaną zatrzymane w **niezmergowany** — pełni rolę dokumentacji. Kontenery na vps zostaną zatrzymane w
osobnej, nadzorowanej sesji. Szczegóły: [ai-cluster-LEGACY.md](ai-cluster-legacy.md). osobnej, nadzorowanej sesji. Szczegóły: [ai-cluster-LEGACY.md](ai-cluster-LEGACY.md).
## Approvale zostają HITL (2026-07-28) ## Approvale zostają HITL (2026-07-28)

View file

@ -1,15 +1,6 @@
---
okf: "0.1"
type: phase
visibility: private
status: active
updated: 2026-07-28
links: []
---
# Plan naprawy subsystemu A (control-plane) — 2026-07-28 # Plan naprawy subsystemu A (control-plane) — 2026-07-28
Kontekst: kb/subsystems/recon-multiagent.md. Decyzje bazowe: Kontekst: docs/architecture/RECON-multiagent-2026-07-27.md. Decyzje bazowe:
- Flota dzieli się na dwa subsystemy. A = utrzymaniowy (control-plane, node-agenty, - Flota dzieli się na dwa subsystemy. A = utrzymaniowy (control-plane, node-agenty,
self-healing) — ten plan. B = zleceniowy (dyspozytor + Telegram + KB + HA + self-healing) — ten plan. B = zleceniowy (dyspozytor + Telegram + KB + HA +
homelab-ops) — prowadzony w osobnym projekcie, poza tym planem. homelab-ops) — prowadzony w osobnym projekcie, poza tym planem.

View file

@ -1,16 +1,6 @@
---
okf: "0.1"
type: audit
visibility: private
status: active
updated: 2026-07-30
as_of: 2026-07-30
links: []
---
# Recon — node-agent vs stability-agent (2026-07-30) # Recon — node-agent vs stability-agent (2026-07-30)
Read-only recon. Ground truth: `kb/subsystems/recon-multiagent.md` Read-only recon. Ground truth: `docs/architecture/RECON-multiagent-2026-07-27.md`
(A1, A2, B7, D15). Source read at master @ `473bf8e`; this branch is cut from (A1, A2, B7, D15). Source read at master @ `473bf8e`; this branch is cut from
master @ `0650eb8`. The two intervening merges (`0650eb8` ha-mcp, `cb8a19d` master @ `0650eb8`. The two intervening merges (`0650eb8` ha-mcp, `cb8a19d`
kb-query tests) touch none of the audited paths — `services/node-agent/`, kb-query tests) touch none of the audited paths — `services/node-agent/`,
@ -66,7 +56,7 @@ control plane, stability-agent feeds the agent-system UI, and stability-agent's
half of the event store is a write-only archive nothing has ever read. half of the event store is a write-only archive nothing has ever read.
A prior recon reached the same conclusion about the event path on 2026-07-06 A prior recon reached the same conclusion about the event path on 2026-07-06
(`kb/audits/prometheus-cutover-2026-07-06.md:88-91`); it has not been acted on. (`docs/infra/prometheus-cutover-recon-2026-07-06.md:88-91`); it has not been acted on.
--- ---
@ -307,7 +297,7 @@ prefix that `container_service_name()` was written to strip.
- Remove entries from `hosts/solaria/services.yaml` and `hosts/vps/services.yaml`. - Remove entries from `hosts/solaria/services.yaml` and `hosts/vps/services.yaml`.
- `docker compose down` on vps, piha, solaria (chelsty-infra when reachable). - `docker compose down` on vps, piha, solaria (chelsty-infra when reachable).
- Purge or archive `/opt/homelab/events/2026-*/` on all four nodes (~5 MB on piha, mostly May). - Purge or archive `/opt/homelab/events/2026-*/` on all four nodes (~5 MB on piha, mostly May).
- Update CLAUDE.md (agent-system architecture §1, event-path claim at line 100), `kb/services/chelsty-stability-agent.md`, recon A1/A2/B7. - Update CLAUDE.md (agent-system architecture §1, event-path claim at line 100), `docs/chelsty-stability-agent.md`, recon A1/A2/B7.
- **Not covered by the cleanup:** the Redis publisher must be rehomed first or the UI loss is permanent. - **Not covered by the cleanup:** the Redis publisher must be rehomed first or the UI loss is permanent.
### (b) Merge stability-agent's unique checks into node-agent ### (b) Merge stability-agent's unique checks into node-agent

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: subsystem
visibility: private
status: active
updated: 2026-07-30
links: []
---
# Architecture Recon — multi-agent systems (2026-07-27) # Architecture Recon — multi-agent systems (2026-07-27)
Read-only recon of the two multi-agent systems (control-plane, ai-cluster) across vps, Read-only recon of the two multi-agent systems (control-plane, ai-cluster) across vps,
@ -615,7 +606,7 @@ No runtime state was touched.
**Legacy** **Legacy**
- `kb/decisions/ai-cluster-legacy.md`: ai-cluster is retired in place, - `docs/architecture/ai-cluster-LEGACY.md`: ai-cluster is retired in place,
not migrated (bus idle since 2026-06-09, C9); branch `task/ai-cluster-solaria` not migrated (bus idle since 2026-06-09, C9); branch `task/ai-cluster-solaria`
stays unmerged as documentation; surviving patterns listed; runtime stays unmerged as documentation; surviving patterns listed; runtime
retirement runbook (stop stack on vps, observe `free -m`, remove containers) retirement runbook (stop stack on vps, observe `free -m`, remove containers)

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: decision
visibility: public
status: active
updated: 2026-07-30
links: []
---
# ai-cluster — LEGACY, wygaszany (decyzja 2026-07-28) # ai-cluster — LEGACY, wygaszany (decyzja 2026-07-28)
## Decyzja ## Decyzja
@ -14,7 +5,7 @@ links: []
Stack **ai-cluster** działający na vps (`ai-cluster-openclaw-1`, `codex-worker`, Stack **ai-cluster** działający na vps (`ai-cluster-openclaw-1`, `codex-worker`,
`planner-worker`, `service-ops-worker`, `redis`, `mosquitto`) jest **wygaszany, `planner-worker`, `service-ops-worker`, `redis`, `mosquitto`) jest **wygaszany,
nie migrowany**. Podstawa (recon nie migrowany**. Podstawa (recon
[RECON-multiagent-2026-07-27.md](../subsystems/recon-multiagent.md), C9): [RECON-multiagent-2026-07-27.md](RECON-multiagent-2026-07-27.md), C9):
bus `codex/*` jest martwy od **2026-06-09** — zero nowych połączeń przez ~7 tygodni, bus `codex/*` jest martwy od **2026-06-09** — zero nowych połączeń przez ~7 tygodni,
workery trzymają tylko puste długożyjące połączenia. workery trzymają tylko puste długożyjące połączenia.

1276
docs/backlog.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: subsystem
visibility: public
status: active
updated: 2026-05-20
links: []
---
# Node Capability Model # 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. 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.

View file

@ -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 # CHELSTY Runtime
This document describes the runtime environment and deployment flow for CHELSTY, an offline-capable home automation edge node split across two VMs. 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. 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 ## Critical Backup Sets
| Data | Path | | Data | Path |

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: service
visibility: private
status: active
updated: 2026-05-20
links: []
---
### CHELSTY Stability Agent ### CHELSTY Stability Agent
The stability-agent on CHELSTY provides local observability and health monitoring for the node's services and infrastructure. The stability-agent on CHELSTY provides local observability and health monitoring for the node's services and infrastructure.

View file

@ -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 # Core Stack
## Description ## Description

View file

@ -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 # Deployment Conventions
This document describes the GitOps-lite deployment process for the homelab. 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. 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. 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 ## 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/`. 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/`.

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: subsystem
visibility: public
status: active
updated: 2026-05-12
links: []
---
# Homelab Event System # Homelab Event System
The homelab multi-agent platform uses a filesystem-first event architecture for observability, auditability, and agent reasoning. The homelab multi-agent platform uses a filesystem-first event architecture for observability, auditability, and agent reasoning.

View file

@ -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 # Hardware
## Description ## Description

View file

@ -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 # Hetzner VPS
## Description ## Description

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: incident
visibility: private
status: active
updated: 2026-07-30
links: []
---
# Incydent: zniknięcie kontenera `ollama` na SOLARII — 2026-07-30 # Incydent: zniknięcie kontenera `ollama` na SOLARII — 2026-07-30
**Status:** root-cause ustalony, potwierdzony logiem i kodem. Fix NIE zaimplementowany (świadomie — patrz §7). **Status:** root-cause ustalony, potwierdzony logiem i kodem. Fix NIE zaimplementowany (świadomie — patrz §7).

View file

@ -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 # Inwentaryzacja floty homelab-codex — 2026-06-30
Zebrano: 2026-06-30 17:09 CEST Zebrano: 2026-06-30 17:09 CEST

View file

@ -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 # 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). 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), 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`). oraz z repo na `master` (HEAD `22adfb1`).
Dostępność nodów podczas weryfikacji: Dostępność nodów podczas weryfikacji:

View file

@ -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) # 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** READ-ONLY recon. Zero zmian w kodzie/serwisach. Wszystkie czasy **UTC**

View file

@ -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) # Monitoring coverage — co biega vs co jest monitorowane (recon 2026-07-14)
**Pytanie:** czy wszystkie serwisy floty są monitorowane? **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 | | 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 | | 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 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. (w audycie 06-30 były w 33 shadow; dziś nie biegają). 06-30: 40 kontenerów → dziś: 42.

View file

@ -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 # Migracja okit.pl: 42.pl (FreeDNS) -> Cloudflare — plan faz
Cel: okit.pl na Cloudflare (jak kapala.org) -> wildcard *.okit.pl DNS-01 -> Cel: okit.pl na Cloudflare (jak kapala.org) -> wildcard *.okit.pl DNS-01 ->

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: runbook
visibility: private
status: active
updated: 2026-07-16
links: []
---
# Ollama SOLARIA: manual → declarative cutover runbook # Ollama SOLARIA: manual → declarative cutover runbook
Date: 2026-07-15 Date: 2026-07-15
@ -97,7 +88,7 @@ bind-mount the existing model directory.**
docker exec ollama ollama ps docker exec ollama ollama ps
``` ```
3. Embeddings endpoint + vector dimension (deferred check from 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 ```bash
curl -s http://localhost:11434/api/embeddings -d '{"model":"bge-m3","prompt":"test"}' \ 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))" | 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 - Given the missing driver, the cutover proceeded **in CPU-only mode**: the
`deploy.resources` GPU reservation was commented out in `deploy.resources` GPU reservation was commented out in
`services/ollama/docker-compose.yml` (commit `f57a01a`), and the driver fix `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. mail-embedding phase.
- **2026-07-16: driver fixed.** Installed `nvidia-driver-595-open` from the - **2026-07-16: driver fixed.** Installed `nvidia-driver-595-open` from the
distro repository — not the old `ppa:graphics-drivers/ppa` (jammy), which distro repository — not the old `ppa:graphics-drivers/ppa` (jammy), which

View file

@ -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 # 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). > 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.** > 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). > 41 kontenerow Up (inwentaryzacja 2026-06-30 liczyla 40; wszystkie nadal biega).

View file

@ -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) # Prometheus liveness cutover — recon starego toru (2026-07-06)
Read-only recon przed cutoverem liveności floty na Prometheus `up{}`. Mapa obecnego 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). chelsty/chelsty-infra (exporter DOWN po LTE, 2026-06-26).
- Reguła `NodeDown` (`rules/liveness.yml:21-28`): `up{node=~"vps|piha"} == 0`, - Reguła `NodeDown` (`rules/liveness.yml:21-28`): `up{node=~"vps|piha"} == 0`,
`for: 5m`, severity critical. solaria/lustro świadomie wykluczone (`:12-16` — `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`. 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, - 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ą `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. | | 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-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 | | 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 **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 (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 Dodatkowo docs sygnalizują konflikt IP w komentarzach `prometheus.yml:57` vs
`hosts/chelsty-infra/host.yaml:12` — do wyjaśnienia przy ewentualnym dodawaniu scrape. `hosts/chelsty-infra/host.yaml:12` — do wyjaśnienia przy ewentualnym dodawaniu scrape.
*(rzeczywisty bieżący stan chelsty — do weryfikacji na żywo; ostatni zapis: *(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 **Wniosek twardy:** cutover NIE może być globalny. Docelowa architektura to
**hybryda per-node**: `up{}` dla scrape'owanych (vps, piha, solaria, lustro), **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 - 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. z `hosts/chelsty-infra/host.yaml:12`); do tego czasu zostaje na torze eventowym.
- NodeDown dla solaria/lustro: świadomie odroczone do anomaly detection - 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. - Watchdog na sam Prometheus (D.2 pkt 5) — mały task przy etapie 3.
- saturn / chelsty-ha: świadomie poza monitoringiem — status quo. - 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: **Seria.** Minimalnie trzy taski implementacyjne + weryfikacje między nimi:
(1) etap 1 shadow-read; (2) etap 3 flaga per-node (po tygodniu etapu 2); (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. Etap 0 to czynność operatorska (runtime, nie repo). Etap 5 to niezależny backlog.
--- ---

View file

@ -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) # Prometheus cutover — Etap 2: analiza zgodności shadow-run (2026-07-15)
Analiza READ-ONLY logów `SHADOW_LIVENESS_MISMATCH` observera (parallel-run od 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 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 (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 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. `NodeDown` dla solaria/lustro słusznie pozostaje wyłączony.
### Rekomendowany mapping (potwierdzenie rekomendacji z recon F/Etap 2) ### Rekomendowany mapping (potwierdzenie rekomendacji z recon F/Etap 2)

View file

@ -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 # Audyt niezarządzanych stacków na VPS — 2026-07-27
Recon read-only przed konsolidacją do GitOps. Zebrane przez `ssh vps` (user `oskar`, Recon read-only przed konsolidacją do GitOps. Zebrane przez `ssh vps` (user `oskar`,

View file

@ -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 # Joplin Server
## Description ## Description

View file

@ -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 # 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. Stan bazy: document_chunk = 2683 chunki (bge-m3, dim 1024), 160 dokumentów z 186 kopert paperless.

View file

@ -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) # Baza wiedzy — przegląd i log decyzji (homelab-codex · KB)
> Master-dokument inicjatywy. Stoi ponad dokumentami per-projekt (`kb-01-email-design.md`, …). > 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) ## Tor równoległy (nie tutaj)
Hardening homelabu / stabilizacja control-plane — osobny wątek. Hardening homelabu / stabilizacja control-plane — osobny wątek.

View file

@ -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) # Filar maili — projekt (homelab-codex · KB · projekt #1)
> Pierwszy filar. Wzorzec referencyjny dla pozostałych (archiwum, embeddingi na SOLARIA, szkielet agenta, deploy). > 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: 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. - **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. 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 ## 7. Deploy
- Wszystko w `homelab-codex`, przez Git na SATURN, konwencja override `hosts/<node>/runtime/<svc>/`. - 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 - Usługi: `jmap-poller` (Fastmail), `imap-poller` (Gmail), `indexer`, embed (ollama na SOLARIA), `postgres+pgvector`, `mail-agent`; bulk importer jako one-shot job.
planowane jako osobne `jmap-poller` + `imap-poller`), `indexer`, embed (ollama na SOLARIA),
`postgres+pgvector`, `mail-agent`; bulk importer jako one-shot job.
- Deploy skryptem czytającym `inventory/topology.yaml`. - 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)* 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). 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 3. Fastmail JMAP live ingest → archiwum.
2026-08-06; kod gotowy, pierwszy żywy run po stronie operatora — 4. Gmail IMAP live sync → archiwum.
`kb/runbooks/mail-sync-run.md`)*
4. Gmail IMAP live sync → archiwum. *(j.w. — ten sam job `jobs/mail-imap-sync`)*
5. Filtr archiwum→indeks. 5. Filtr archiwum→indeks.
6. Indexer (parse → chunk → embed bge-m3) → pgvector. 6. Indexer (parse → chunk → embed bge-m3) → pgvector.
7. Cienki agent maili + tool dla warstwy 4. 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) ## 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). - **Sizing Gmaila** — ile realnie waży „All Mail"? (przesądza node/dysk archiwum).
- ✅ **Unifikacja adaptera** — ZAMKNIĘTE 2026-08-06 na rzecz **jednego IMAP-a** dla obu kont - **Unifikacja adaptera** — jeden wspólny IMAP dla Fastmail + Gmail (mniej kodu) vs JMAP dla Fastmail + IMAP dla Gmail (JMAP bogatszy)?
(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.
- **Reguły filtra** — startowa lista blacklist domen/nagłówków. - **Reguły filtra** — startowa lista blacklist domen/nagłówków.
- Vector store: pgvector **przyklepane** (spine). - Vector store: pgvector **przyklepane** (spine).
- Embed model: bge-m3 **przyklepane**. - Embed model: bge-m3 **przyklepane**.

View file

@ -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 # KB filar #2 — Dokumenty (Nextcloud + Paperless) — design
> Dokument-master filaru dokumentow. Stoi pod `kb-00-overview.md`. > Dokument-master filaru dokumentow. Stoi pod `kb-00-overview.md`.
> Cel: kazda sesja / Claude Code startuje z pelnym kontekstem decyzji. > Cel: kazda sesja / Claude Code startuje z pelnym kontekstem decyzji.
> Status: ARCHITEKTURA ZAMKNIETA (2026-07-01), implementacja modulowa czeka. > 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.
--- ---

View file

@ -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) # Modul 0 — Odchudzic PIHA (prerekwizyt filaru dokumentow)
> Prerekwizyt modulow 2/4 (Paperless/Nextcloud na PIHA). Bez tego PIHA nie ma > 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) ## STATUS: prerekwizyt RAM SPELNIONY (2026-07-02)
Faza 1 (audyt read-only) + faza 2 (egzekucja po review Oskara) wykonane — 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"). + egzekucja").
- **Kryterium >= 1.5Gi available: SPELNIONE.** Przed egzekucja: 2.8Gi available - **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. serwis wszedl z zapasem, nie na styku swap.
## Wymogi ## 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: - Zidentyfikowac kandydatow do usuniecia/przeniesienia/wylaczenia:
- **elasticsearch 1Gi** — kto tego uzywa? (wikijs? diskover?) — jesli martwy, ubic - **elasticsearch 1Gi** — kto tego uzywa? (wikijs? diskover?) — jesli martwy, ubic
- **diskover** — jednorazowy indekser? czy chodzi ciagle bez potrzeby? - **diskover** — jednorazowy indekser? czy chodzi ciagle bez potrzeby?

View file

@ -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) # Modul 1 — SSO Forgejo-OIDC (decyzja + wzorzec wpiecia)
> Fundament tozsamosci dla filaru dokumentow (i szerzej homelaba). Zapisuje decyzje > Fundament tozsamosci dla filaru dokumentow (i szerzej homelaba). Zapisuje decyzje

View file

@ -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) # Modul 2 — Paperless-ngx serwis (na PIHA)
> Serwis dokumentow: UI+API+Postgres+Redis. Always-on na PIHA. OCR-worker OSOBNO > Serwis dokumentow: UI+API+Postgres+Redis. Always-on na PIHA. OCR-worker OSOBNO

View file

@ -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) # Modul 3 — Paperless OCR-worker (SOLARIA + fallback PIHA)
> Ciezki OCR odseparowany od serwisu. Worker na SOLARIA (moc), fallback PIHA (wolno). > Ciezki OCR odseparowany od serwisu. Worker na SOLARIA (moc), fallback PIHA (wolno).

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: phase
visibility: private
status: active
updated: 2026-07-02
links: []
---
# Modul 4 — Nextcloud (drive/WebDAV + OIDC) # Modul 4 — Nextcloud (drive/WebDAV + OIDC)
> Drugi adapter dokumentow: zamiennik Google Drive, dowolne pliki + sync. Zrodlo > Drugi adapter dokumentow: zamiennik Google Drive, dowolne pliki + sync. Zrodlo

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: phase
visibility: private
status: active
updated: 2026-07-02
links: []
---
# Modul 5 — Ingest dokumentow -> koperta KB # Modul 5 — Ingest dokumentow -> koperta KB
> Adapter obu zrodel (Paperless API + Nextcloud WebDAV) -> koperta KB. Domyka filar #2 > Adapter obu zrodel (Paperless API + Nextcloud WebDAV) -> koperta KB. Domyka filar #2

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: phase
visibility: private
status: active
updated: 2026-07-30
links: []
---
# Raport dedup: fallback embed SOLARIA→PIHA — e7625cd (master) vs 3d4ee38 (task/kb-f4-fallback) # Raport dedup: fallback embed SOLARIA→PIHA — e7625cd (master) vs 3d4ee38 (task/kb-f4-fallback)
**Data**: 2026-07-30 · **Worktree**: `task/kb-fallback-dedup` · **Status**: salvage **wykonany** **Data**: 2026-07-30 · **Worktree**: `task/kb-fallback-dedup` · **Status**: salvage **wykonany**
@ -71,10 +62,10 @@ zegarem, master trzyma to samo wewnątrz `EmbedRouter` (też injektowalny zegar)
| `services/kb-query/{README,env.example,service.yaml,docker-compose.yml}` | ~±58 | duplikat | **porzuć** | inny (porzucony) schemat env `OLLAMA_PIHA_URL`; master bogatszy (testy A/B/C, rename `OLLAMA_URL`→`EMBED_PRIMARY_URL`) | | `services/kb-query/{README,env.example,service.yaml,docker-compose.yml}` | ~±58 | duplikat | **porzuć** | inny (porzucony) schemat env `OLLAMA_PIHA_URL`; master bogatszy (testy A/B/C, rename `OLLAMA_URL`→`EMBED_PRIMARY_URL`) |
| `packages/kb-retrieval/src/kb_retrieval/embed.py` (`timeout_s` w `embed_chunk`) | +16 | duplikat funkcji | **porzuć** | master osiąga twardy timeout przez `asyncio.wait_for` bez zmiany współdzielonego pakietu — mniejsza powierzchnia zmian, ten sam efekt | | `packages/kb-retrieval/src/kb_retrieval/embed.py` (`timeout_s` w `embed_chunk`) | +16 | duplikat funkcji | **porzuć** | master osiąga twardy timeout przez `asyncio.wait_for` bez zmiany współdzielonego pakietu — mniejsza powierzchnia zmian, ten sam efekt |
| `jobs/documents-ingest/eval/retrieval_eval.py` (`--transport {direct,http}` + `--base-url`) | +109 | **unikalna wartość** | **cherry-pick** | plan §2 decyzja 6 / §9 (bramka HTTP-equivalence) — **na masterze w ogóle nie istnieje**; e7625cd nie tknął tego pliku, patch aplikuje się czysto; kod woła tylko `GET /search` i czyta `envelope_id`/`dist`/`source` — w pełni zgodny z odpowiedzią mastera | | `jobs/documents-ingest/eval/retrieval_eval.py` (`--transport {direct,http}` + `--base-url`) | +109 | **unikalna wartość** | **cherry-pick** | plan §2 decyzja 6 / §9 (bramka HTTP-equivalence) — **na masterze w ogóle nie istnieje**; e7625cd nie tknął tego pliku, patch aplikuje się czysto; kod woła tylko `GET /search` i czyta `envelope_id`/`dist`/`source` — w pełni zgodny z odpowiedzią mastera |
| `kb/phases/kb-m5-documents-ingest-fazy.md` | +6 | **unikalna wartość** | **cherry-pick** | dokumentacja powyższego, idzie w parze | | `jobs/documents-ingest/README.md` | +6 | **unikalna wartość** | **cherry-pick** | dokumentacja powyższego, idzie w parze |
| `docs/sessions/2026-07-27-kb-f4-fallback.md` | +189 | **unikalna wartość** | **adaptuj** | jedyny zapis: (1) znalezisko osieroconego natywnego `ollama.service` na PIHA + jego wyłączenie 2026-07-27 i backlog odinstalowania, (2) kalibracja live ollama-piha (GO: peak ~983 MiB, ~4.25.3 s/embed), (3) metodologia i wyniki bramki §9 (HTTP-equivalence 0 rozbieżności; sol-down Δ~3e-4), (4) rsync-deploy → dirty working tree na PIHA. Wciągnąć z dopiskiem redakcyjnym, że zmergowana implementacja to **inny kod** (e7625cd) i wyniki bramki wymagają powtórki | | `docs/sessions/2026-07-27-kb-f4-fallback.md` | +189 | **unikalna wartość** | **adaptuj** | jedyny zapis: (1) znalezisko osieroconego natywnego `ollama.service` na PIHA + jego wyłączenie 2026-07-27 i backlog odinstalowania, (2) kalibracja live ollama-piha (GO: peak ~983 MiB, ~4.25.3 s/embed), (3) metodologia i wyniki bramki §9 (HTTP-equivalence 0 rozbieżności; sol-down Δ~3e-4), (4) rsync-deploy → dirty working tree na PIHA. Wciągnąć z dopiskiem redakcyjnym, że zmergowana implementacja to **inny kod** (e7625cd) i wyniki bramki wymagają powtórki |
| `services/ollama-piha/*` (5 plików) | +155 | duplikat | **porzuć** | wersja mastera lepsza: named volume `ollama_piha_models` (uzasadnienie uid-pattern PIHA), healthcheck sprawdza obecność `bge-m3`, bind tylko 127.0.0.1+LAN | | `services/ollama-piha/*` (5 plików) | +155 | duplikat | **porzuć** | wersja mastera lepsza: named volume `ollama_piha_models` (uzasadnienie uid-pattern PIHA), healthcheck sprawdza obecność `bge-m3`, bind tylko 127.0.0.1+LAN |
| `hosts/piha/runtime/ollama-piha/docker-compose.override.yml` | +13 | duplikat + **1 unikalny fakt** | **adaptuj (mikro)** | ten sam `mem_limit: 2560m`; ale komentarz brancha zawiera potwierdzony pomiar (peak ~983 MiB), a master wciąż mówi „Confirm/trim after live calibration" — dopisać wynik kalibracji do komentarza override'u i/lub sekcji „Calibration" w `kb/services/ollama-piha.md` | | `hosts/piha/runtime/ollama-piha/docker-compose.override.yml` | +13 | duplikat + **1 unikalny fakt** | **adaptuj (mikro)** | ten sam `mem_limit: 2560m`; ale komentarz brancha zawiera potwierdzony pomiar (peak ~983 MiB), a master wciąż mówi „Confirm/trim after live calibration" — dopisać wynik kalibracji do komentarza override'u i/lub sekcji „Calibration" w `services/ollama-piha/README.md` |
| `hosts/piha/services.yaml` | ±26 | duplikat | **porzuć** | master ma własny wpis `ollama-piha` + soft-dependency kb-query; drobna różnica (`offline_required: true` na branchu vs `false` na masterze) — master źródłem prawdy | | `hosts/piha/services.yaml` | ±26 | duplikat | **porzuć** | master ma własny wpis `ollama-piha` + soft-dependency kb-query; drobna różnica (`offline_required: true` na branchu vs `false` na masterze) — master źródłem prawdy |
--- ---
@ -107,7 +98,7 @@ skonfigurowanego → `EmbedBackendError`, `fallback_status` (up/unconfigured), s
## 4. Rekomendacja zbiorcza (lista do zatwierdzenia) ## 4. Rekomendacja zbiorcza (lista do zatwierdzenia)
1. **S1 — cherry-pick**: `retrieval_eval.py --transport http --base-url` + akapit w 1. **S1 — cherry-pick**: `retrieval_eval.py --transport http --base-url` + akapit w
`kb/phases/kb-m5-documents-ingest-fazy.md` (plan §2 D6/§9; aplikuje się czysto, zero zależności `jobs/documents-ingest/README.md` (plan §2 D6/§9; aplikuje się czysto, zero zależności
od porzuconego kodu brancha). od porzuconego kodu brancha).
2. **S2 — adaptuj**: `docs/sessions/2026-07-27-kb-f4-fallback.md``docs/sessions/` 2. **S2 — adaptuj**: `docs/sessions/2026-07-27-kb-f4-fallback.md``docs/sessions/`
z dopiskiem redakcyjnym na górze (implementacja z tej sesji porzucona na rzecz z dopiskiem redakcyjnym na górze (implementacja z tej sesji porzucona na rzecz
@ -116,7 +107,7 @@ skonfigurowanego → `EmbedBackendError`, `fallback_status` (up/unconfigured), s
3. **S3 — adaptuj**: luki testowe T1 + T2 (T3 opcjonalnie) do `test_embed_router.py`. 3. **S3 — adaptuj**: luki testowe T1 + T2 (T3 opcjonalnie) do `test_embed_router.py`.
4. **S4 — adaptuj (mikro)**: wynik kalibracji 2026-07-27 (peak ~983 MiB, ~4.25.3 s, 4. **S4 — adaptuj (mikro)**: wynik kalibracji 2026-07-27 (peak ~983 MiB, ~4.25.3 s,
werdykt GO) do komentarza `hosts/piha/runtime/ollama-piha/docker-compose.override.yml` werdykt GO) do komentarza `hosts/piha/runtime/ollama-piha/docker-compose.override.yml`
i sekcji Calibration w `kb/services/ollama-piha.md` — pomiar dotyczył kontenera i sekcji Calibration w `services/ollama-piha/README.md` — pomiar dotyczył kontenera
ollama-piha (ta sama konfiguracja: obraz, `OLLAMA_KEEP_ALIVE=0`, `mem_limit 2560m`), ollama-piha (ta sama konfiguracja: obraz, `OLLAMA_KEEP_ALIVE=0`, `mem_limit 2560m`),
więc **przenosi się** na wersję mastera; różni się tylko storage (bind vs named więc **przenosi się** na wersję mastera; różni się tylko storage (bind vs named
volume), co nie wpływa na RAM/latencję. volume), co nie wpływa na RAM/latencję.
@ -130,7 +121,7 @@ skonfigurowanego → `EmbedBackendError`, `fallback_status` (up/unconfigured), s
(3d4ee38) i worktree `~/homelab-codex-ws-kb-f4-fallback` po zakończeniu salvage. (3d4ee38) i worktree `~/homelab-codex-ws-kb-f4-fallback` po zakończeniu salvage.
- **(b) Powtórka testu sol-down na żywym masterze**: kalibracja i bramka z 2026-07-27 - **(b) Powtórka testu sol-down na żywym masterze**: kalibracja i bramka z 2026-07-27
dotyczyły **innego kodu** (`fallback.py`, env `OLLAMA_PIHA_URL`) — na wdrożonym dotyczyły **innego kodu** (`fallback.py`, env `OLLAMA_PIHA_URL`) — na wdrożonym
e7625cd trzeba przejść testy A/B/C z `kb/services/kb-query.md` oraz bramkę e7625cd trzeba przejść testy A/B/C z `services/kb-query/README.md` oraz bramkę
`retrieval_eval.py --transport http` (po S1): HTTP-equivalence przy SOLARIA-up `retrieval_eval.py --transport http` (po S1): HTTP-equivalence przy SOLARIA-up
(identyczne `dist`) i sol-down (Δ≤epsilon, kolejność top-k identyczna; baseline (identyczne `dist`) i sol-down (Δ≤epsilon, kolejność top-k identyczna; baseline
z 27.07: Δ~3e-4). Symulacja wg README: `EMBED_PRIMARY_URL=http://192.0.2.1:11434` z 27.07: Δ~3e-4). Symulacja wg README: `EMBED_PRIMARY_URL=http://192.0.2.1:11434`

View file

@ -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) # Moduł 5, faza mailowa — treść maili w retrievalu (RECON + PLAN)
> Status (2026-08-06): Kroki 0-5 WYKONANE na żywo (chunker wydzielony, hybrid > Status (2026-07-23): Kroki 0-4 WYKONANE na żywo (chunker wydzielony, hybrid
> retrieval, Etap A apply na żywej bazie, bramka jakościowa **PASS** — patrz §8). > retrieval, Etap A apply na żywej bazie), Krok 5 (bramka jakościowa) **PASS**
> **Etap B (Krok 6) ZAMKNIĘTY 2026-08-06**: pełny korpus gmail jest zchunkowany > — patrz §8 dla liczb i werdyktu. Etap B (pełne archiwum) i Krok 7 (recon
> i zembedowany (389 012 chunków, zero nie-excluded bez wektora) — patrz §9 > IMAP/JMAP) wciąż przed nami.
> „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`.
> >
> Kontynuacja `05-faza4-plan.md` (faza 4: `packages/kb-retrieval` wydzielone, > 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 > 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 > 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ń"**. > 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 > 225 030 kopert gmail ma dziś w bazie tylko nagłówki — treści leżą wyłącznie
> nagłówki — treści leżały wyłącznie w archiwum .eml na PIHA (stan zamknięty > w archiwum .eml na PIHA. Ta faza wprowadza treści maili do `document_chunk`
> Etapem B, §9). Ta faza wprowadza treści maili do `document_chunk`
> i udostępnia je w retrievalu. **To nadal wyszukiwarka, nie chat** — synteza, > 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). > 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). `hybrid_query` analogicznie do `cascade_query` (jeden embed zapytania).
- `kb-query`: `mode` pattern `^(cascade|flat|hybrid)$`; **domyślny `mode` - `kb-query`: `mode` pattern `^(cascade|flat|hybrid)$`; **domyślny `mode`
przełączany na `hybrid` dopiero po PASS bramki (§8)** — do tego czasu przełączany na `hybrid` dopiero po PASS bramki (§8)** — do tego czasu
hybrid dostępny jawnie. *(Wykonane 2026-08-06: default = `hybrid`, patrz hybrid dostępny jawnie. Wyniki gmail w UI już obsłużone (faza 4: subject/from
DoD (d) w §12.)* Wyniki gmail w UI już obsłużone (faza 4: subject/from
z headers + „Kopiuj Message-ID"). z headers + „Kopiuj Message-ID").
- Testy jednostkowe na mockach (merge, pusta gałąź summary, pusta gałąź mail). - 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 **`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, 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** mała zmiana w serwisie, nie w `packages/kb-retrieval`).
po regresji na pełnym korpusie (Etap B) — szczegóły przy DoD (d) w §12.
## 9. Krok 6 — Etap B: pełne archiwum ## 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).** **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 ≈ 2540 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 20102015 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) ## 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 34 (`jobs/fastmail-poller`, Zakotwiczone w kb-00 jako etapy 34 (`jobs/fastmail-poller`,
`jobs/gmail-imap-poller`). Zarys decyzji do tamtego reconu: `jobs/gmail-imap-poller`). Zarys decyzji do tamtego reconu:
@ -766,24 +617,20 @@ Zakotwiczone w kb-00 jako etapy 34 (`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 | | `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" | | 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 | | 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 | | IMAP/JMAP przyrostówka — implementacja | Krok 7 (zarys) | osobny recon + pakiet |
| 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" |
## 12. Plan implementacji (kolejność = zależności) ## 12. Plan implementacji (kolejność = zależności)
| # | Krok | Zależy od | Szacunek | Stan | Dowód (2026-08-04) | | # | Krok | Zależy od | Szacunek |
|---|---|---|---|---|---| |---|---|---|---|
| 0 | Chunker → `packages/kb-mail` | — | 0,5 sesji | **WYKONANE** | `348ce10`; `packages/kb-mail/src/kb_mail/chunking.py` + `tests/test_chunking.py` | | 0 | Chunker → `packages/kb-mail` | — | 0,5 sesji |
| 1 | `embed_batch` w kb-retrieval | — | 0,5 sesji | **WYKONANE** | `51998fd`; `kb_retrieval/embed.py:61` (`embed_batch`) + `tests/test_embed.py` | | 1 | `embed_batch` w kb-retrieval | — | 0,5 sesji |
| 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` | | 2 | Job `mail-body-ingest` | 0, 1 | 2 sesje |
| 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) | | 3 | Tryb hybrid (kb-retrieval + kb-query) | — (równolegle z 2) | 1 sesja |
| 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 | | 4 | rsync + Etap A (12 mies.) + kalibracja | 2 | 1 sesja |
| 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`) | | 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 | **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` | | 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) | **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 | | 7 | Recon przyrostówki IMAP/JMAP | — (po 6) | 1 sesja (poza DoD fazy) |
| 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`) |
**Kryterium ukończenia fazy mailowej:** (a) pełny korpus gmail zchunkowany **Kryterium ukończenia fazy mailowej:** (a) pełny korpus gmail zchunkowany
(bilans domknięty, `parse_errors` na poziomie pojedynczych sztuk jak (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ą domyślnie odpowiada trybem hybrid na `kb.kapala.org`, (e) koperty gmail mają
`entities[type=threading]`. `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 ## 13. Szacunki zbiorcze
- **Dane**: +~496k wierszy `document_chunk` (~271k z embeddingiem, ~225k - **Dane**: +~496k wierszy `document_chunk` (~271k z embeddingiem, ~225k
flagowanych `newsletter`); baza 250 MB → ~57 GB (dysk PIHA: 144 GB wolne, flagowanych `newsletter`); baza 250 MB → ~57 GB (dysk PIHA: 144 GB wolne,
zapas >20×). HNSW rośnie inkrementalnie przy insertach — bez rebuildu. 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,51 h (24 rdzenie) - **GPU/czas runów**: Etap A <1 h e2e; Etap B: parse ~0,51 h (24 rdzenie)
+ embed ~11,5 h (batch 64, zmierzone 818 ms/chunk) + inserty do PIHA. + embed ~11,5 h (batch 64, zmierzone 818 ms/chunk) + inserty do PIHA.
- **Koszty zewnętrzne: 0 USD** (bez streszczeń — Decyzja 6). - **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 ## 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 — 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 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 danych". Ten plan wprowadza je do retrievalu w ~7 sesji i za 0 USD: ponowny

View file

@ -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) # Modul 5, faza 2 — koperta dokumentow + embeddingi + cross-source (RECON + PLAN)
> Status: RECON ZAKONCZONY (2026-07-13), architektura DO ZATWIERDZENIA. Zaden kod nie > 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 (23k chunkow) → **rzedu minut**, nie wymaga rzedu dziesiatek-set chunkow/s. Caly pilot (23k chunkow) → **rzedu minut**, nie wymaga
specjalnego batchowania/partii. specjalnego batchowania/partii.
- **Skala docelowa (70k zalacznikow z maili)**: modul 5 faza-1 to swiadomie **probka, nie - **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) — 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 **to jest waskie gardlo skalowania, nie embedding** — oraz (b) filtra selektywnosci
(decyzja #6). Sam embedding bge-m3 nie bedzie bottleneckiem nawet przy tysiacach (decyzja #6). Sam embedding bge-m3 nie bedzie bottleneckiem nawet przy tysiacach

View file

@ -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) # Moduł 5, faza 3 — warstwa kompilacji (RECON + PLAN)
> Status: RECON ZAKOŃCZONY (2026-07-16), plan DO ZATWIERDZENIA. Zero kodu, zero migracji, > 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), - **kompletność faktów kluczowych** (kwoty, daty, strony, numery),
- **jakość tagów** (trafność + zgodność ze słownikiem). - **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 **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 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 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. pakietu przechodzi.
**Eval-set utrwalony**: `jobs/documents-ingest/eval/queries.yaml` (7 zapytań z pilota **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, `queries.yaml` to jego wersjonowana kopia robocza). Skrypt bramki (read-only, integracyjny,
**nie wchodzi do pytest**): `jobs/documents-ingest/eval/retrieval_eval.py`. **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 `/etc/systemd/system/`, `systemctl enable --now kb-ingest.timer`). Pierwszy
systemd-timer w repo — świadomie host-level, nie kontener (joby potrzebują jednocześnie systemd-timer w repo — świadomie host-level, nie kontener (joby potrzebują jednocześnie
LAN, DB i plików hosta; konteneryzacja nic tu nie daje). LAN, DB i plików hosta; konteneryzacja nic tu nie daje).
- **Harmonogram**: ~~`OnCalendar=*-*-* 03:30`~~**`OnCalendar=0/2:00:00` (co 2 h) od - **Harmonogram**: `OnCalendar=*-*-* 03:30`, `Persistent=true` (nadgania po reboocie).
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.
- **Sekwencja skryptu**: adapter `--apply` → chunk_embed `--apply` - **Sekwencja skryptu**: adapter `--apply` → chunk_embed `--apply`
(`OLLAMA_URL=http://solaria:11434`) → **mail_body_ingest `--only-unchunked`** (dodane (`OLLAMA_URL=http://solaria:11434`) → (po decyzji z pilota, rozszerzenie później:
2026-08-06 — konsument kolejki, którą wypełnia `jobs/mail-imap-sync`; import miękki, więc summarize nowych dokumentów). Log do `/opt/homelab/logs/kb-ingest/run-YYYYMMDD.log`.
venv bez tego pakietu pomija etap zamiast wywracać wrapper) → summarize → embed-summaries.
Log do `/opt/homelab/logs/kb-ingest/run-YYYYMMDD.log`.
- **Tolerancja na SOLARIĘ offline** (`availability_target: medium`): wrapper odróżnia - **Tolerancja na SOLARIĘ offline** (`availability_target: medium`): wrapper odróżnia
„Ollama nieosiągalna" (probe `GET /api/tags` przed embedem; brak → pomiń embed, „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 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 > 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. > (przyrostówka) — wcześniej kompilat byłby fotografią przeszłości.
**Inwariant 7 (dodany 2026-08-27 — rozszerzenie, nie zmiana inwariantów 16
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) ### 8.2 Rozwinięcie wykonawcze (szczegóły, decyzje architektoniczne bez zmian)
**Repozytorium** (rozstrzygnięcie punktu 6 szkicu): osobne repo `kb-wiki` — decyzja 7, **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 ## 9. Poza zakresem fazy 3
Granice planu — wszystko poniżej jest świadomie odłożone, z istniejącym miejscem w 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): `docs/sessions/2026-07-16.md`, backlog operatora):
| Temat | Gdzie zakotwiczone | Kiedy | | Temat | Gdzie zakotwiczone | Kiedy |

View file

@ -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) # Moduł 5, faza 4 — kb-query + UI (RECON + PLAN)
> Status: RECON ZAKOŃCZONY (2026-07-22), plan DO ZATWIERDZENIA. Zero kodu, zero > 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) ### 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** (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 (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 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ą (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 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` 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**. #6) — **żaden nowy certyfikat nie jest potrzebny**.
- **DNS — dwie warstwy, obie trzeba dotknąć** (lekcja `okit-cloudflare-migracja.md` - **DNS — dwie warstwy, obie trzeba dotknąć** (lekcja `okit-cloudflare-migracja.md`
§"WAZNE: split-horizon DNS"): (a) Cloudflare rekord A → Tailscale IP PIHA §"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 ### Decyzja 3 — Linki do źródeł: paperless vs gmail
**Paperless**: URL do dokumentu — **do zweryfikowania na żywym Paperless przed **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: dokumentuje tylko subdomenę, nie ścieżkę). Kandydat wg konwencji paperless-ngx UI:
`https://paper.kapala.org/documents/<id>/details` (Angular routing) — `envelope_id` `https://paper.kapala.org/documents/<id>/details` (Angular routing) — `envelope_id`
`paperless:<id>` już niesie surowy `<id>` do wstawienia. Krok implementacji: jeden `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). - Pole zapytania + submit (Enter albo przycisk).
- Wyniki grupowane po `envelope_id` (dokument), w obrębie dokumentu chunki - Wyniki grupowane po `envelope_id` (dokument), w obrębie dokumentu chunki
posortowane po `dist`. 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.450.55` żółty, `> 0.55` zweryfikowane bramką): `dist < 0.45` zielony, `0.450.55` żółty, `> 0.55`
**nie renderować wyniku**, tylko komunikat "brak odpowiedzi w KB" (żółta/czerwona **nie renderować wyniku**, tylko komunikat "brak odpowiedzi w KB" (żółta/czerwona
strefa nadal renderuje wynik z ostrzeżeniem wizualnym; czerwona = brak sensownego strefa nadal renderuje wynik z ostrzeżeniem wizualnym; czerwona = brak sensownego

View file

@ -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) # Decyzje do podjęcia — filar dokumentów (moduły 2/3/4)
> Zbiorcza lista decyzji z przygotowania configów (2026-07-06, branch > 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 + rsync/borg → SOLARIA (2 TB, ta sama LAN). Retencja: 7 dziennych +
4 tygodniowe + 6 miesięcznych. Offsite (np. restic → chmura) zostaje jako 4 tygodniowe + 6 miesięcznych. Offsite (np. restic → chmura) zostaje jako
future-note, poza zakresem tego etapu. Cron/skrypt deployowy powstaje przy 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 — - **4. Redis brokera: `requirepass`.** Broker (6380) dostaje hasło —
`PAPERLESS_REDIS_PASSWORD` w `.env` po obu stronach (paperless@PIHA, `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 + SOLARIA po NFS) świadomie zaakceptowane — indeks jest odtwarzalny
(`document_index reindex`), oryginałom nic nie grozi. Bez zmian w (`document_index reindex`), oryginałom nic nie grozi. Bez zmian w
configu; fallback-worker na PIHA zostaje. Szczegóły: 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` - **6. Domeny: `kapala.org` (mesh, prywatne).** `paper.kapala.org`
(Paperless), `cloud.kapala.org` (Nextcloud) — potwierdzone, `*.okit.pl` (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, maintainerów paperless-ngx (nieoficjalnie wspierany): ten sam obraz,
`command: celery --app paperless worker`, wspólny Redis+Postgres+storage, `command: celery --app paperless worker`, wspólny Redis+Postgres+storage,
identyczne ścieżki kontenerowe i numeryczny UID po obu stronach. Pełny 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 - Storage dokumentów na PIHA; NFS export → SOLARIA po LAN
(192.168.31.5 → 192.168.31.70), nie Tailscale. (192.168.31.5 → 192.168.31.70), nie Tailscale.
- AOF w Redis brokera (kolejka przeżywa restart — zero utraty zadań). - AOF w Redis brokera (kolejka przeżywa restart — zero utraty zadań).

View file

@ -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 # Service Lifecycle and Recovery
This document defines the lifecycle of a service in the homelab and the procedures for operational 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`. - `docker compose down`.
- Archive `/opt/homelab/data/<service>` if necessary. - 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 ## Persistent Data Conventions
- **Data**: `/opt/homelab/data/<service>` - Primary persistent state. - **Data**: `/opt/homelab/data/<service>` - Primary persistent state.

View file

@ -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 # Networking
## Description ## Description

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: runbook
visibility: public
status: active
updated: 2026-05-12
links: []
---
# Node Onboarding Workflow # Node Onboarding Workflow
This document describes the process of onboarding a new Linux machine into the homelab platform. This document describes the process of onboarding a new Linux machine into the homelab platform.

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: subsystem
visibility: public
status: active
updated: 2026-06-17
links: []
---
# Observer Runtime # 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. 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
View 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?

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: subsystem
visibility: public
status: active
updated: 2026-05-11
links: []
---
# Service Model and Healthchecks # Service Model and Healthchecks
This document defines the normalized service model for the homelab. This document defines the normalized service model for the homelab.

View file

@ -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 # Services
## Description ## Description

View file

@ -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 # SESSION: Budowa planner-agent — LLM-based diagnostics
**DATA:** 2026-05-27 **DATA:** 2026-05-27

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: session-log
visibility: private
status: active
updated: 2026-05-27
links: []
---
# SESSION: Stabilizacja systemu wieloagentowego homelabu # SESSION: Stabilizacja systemu wieloagentowego homelabu
**DATE:** 2026-05-27 **DATE:** 2026-05-27

View file

@ -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) # Sesja 2026-06-08 — onboarding LUSTRO (RPi4 / Magic Mirror / KEN)
## Cel ## Cel
@ -90,7 +81,7 @@ przez Tailscale działa bezhasłowo. Verify czysty (arch=aarch64).
## Learnings ## 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` - 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) - istniejący node z userem uid=1000: użyj go zamiast tworzyć `oskar` (kolizja uid)

View file

@ -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 # Sesja 2026-06-09 — flota recovery + LUSTRO register
## Cel ## Cel
@ -130,4 +121,4 @@ Docelowo: osobny worktree per task.
## Tech-debt złapany w sesji ## Tech-debt złapany w sesji
→ wpisany do `kb/phases/backlog.md` → wpisany do `docs/backlog.md`

View file

@ -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 # Sesja 2026-06-10/11 — lustro SSH shipping fix + ha-diag-agent piha
## Cel ## 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, (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 ż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. 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 ## 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, poison-quarantine review, `--omit-dir-times`, stale komentarz node_agent.py,
shipping success na `logger.debug`, event-bloat lustro na VPS). 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 d7e0d31 fix(ha-diag-agent): remove host port mapping for 8087
### Files changed ### Files changed
kb/runbooks/ha-diag-agent-deploy.md | 4 ++-- services/ha-diag-agent/DEPLOY.md | 4 ++--
kb/services/ha-diag-agent.md | 4 ++-- services/ha-diag-agent/README.md | 4 ++--
services/ha-diag-agent/docker-compose.yml | 3 --- services/ha-diag-agent/docker-compose.yml | 3 ---
services/ha-diag-agent/service.yaml | 3 --- services/ha-diag-agent/service.yaml | 3 ---
4 files changed, 4 insertions(+), 10 deletions(-)) 4 files changed, 4 insertions(+), 10 deletions(-))

View file

@ -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) # Sesja 2026-06-17 — KB foundations (etap 1 maili)
## Cel ## Cel
@ -120,14 +111,14 @@ services/kb-postgres/service.yaml
services/kb-postgres/env.example services/kb-postgres/env.example
services/kb-postgres/healthcheck.sh services/kb-postgres/healthcheck.sh
services/kb-postgres/init/001_envelope.sql 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/runtime/kb-postgres/docker-compose.override.yml
hosts/solaria/services.yaml hosts/solaria/services.yaml
inventory/topology.yaml inventory/topology.yaml
packages/kb-mail/pyproject.toml packages/kb-mail/pyproject.toml
packages/kb-mail/src/kb_mail/{__init__,envelope,db,archive}.py 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 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) CLAUDE.md (sekcja Shared Python Libraries)
docs/sessions/2026-06-17-kb-foundations.md docs/sessions/2026-06-17-kb-foundations.md
``` ```

View file

@ -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 # Sesja 2026-06-17 — Vikunja OIDC+GitOps · Observer heartbeat-TTL · panel-source
## Zrobione i wdrożone ## Zrobione i wdrożone

View file

@ -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 # Sesja 2026-06-22 — KB spine relokowany na PIHA + przygotowanie hosta
## Cel ## Cel

View file

@ -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 # Sesja 2026-06-22 — decyzja: Prometheus jako źródło prawdy dla liveness floty
## Cel ## Cel

View file

@ -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) # Sesja 2026-06-24 — KB etap 2: importer Gmail (kod gotowy)
## Cel ## Cel
@ -171,7 +162,7 @@ jobs/gmail-bulk-import/pyproject.toml
jobs/gmail-bulk-import/tests/test_importer.py jobs/gmail-bulk-import/tests/test_importer.py
hosts/piha/capabilities.yaml hosts/piha/capabilities.yaml
.gitignore .gitignore
kb/subsystems/kb-overview.md (etap 2 gotowy, konwencja jobs/) docs/kb/kb-00-overview.md (etap 2 gotowy, konwencja jobs/)
kb/subsystems/kb-mail-pillar.md (§8 krok 2 = kod gotowy) docs/kb/kb-01-email-design.md (§8 krok 2 = kod gotowy)
docs/sessions/2026-06-24-kb-gmail-importer.md docs/sessions/2026-06-24-kb-gmail-importer.md
``` ```

View file

@ -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 # Sesja 2026-06-24 — fleet-prometheus etap 1: scaffold + deploy-node hostname fix
## Cel ## 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 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 stworzone innym `project-name` niż `deploy-node.sh` oczekuje; Recreate pada na stale

View file

@ -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 # Sesja 2026-06-25 — KB etap 2: bulk import Gmail uruchomiony
## Cel ## Cel

View file

@ -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 # Sesja 2026-06-25 — fleet-prometheus etap 1: uruchomienie na VPS + incydent mózgu
## Cel ## Cel

View file

@ -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 # Sesja 2026-06-26 — fleet-prometheus etap 2: targety floty + zamknięcie buga deploy.sh vps
## Cel ## Cel

View file

@ -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 # Sesja 2026-06-30 — Inwentaryzacja floty + gaszenie dysku SATURN
## Cel ## 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 + - 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 free/df/nproc) z 4 dostępnych nodów: PIHA, VPS, SOLARIA, SATURN. LUSTRO+CHELSTY
offline (timeout :22) -> oznaczone UNREACHABLE. 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: - Kluczowe ustalenia:
- **forgejo** biega na PIHA (always-on), ale `service.yaml owner_node=saturn` — rozjazd - **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 - **mosquitto** biega na VPS, `service.yaml owner=piha`, na PIHA go nie ma

View file

@ -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 # 2026-06-30 — Migracja kapala.org → Cloudflare + wildcard DNS-01, HA i Immich na mesh
## Cel ## Cel

View file

@ -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 # Sesja 2026-06-30 — fleet-prometheus liveness: reguły NodeDown + wpięcie watchdog→Prometheus
## Cel ## Cel

View file

@ -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 # 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) ## 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, - 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 /opt/llm-gateway, proxy do Ollama@SOLARIA) — NIE martwy; immich MUSI byc 24/7
na PIHA (SOLARIA sesyjna) — rekomendacja przeniesienia wykreslona. na PIHA (SOLARIA sesyjna) — rekomendacja przeniesienia wykreslona.

View file

@ -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 # Sesja 2026-07-02 — recon-weryfikacja inwentaryzacji + rozbrojenie trzech min
## Cel ## Cel
@ -21,7 +12,7 @@ Sesja tylko-recon + minimalne fixy; bez deployów nowych feature'ów.
### Recon-weryfikacja inwentaryzacji floty (commit `57a6dff`, read-only) ### 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**: **Bilans 23 rozjazdów z audytu 2026-06-30**:
- **20 wciąż aktualnych** — nic się samo nie naprawiło. - **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 nie rezolwuje z SOLARII; brak formalnego override mem_limit fleet-prometheus
w `hosts/vps/runtime/` (siedzi w bazowym compose — kosmetyka). 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.
--- ---

View file

@ -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 # Sesja 2026-07-06 — cutover liveności na Prometheus: recon starego toru + Etap 0 udowodniony w boju
## Cel ## Cel
@ -22,7 +13,7 @@ Prometheus → brain-watchdog → Telegram, którego brakowało od 2026-06-30.
### Recon cutoveru — wmergowany (commit `d94bb38`) ### 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: zero zmian w kodzie). Kluczowe ustalenia:
- **Cutover to podmiana klasyfikacji liveności w JEDNYM miejscu** - **Cutover to podmiana klasyfikacji liveności w JEDNYM miejscu**

View file

@ -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) # Session log 2026-07-07 — Immich upload fix / pimain cleanup (kontynuacja 2026-07-03)
## Kontekst ## Kontekst

View file

@ -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 # Sesja 2026-07-07 — okit.pl Faza 1 (wildcard cert) + przepiecie 9 hostow
## Kontekst ## Kontekst

View file

@ -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) # Sesja 2026-07-09 — KB configi (9 decyzji) + wzorzec dzielenia plikow (Nextcloud twierdza + Gokapi)
## Kontekst ## Kontekst

View file

@ -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 # Sesja 2026-07-10 — Deploy 1 Paperless (DZIALA) + swap PIHA + npm-API tool w akcji
## Glowne osiagniecie: Paperless serwis LIVE ## Glowne osiagniecie: Paperless serwis LIVE

View file

@ -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 # 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 ## 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). realnej uzytecznosci. Dopiero POTEM dopelniac importy (reszta Takeout, zdjecia, transakcje).
## TODO nastepne ## 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 - Import probki zalacznikow z maili (kilkaset, nie 70k) — do zbudowania RAG
- Interfejs pytan / RAG — warstwa uzytkowa - Interfejs pytan / RAG — warstwa uzytkowa
- Deploy 3 (Nextcloud), Deploy 4 (Gokapi) — configi gotowe, czekaja - Deploy 3 (Nextcloud), Deploy 4 (Gokapi) — configi gotowe, czekaja

View file

@ -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 # Sesja 2026-07-15 — Prometheus cutover Etap 2 (analiza GO) + domknięcie rodziny bugów event-pipeline/checkpoint
## Kontekst ## 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 istniejącego pliku — 0 = leksykalne "starszy niż checkpoint" = dokładnie ten
poison), migracja starych path-checkpointów przy starcie. Zdeployowany na VPS poison), migracja starych path-checkpointów przy starcie. Zdeployowany na VPS
(observer `StartedAt` 07-14). Zweryfikowany dziś jako kompletny i zdeployowany. (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"). leksykalnej").
### 2. docs(infra) analiza Etapu 2 shadow-run (Fable, `8fec62d`) ### 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 `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 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 21:34 UTC). Wzorzec `event=fresh prom=down` to **nie** "żywy węzeł niewidziany

View file

@ -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`) ### 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). 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).

View file

@ -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 # Sesja 2026-07-17/18 — KB faza 3: kroki 2-5 DOMKNIĘTE
## Krok 2 — migracja 004 + pilot streszczeń A/B (17.07) ## Krok 2 — migracja 004 + pilot streszczeń A/B (17.07)

View file

@ -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) # 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. **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.

View file

@ -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 # 2026-07-22 — HA: incydent dwóch mózgów, cutover ken, archiwum legacy
## Odkrycie ## Odkrycie

View file

@ -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) # 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. **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.

View file

@ -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 # 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, 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: test E2E padł: agent rzucał `[Errno 13] Permission denied:
/opt/homelab/actions/dispatch` co cykl. Root cause to ZNANY, POWRACAJĄCY /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ń (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 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 (= 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 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 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 produkcji (PIHA), bez SSH z control-plane do węzłów. Publiczna dziura
autoryzacji na `operator_ui.py:18180` zamknięta (bind ograniczony do 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 zepsutego JSON, uprawnienia `actions/` na innych węzłach, brak twardego checka
`.env`/`TAILSCALE_BIND_IP` w `deploy-local.sh`, brak autoryzacji w `.env`/`TAILSCALE_BIND_IP` w `deploy-local.sh`, brak autoryzacji w
`operator_ui.py`, zapchana approval queue przez `alert_only`, brak `operator_ui.py`, zapchana approval queue przez `alert_only`, brak

View file

@ -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 # 2026-07-22/23 — HA: adapter api, import ken, deploy.sh, otwarcie fazy 1
## Wykonane ## Wykonane

View file

@ -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 # 2026-07-23 (cd.) — HA: klima E2E, audyt Fable, fix-pack 1
## Klima salonowa — pierwsza automatyzacja LLM przez repo (E2E) ## Klima salonowa — pierwsza automatyzacja LLM przez repo (E2E)

View file

@ -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) # 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 frontend i `/search` już LIVE na PIHA (port 8230) od sesji 2026-07-22. Zero
zmian w kodzie kb-query w tej sesji. 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 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 forward-auth/reverse-proxy-level auth** — NPM community edition go nie ma
(sprawdzone: brak `oauth2-proxy`/`authelia`/`forward_auth` w kodzie repo poza (sprawdzone: brak `oauth2-proxy`/`authelia`/`forward_auth` w kodzie repo poza
wzmiankami "przyszła opcja" w `kb/subsystems/kb-documents-pillar.md` i wzmiankami "przyszła opcja" w `docs/kb/kb-02-documents-design.md` i
`kb/nodes/vps.md`). Wszystkie 3 precedensy (paperless/nextcloud/vikunja) `hosts/vps/README.md`). Wszystkie 3 precedensy (paperless/nextcloud/vikunja)
robią OIDC **wewnątrz aplikacji**. kb-query nie ma dziś żadnego logowania. robią OIDC **wewnątrz aplikacji**. kb-query nie ma dziś żadnego logowania.
Zgodnie z instrukcją zadania: **nie budowano** nowego komponentu auth. 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 ## 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". auth odłożone) zastępuje starą notatkę "not wired up yet".
- `docs/sessions/2026-07-23-kb-f4-ingress.md` — ten dokument. - `docs/sessions/2026-07-23-kb-f4-ingress.md` — ten dokument.

View file

@ -1,12 +1,3 @@
---
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 # 2026-07-27 — HA: legacy zamkniete, kasacje, pimirror, sonda Zigbee
## Wykonane ## Wykonane

View file

@ -1,15 +1,6 @@
---
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) # 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`):** > **Dopisek redakcyjny (2026-07-30, dedup — `docs/kb/modules/05-fallback-dedup-raport.md`):**
> implementacja kodu z tej sesji (`app/fallback.py`, branch `task/kb-f4-fallback`, 3d4ee38) > 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 > 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 > kroku planu (e7625cd, `app/embed_router.py`, 2026-07-29) i to ona biega na PIHA. Ten log
@ -23,7 +14,7 @@ links: []
> Z delty brancha uratowano ponadto: `retrieval_eval.py --transport http` (plan §2 D6/§9) > 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`. > i luki testowe T1/T2 przeniesione do `test_embed_router.py`.
**Zakres**: `kb/phases/kb-m5-faza4.md` §2 decyzja 2 / §5 — aktywny fallback **Zakres**: `docs/kb/modules/05-faza4-plan.md` §2 decyzja 2 / §5 — aktywny fallback
embedu, ostatni brakujący element rdzenia fazy 4 (frontend i ingress LIVE od 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 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. DB, zero zmian w `kb_retrieval`'s retrieval logice — wyłącznie warstwa embed + health.

View file

@ -1,12 +1,3 @@
---
okf: "0.1"
type: session-log
visibility: private
status: active
updated: 2026-07-28
links: []
---
# Session log 2026-07-28 # Session log 2026-07-28
## Session 21:59 ## Session 21:59
@ -19,8 +10,8 @@ e8aa3e3 docs(architecture): recon multiagent 2026-07-27
### Files changed ### Files changed
``` ```
kb/phases/subsystem-a-naprawa.md | 60 +++ docs/architecture/PLAN-subsystem-a-2026-07-28.md | 60 +++
kb/subsystems/recon-multiagent.md | 551 +++++++++++++++++++++++ docs/architecture/RECON-multiagent-2026-07-27.md | 551 +++++++++++++++++++++++
2 files changed, 611 insertions(+) 2 files changed, 611 insertions(+)
``` ```

View file

@ -1,12 +1,3 @@
---
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) # 2026-07-30 — HA: legacy zamknięte, MCP read-only (faza 2a)
## Legacy — finał ## Legacy — finał

View file

@ -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 S1S4 (`cb8a19d`):
- `retrieval_eval.py --transport http`
- session log 27.07
- testy luk T1T3
- 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.450.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. **R1R3 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 R1R3 (unfiltered prune) — eskalacja do subsystemu A;
mitygacja M1 na SOLARII na czas backfillu.

View file

@ -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` (R1R3 + 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).

View file

@ -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_

View file

@ -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.

Some files were not shown because too many files have changed in this diff Show more