Update (2026-10-07): An epilogue reflecting on this project's current direction — what has been demonstrated so far, the practical limits of today's mass-market AI tooling, and why closed, connected deployments look like the protocol's strongest near-term use case — has been published separately for anyone researching this work further: CM AI Docking Port: From an AI Integration Experiment to a Protocol for Connected Systems.

CM AI Docking Port

Reception Protocol Spec v1.0 (usklajena specifikacija)

Nastane 2026-09-29 z združitvijo: Master Development Prompt v0.1 (osnovni koncept), Gemini addon ("Planning Draft – implementacijski koraki"), Grok addon ("Planning Draft – Protocol & First Files"). Ta dokument je zdaj referenčna verzija — v0.1 in oba addona ostanejo kot zgodovina razmisleka, ne kot ločena navodila.

Danes nastane samo ta dokument. Prve datoteke (manifest.json, index.html, reception.php) pridejo naslednjič, na dogovorjeni lokaciji (glej §12).


1. Namen (nespremenjeno iz v0.1)

CM AI Docking Port je univerzalni vstopni mehanizem, skozi katerega AI agent odkrije CM Ecosystem in z njim komunicira preko standardnega HTTP/JSON protokola — brez CM-specifičnega vtičnika.

Ni nov CM, ni nov poslovni motor, ni centralni marketplace, ni AI agent. CM ostaje edini vir resnice (source of truth). Docking Port je tanek protokolarni/usmerjevalni sloj nad obstoječimi javnimi API-ji (dokaz koncepta: GET /app/public/api/availability_multi.php, ki že vrača čist, semantično razumljiv JSON — glej realno implementacijo na /var/www/html/app/public/api/availability_multi.php).


2. Ključno usklajevalno načelo (Grok, potrjuje in izostri v0.1 §6/§14)

To je edina prava sprememba/izostritev glede na v0.1 — vse ostalo se le dopolnjuje.

AI agent nikoli ne kliče drugih nodov ali obstoječih poslovnih API-jev neposredno. Agent komunicira izključno z Reception svojega vstopnega noda.

AI Agent
   │
   ▼
CM AI Docking Port (manifest + docs) — odkritje
   │
   ▼
Reception — EDINA točka komunikacije
   │
   ├── local CM (kliče availability_multi.php interno)
   ├── nearby nodes (forward envelope, agent tega ne vidi)
   └── deeper nodes (znotraj max_depth)
   │
   ▼
agregiran odgovor → nazaj do AI Agent

Model je "pošta": agent odda zahtevek na Reception, počaka, dobi en sam agregiran odgovor. Agent ni usmerjevalec in ne pozna interne topologije mreže — to ohranja decentralizacijo (v0.1 §9) in hkrati preprečuje, da bi agent postal vektor za zanke, zlorabo ali fragmentacijo protokola.

Vse iz v0.1 §7–§12 (geografski routing, verižna poizvedba, globina, zaščita pred kroženjem) ostaja veljavno — le izvaja ga Reception rekurzivno, agent tega procesa ne vidi in vanj ne posega.


3. Dve ravni dostopa (nespremenjeno iz v0.1 §5)


4. Geografija kot routing princip (nespremenjeno iz v0.1 §7–§8)

Vstopna točka ≠ cilj iskanja. Agent lahko potrka na CM v Radovljici in vpraša za Ljubljano — Reception usmerja glede na target.location, ne glede na lokacijo vstopnega noda.


5. Request Envelope — usklajena končna shema

Osnova iz v0.1 §13, dopolnjena s konkretnimi vrednostmi iz Grok addona.

{
  "protocol": "CM-AI-Docking-Port",
  "version": "0.1",
  "request_id": "req-2026-08-10-abc123",
  "request_expiry": "2026-08-10T12:00:00Z",
  "origin": "ai-agent",
  "target": {
    "location": { "lat": 46.05, "lon": 14.50, "label": "Ljubljana" },
    "radius_km": 30
  },
  "query": {
    "type": "availability",
    "from": "2026-08-10",
    "to": "2026-08-16",
    "guests": 2,
    "units": null
  },
  "limits": {
    "max_depth": 2,
    "max_distance_km": 40,
    "max_results": 10
  },
  "visited": []
}

Pravila:


6. Response Envelope — nova dopolnitev (Grok; v0.1 tega ni imel eksplicitno)

v0.1 §15 je pokazal samo surov odgovor availability_multi.php. Manjkala je ovojnica za agregiran, multi-node odgovor. To je zapolnjena vrzel:

{
  "protocol": "CM-AI-Docking-Port",
  "version": "0.1",
  "request_id": "req-2026-08-10-abc123",
  "ok": true,
  "partial": false,
  "search_meta": {
    "entry_node": "CM-AI-DEMO-001",
    "target_location": { "lat": 46.05, "lon": 14.50, "label": "Ljubljana" },
    "nodes_queried": 1,
    "nodes_reached": 1,
    "max_depth_used": 0,
    "max_depth_limit": 2
  },
  "results": [
    {
      "node_id": "CM-AI-DEMO-001",
      "depth": 0,
      "source": "local_availability_api",
      "freshness": { "generated_at": "2026-09-28T18:18:29+00:00" },
      "data": { "...": "neposreden izhod availability_multi.php, brez reinterpretacije" }
    }
  ],
  "errors": []
}

Pravilo (v0.1 §14 potrjeno): Reception ne reinterpretira poslovnega odgovora — data je surov izhod obstoječega API-ja, samo ovit s provenance (node_id, depth, source, freshness). partial: true + razlog, kadar kak node ni bil dosegljiv (v0.1 §20 — brez lažnega vtisa popolne pokritosti).


7. Manifest — usklajena končna shema

Združuje v0.1 §16 (osnovna struktura) z Grokovo dopolnitvijo (capability-level metadata: side_effect, requires_consent — neposredno iz v0.1 §22 zahteve).

Objavljen na dveh mestih (ena vsebina, ena morda redirect na drugo):

/.well-known/cm-ai-docking-port.json
/app/public/ai/manifest.json
{
  "protocol": "CM-AI-Docking-Port",
  "version": "0.1",
  "service": {
    "name": "CM Ecosystem Reception",
    "type": "ai-entry-point",
    "node_id": "CM-AI-DEMO-058",
    "location": { "label": "Lancovo / Radovljica area (Demo 58)", "lat": 46.34, "lon": 14.17 }
  },
  "capabilities": {
    "local": [
      { "id": "availability", "side_effect": false, "requires_consent": false,
        "description": "Check unit availability for a date range" }
    ],
    "ecosystem": [
      { "id": "accommodation-discovery", "side_effect": false, "requires_consent": false,
        "description": "Geographic discovery across participating CM nodes via Reception" }
    ]
  },
  "reception": { "method": "POST", "endpoint": "/app/public/ai/reception.php", "content_type": "application/json" },
  "documentation": {
    "human": "/app/public/ai/index.html",
    "machine": "/app/public/ai/manifest.json",
    "spec": {
      "html": "/app/public/ai/spec.html",
      "markdown": "/app/public/ai/spec.md",
      "note": "Full working design specification. SKLENJENO sections are settled; OSNUTEK sections are open drafts.",
      "spec_synced_at": "<ISO8601, auto-updated on each publish>"
    }
  },
  "limits_default": { "max_depth": 2, "max_distance_km": 50, "max_results": 20 },
  "notes": [
    "AI agents must always communicate with Reception.",
    "Do not call internal business APIs directly.",
    "Entry point and search target are separate concepts."
  ]
}

Dodano 2026-09-30 (Dvojček/Gemini predlog, uporabnik potrdil): manifest zdaj eksplicitno referencira ta dokument (documentation.spec, obe obliki) in nosi spec_synced_at — kdaj je bila javna kopija specifikacije nazadnje osvežena iz lokalnega delovnega izvoda. Objava ni ročna: ~/Dokumenti/Guestbook/publish_ai_docking_spec.sh v enem koraku redigira LAN podrobnosti, pretvori .md v stilizirano spec.html, deploya oboje na instanco 58, in posodobi spec_synced_at na obeh manifestih (/app/public/ai/manifest.json in /.well-known/cm-ai-docking-port.json). Poganja se ročno ob vsaki vsebinski spremembi tega dokumenta — ni (še) avtomatiziran cron.


8. Vloga availability_multi.php (nespremenjeno, potrjeno v0.1 §15 + Gemini §Korak2)

Reception (lokalni node)
   │  preveri: je target v dosegu? je depth 0?
   ▼
kliče lastni GET /app/public/api/availability_multi.php?units=...&from=...&to=...&mode=basic
   │
   ▼
vzame JSON odgovor, vstavi v results[] z provenance (§6)
   │
   ▼ (če max_depth > 0 in potrebno)
forwarda envelope sosedom, zbira odgovore
   ▼
vrne Response Envelope agentu

Docking Port ne posega v availability_multi.php ali katerikoli drug obstoječi endpoint. Klic je interni, agent ga ne vidi.


9. Varovalke in omejitve (nespremenjeno, potrjeno iz vseh treh virov)

Iz v0.1 §11, §12, §19, §21, §22, §25, dodatno eksplicirano v Gemini addonu kot "Opozorilo za razvijalca":

Prepovedano v prvi fazi (MVP):

Obvezno:


10. Human-readable dokumentacija (/app/public/ai/index.html) — nespremenjeno iz v0.1 §17

Kratka stran, berljiva ljudem in LLM-jem (jasni <h1>/<code> bloki, brez težkega JS-a), ki razloži: kaj je Docking Port, kaj je Reception (edina točka za AI), primer Request/Response Envelope, da agent ne kliče notranjih API-jev neposredno, omejitve, in da gre za read/discovery MVP.


11. MVP koraki — usklajen zaporedni načrt

Združitev v0.1 §23–§25 (postopnost, "ne graditi prezgodaj") + Gemini addon (4 konkretni koraki) + Grok addon (datotečni seznam).

Korak 0 (danes, 2026-09-29): ta usklajena specifikacija ✅

Korak 1: Static Entry
  /.well-known/cm-ai-docking-port.json  ✅ DEPLOYANO 2026-09-29 (uporabnik ročno naredil sudo mkdir
                                            +chown na instanci 58, Claude Code postavil vsebino),
                                            potrjeno: https://ai-cmfree.duckdns.org/.well-known/cm-ai-docking-port.json (HTTP 200)
  /app/public/ai/manifest.json          ✅ DEPLOYANO 2026-09-29 (node_id CM-AI-DEMO-058,
                                            usklajen skozi Claude+Gemini+Grok+GPT review),
                                            potrjeno javno dostopno: https://ai-cmfree.duckdns.org/app/public/ai/manifest.json (HTTP 200)
  /app/public/ai/index.html             ✅ DEPLOYANO 2026-09-29 (Claude Code pripravil vsebino,
                                            uporabnik obrnil vrstni red: tokrat Claude pred skupino),
                                            potrjeno: https://ai-cmfree.duckdns.org/app/public/ai/index.html (HTTP 200)

Korak 1 zaključen v celoti.

Korak 2: Reception (single node, depth 0)          ✅ DEPLOYANO 2026-09-29
  /app/public/ai/reception.php
  - koda: Gemini prvi osnutek → GPT (izčrpen review proti živemu API-ju, 5 popravkov) → Claude
    (preverjeno: curl extenzija na 58 obstaja, Apache ima en sam catch-all vhost torej Host-header
    ni nujen a je neškodljiv, `availability_multi.php` dejansko vrača `generated_at`) → deployano
    z `.tmp` + `php -l` lint + atomski `mv`, brez okna polomljene sintakse v živo
  - popravki proti Geminijevemu prvotnemu osnutku: `units: null` → interni `units=all`; lokalni
    fail = `ok:false` (ne `partial:true`, to je rezervirano za Korak 3); VEDNO Response Envelope
    tudi na 400/405; samo POST (+OPTIONS/CORS); agent ne vidi internega URL-ja/HTTP kode/curl napake
  - Claude dodal: `error_log()` server-side ob curl napaki (samo v log, agent ga ne vidi)
  - testirano 5 scenarijev na živem endpointu: veljaven zahtevek (ok:true, L1 available),
    GET→405+envelope, napačen protocol→400+envelope, manjkajo from/to→200+missing_dates,
    nepodprt query.type→200+unsupported_query_type — vsi po pričakovanjih
  - endpoint: https://ai-cmfree.duckdns.org/app/public/ai/reception.php
  - index.html status badge posodobljen na "live" ŠELE po uspešnem testu (ne hkrati z uploadom)

Naslednje: Korak 3 (neighbour table + geografski forwarding) — podroben načrt v §16, testni scenarij v §13.

Šele po potrjenem Koraku 4 razmišljati o širšem discoveryju, kompleksnejšem routingu, cache-u, globalnih indeksih (v0.1 §25 — to velja nespremenjeno).


12. Lokacija izvedbe — odločeno danes (2026-09-29)

Prve dejanske datoteke (Korak 1–2) gredo na ai-cmfree demo, instanca 58 (ai-cmfree.duckdns.org → GN7 reverse-proxy → 58; glej memorijo ai_bot_demo_public_access) — brez pravih podatkov, robots.txt že odprt, obstoječa promotion-testing površina. To je edino okolje kjer read/discovery MVP nima tveganja glede realnih gostov/rezervacij.

Realni endpoint availability_multi.php (referenca za obliko odgovora, §1/§8 zgoraj) je preverjen na GN7 (/var/www/html/app/public/api/availability_multi.php) — instanca 58 ima svojo kopijo istega API-ja, path je treba potrditi ob dejanski implementaciji (ni nujno identičen /app/public/api/...).


13. Testni scenarij (usklajen iz v0.1 §24 + Gemini + Grok)

Tri testne CM/node instalacije na različnih lokacijah (Korak 4). Preveriti:

  1. Agent najde manifest, prebere capabilities in reception endpoint.
  2. Zahtevek za lokalno availability (depth 0) vrne pravilen Response Envelope.
  3. Zahtevek s ciljem na drugi lokaciji pravilno potuje Node A → relevanten sosed in vrne rezultat od tam.
  4. visited prepreči zanko (A vabi B, B vabi A).
  5. max_depth ustavi širjenje na pravem koraku.
  6. request_id prepreči podvojeno obravnavo istega zahtevka.
  7. Izpad enega noda (B nedosegljiv) ne prekine iskanja — rezultat vsebuje partial: true in jasno pove obseg, ne pretvarja se, da je pregledan cel Ecosystem.
  8. Odgovor je agentu semantično jasen brez dodatne razlage.

Testna infrastruktura — pripravljena 2026-09-29

Tri instalacije za zgornji test:

Node node_id Lokacija (lat/lon) Kaj je URL
A (pravi) CM-AI-DEMO-058 Lancovo/Radovljica (46.34, 14.17) Prava CM instanca, prava availability_multi.php podatka https://ai-cmfree.duckdns.org/app/public/ai/reception.php
B (stub) CM-AI-STUB-BLED Bled (46.3683, 14.1146) Test stub na 57, fiksni izmišljeni podatki, ni prave CM logike [interno testno omrežje — LAN, ni javno dostopno]
C (stub) CM-AI-STUB-LJ Ljubljana (46.0569, 14.5058) Test stub na 57, fiksni izmišljeni podatki, ni prave CM logike [interno testno omrežje — LAN, ni javno dostopno]

Stub node-a B in C implementirata isti Reception protokol (envelope, POST-only, invalid_envelope/missing_dates/unsupported_query_type obravnava) kot pravi node, a vedno odgovorita lokalno (depth 0, brez lastnega forwardinga) s fiksnimi izmišljenimi podatki — nikoli ne kličeta prave CM logike. Namen: omogočiti resnično testiranje max_fanout/sort-po-razdalji/visited mehanike na pravem omrežju (HTTP klici med pravima strežnikoma), preden se karkoli od tega postavi na produkcijo.

Preverjeno 2026-09-29: oba stub node-a odgovarjata pravilno lokalno na 57; node A (58) dostopa do obeh preko LAN (interno LAN, IP namerno ni objavljen) — HTTP 200, potrjen doseg. Testna infrastruktura za §13 scenarij je s tem pripravljena; sama forwarding koda (Korak 3) na node A še ni napisana.


14. §14 — ZAPRTO / SKLENJENO (formalno 2026-09-30, po Gregovem A→B→C okviru, potrjeno Claude+Grok+GPT+Gemini)

Prvotno odprta vprašanja (Grok predlog, Claude dopolnil, Gemini "Dvojček" zaprl — vsi trije se strinjajo):

Nova odprta točka (predlog uporabnika, 2026-09-29, še NI usklajena s celotno skupino)

Globina (depth) ≠ širina (fanout) — spec trenutno omejuje samo, kolikokrat se zahtevek sme odbiti naprej (max_depth), ne pa, koliko sosedov Reception na enem hopu dejansko pokliče. Brez omejitve širine je promet eksponenten: pri kuriranih 5 sosedih in max_depth: 2 lahko en agentov zahtevek sproži do 5 + 25 = 30 vzporednih klicev po mreži — to je bolj realno "open relay" tveganje kot tisto, ki ga rešuje rate limit na enem nodu, ker se pomnoži po celotni mreži.

Uporabnikov sklep (po lastnem premisleku je zavrnil agentovo nastavljivo globino IN geografsko spremenljivo globino kot nesmiselni — oboje bi obremenilo enega od udeležencev): edino kar šteje je globina 0 (lasten strežnik) in globina 1 (prvi najbližji nodi) — in ključno vprašanje ni "kako globoko", ampak "s koliko nodi node komunicira vzporedno na enem hopu".

Predlog za razpravo (Claude, še ne implementacija):

SKLENJENO 2026-09-29 — Gemini potrdil max_fanout predlog in dodatno razložil "jump over" mehaniko (usmerjanje po Haversine razdalji do target.location, ne do vstopne točke): node sortira svoje sosede iz nodes.json po razdalji do cilja in vzame prvih max_fanout, s čimer lahko zahtevek v enem skoku "preskoči" geografsko bližje, a smerno nerelevantne sosede (primer: Piran → Rogla preskoči Portorož/Izolo/Koper, gre direktno proti Ljubljani). Priporočilo za operaterje: nodes.json naj poleg 2-3 mikrolokalnih sosedov vsebuje vsaj 1 "regionalni hub" (večje, centralno vozlišče), da jump-over deluje učinkovito na daljših razdaljah. To ni kršitev prepovedi centralnega registra (§9) — hub je navaden node, izbran prostovoljno s strani operaterja, ne vsiljena infrastruktura.

Vsi štirje (Claude, Grok, GPT, Gemini) so s tem §14 v celoti usklajen. Zelena luč za pripravo nodes.json strukture in pisanje forwarding logike v Koraku 3 — testna infrastruktura zanjo je pripravljena (glej §13 zgoraj).

Ujeta ideja za kasnejšo fazo (NI odločeno, NI del MVP) — avtomatiziran bootstrap

Uporabnik je 2026-09-29 predlagal (izrecno kot razmislek, ne odločitev): namesto ročne kuracije nodes.json, bi ob podpisu "pogodbe o delitvi namestitev" skript samodejno poiskal 3 najbližje node in jih zapisal kot sosede; node-i bi to sprejeli in označili z zastavico "Povezan input"; vsak node bi imel omejeno število vhodnih/izhodnih "rež" (X input, X output), brez podvajanja povezav.

Zakaj to (zaenkrat) ni šlo v spec kot odločitev:

Vredno vrniti se k temu v v0.2, ko/če bo ročno vzdrževanje nodes.json dejansko postalo boleče zaradi realnega števila node-ov. Koncept "X input/X output rež brez podvajanja" je sam po sebi dober vzorec (naravno omeji stopnjo omrežja, ustvari back-pressure) — vredno ga takrat znova pogledati, morda celo neodvisno od vprašanja avtomatizacije odkrivanja.


15. Deset temeljnih načel (nespremenjeno iz v0.1 §26, ostaja veljavno kot preverba pri vsakem naslednjem koraku)

  1. CM ostane source of truth.
  2. AI ne dobi neposrednega dostopa do celotnega Ekosistema — samo do Reception (§2, izostreno).
  3. Vsaka CM instalacija ima lahko svoja vrata.
  4. Reception je skupen protokol, ne nujno centralni strežnik.
  5. Geografija vodi discovery.
  6. Poizvedba se širi po globini, vedno z omejitvami.
  7. Zahtevek nosi svojo zgodovino (request_id, visited, depth).
  8. Podatki ostanejo lokalni — v mrežo gre samo rezultat, ki ga node sme izpostaviti.
  9. Ni vezave na enega AI ponudnika.
  10. Docking Port je razširljiv preko manifesta/protokola, ne novih AI sistemov.

16. Korak 3 — Načrt pred kodo (SKLENJENO — čaka implementacijo, formalno zaprto 2026-09-30)

Po Gregovem A→B→C okviru (Gemini/Grok/GPT potrdili): preden se napiše ena vrstica forwarding kode v reception.php, se uskladimo na treh točkah. To je osnutek za skupni pregled, ne dokončana odločitev.

16.1 Shema nodes.json

Posodobljeno 2026-09-30 — samo-opisna oblika (uporabnikov eksperiment), vsebuje tudi max_fanout (prejšnji osnutek ni povedal, kam se ta shrani):

{
  "protocol": "CM-AI-Docking-Port",
  "version": "0.1",
  "node_id": "CM-AI-DEMO-058",
  "max_fanout": 2,
  "neighbours": [
    {
      "node_id": "CM-AI-STUB-BLED",
      "label": "Bled (stub test node)",
      "location": { "lat": 46.3683, "lon": 14.1146 },
      "reception_url": "[interno testno omrežje — LAN, ni javno dostopno]"
    }
  ]
}

Realna lokacija (po incidentu in popravku 2026-09-30): /home/cmfree/cm-private/ai/nodes.json na instanci 58 — izven /var/www v celoti (ne samo izven /app/public/), lastnik cmfree:www-data, pravice 640 na datoteki in 750 na mapah. reception.php jo bo bral po absolutni poti.

Drugi popravek, isti dan — dovoljenja v verigi map: reception.php teče kot www-data (potrjeno: ps aux, APACHE_RUN_USER=www-data), ki ni član skupine cmfree (id www-data → samo skupina 33(www-data)). /home/cmfree je imel other: ---, kar bi www-data popolnoma blokiralo pri prehodu (traverse) v mapo — reception.php sploh ne bi mogel priti do datoteke, ne glede na njena lastna dovoljenja. To bi na živem Koraku 3.1 tiho spodletelo (is_readable() === false), ne bi pa se pokazalo prej, ker Korak 2 te poti sploh ne uporablja.

Popravek (brez sudo, cmfree je lastnik /home/cmfree): chmod o+x /home/cmfree (samo prehod, brez branja/izpisovanja vsebine za "other") + chgrp www-data na cm-private/ in cm-private/ai/ (cmfree je član skupine www-data, zato lahko to naredi sam). Veriga po popravku:

/home/cmfree          drwxr-x--x cmfree:cmfree   (+x za "other" — samo prehod)
/home/cmfree/cm-private     drwxr-x--- cmfree:www-data
/home/cmfree/cm-private/ai  drwxr-x--- cmfree:www-data
.../nodes.json               -rw-r----- cmfree:www-data

Empirično preverjeno (ne samo izračunano): začasen PHP test skript na živem endpointu (odstranjen takoj po testu) je potrdil is_readable() === true, 129 bajtov, veljaven JSON. Javna izpostavljenost ostaja zaprta (ponovno preverjeno po spremembi dovoljenj).

Incident in popravek (2026-09-30): uporabnik je pri eksperimentiranju ročno ustvaril testno nodes.json na /var/www/html/app/config/ai/nodes.json — znotraj DocumentRoot-a (/var/www/html), zato je bila datoteka javno dostopna na https://.../app/config/ai/nodes.json (HTTP 200), poleg tega je bil na tej poti vklopljen Apache Indexes (javno vidljiv directory listing obeh nadrejenih map). Ujeto s strani zunanjega pregleda (curl), preden je vsebina postala nevarna (neighbours je bil prazen). Ista vrsta napake kot [[connector_modules_data_dir_exposure_2026_08_05]] — pot znotraj DocumentRoot-a, ne prave dostopne pravice. Popravek: datoteka premaknjena izven /var/www v celoti (/home/cmfree/cm-private/ai/, mapa cmfree:cmfree drwxr-x---, torej noben Apache config je nikoli ne more streči, ne glede na Indexes), prazni mapi v DocumentRoot-u odstranjeni. Preverjeno: GET /app/config/ai/nodes.json in /app/config/ zdaj 404, Reception brez regresije (ok:true, L1). Pomembno — namerno ni javno dostopna datoteka. V nasprotju z manifest.json (namerno javen, to je discovery površina), nodes.json je operativna konfiguracija posameznega noda — seznam, komu node zaupa in kam posreduje promet. Ta projekt ima že realno izkušnjo s tem natančnim razredom napake (data/ mape connector modulov so bile javno berljive, popravljeno 2026-08-05, ponovilo se je še dvakrat) — nodes.json gre zato izven javnega docroot-a (ali vsaj eksplicitno blokirana v Apache konfiguraciji), bere jo samo reception.php server-side. To ni teoretična previdnost, je ponovitev znane napake, ki se tej ekipi je že zgodila.

16.2 Pogoji za forwarding

Realna sprememba glede na Korak 2: trenutna živa koda vedno kliče lokalni availability_multi.php, ne glede na target.location. To je bilo ustrezno za Korak 2 (depth 0, brez konteksta cilja), a za Korak 3 ni več smiselno — če nekdo iz Pirana vpraša za Roglo, Piranov lokalni odgovor ni relevanten rezultat, samo šum v results[].

SKLENJENO 2026-09-30 — efektivna razdalja (popravek, lokalni klic in izbira sosedov): target.radius_km (agentova želena relevantnost) in limits.max_distance_km (node-ova/sistemska zgornja meja iz limits_default, če agent ne pove svoje) sta dve različni stvari, ne sopomenki. Prejšnji osnutek je uporabljal samo radius_km — to je bila ista vrsta tihe dvoumnosti kot prej pri request_expiry, zdaj eksplicitno razrešena:

effective_distance_km =
  če sta podana oba:  min(target.radius_km, limits.max_distance_km)
  če je podan samo eden: ta eden
  če ni podan noben: limits_default.max_distance_km (iz manifesta)

Razlog za min(), ne za katero koli polje samo: radius_km je agentova poslovna želja ("rezultati do X km od cilja so zame še uporabni"), max_distance_km je node-ova/sistemska zgornja meja prometa. Agent z ozkim radius_km: 5 ne sme dobiti "lokalnega" odgovora 40 km stran samo zato, ker je node-ov max_distance_km širši — in obratno, node-ova lastna zgornja meja mora vedno obrezati tudi prevelik agentov radius_km, dosledno z že sklenjenim načelom pri max_depth (§14 — server vedno obreže, nikoli ne zaupa golemu agentovemu številu).

effective_distance_km se uporabi na dveh mestih, ne samo enem (dopolnitev, ne v prvotnem predlogu): tako pri odločitvi za lokalni klic KOT pri izbiri sosedov za forwarding — sicer node zavrne lasten odgovor kot "predaleč", nato pa brez težav forwarda k sosedu, ki je še bolj oddaljen, kar je notranje nekonsistentno.

Predlagan sklep (za potrditev): 1. Lokalni klic je pogojen z razdaljo: samo če je haversine(own_location, target.location) <= effective_distance_km, node pokliče svoj lokalni availability_multi.php. Sicer lokalnega klica sploh ne naredi. 2. Forward pogoj: len(visited) < max_depth_limit (kjer je max_depth_limit = min(agentov limits.max_depth, node-ova lastna omejitev iz limits_default) — server vedno obreže, nikoli ne zaupa golemu agentovemu številu, skladno z že sklenjenim §14). 3. Izbira sosedov: iz nodes.json izloči node-e, katerih node_id je že v visited IN katerih razdalja do target.location presega effective_distance_km; preostale sortiraj po Haversine razdalji do target.location; vzemi prvih max_fanout (sklenjeno z Gemini). 4. Pred posredovanjem: dodaj lastni node_id v visited, posreduj isti envelope (z dopolnjenim visited, nespremenjenim request_id/request_expiry) vsakemu izbranemu sosedu, vzporedno (ne zaporedno — da se čas ne seštevaj linearno). 5. Če je len(visited) >= max_depth_limit ali nodes.json po filtriranju (visited + effective_distance_km) prazen, node ne poskuša forwardati — ne napako, samo prazen forward-del odgovora.

SKLENJENO 2026-09-30 (uporabnik): forward je fallback-only, in to ni samo tehnična odločitev za manj prometa — je namerna vrednostna izbira. Node najprej poskusi obdržati gosta pri sebi; šele če lokalni rezultat ni "true" (glej natančen pogoj spodaj), referira naprej v zaupano mrežo. To je digitalni ekvivalent že uveljavljenega vzorca v tem projektu — fizični keycard referral kot prototip CM Community (glej [[guest_trust_and_referral_strategy]]) — prenesen v protokol namesto zgolj v gostov ključ. Mreža je tako po zasnovi overflow/referral mehanizem, ne generično "poišči vse v bližini" iskanje.

Natančen pogoj (za implementacijo): lokalni klic šteje za "true rezultat" in ne forwarda naprej, če data.ok === true IN data.available_count > 0 (obstaja dejansko prosta enota, ki ustreza povpraševanju). Če je lokalni klic spregledan zaradi razdalje (§16.2 točka 1), ali je vrnil available_count: 0, ali je lokalni klic spodletel — node forwarda naprej, znotraj max_depth/max_fanout omejitev.

Pomembna ločnica (uporabnik, 2026-09-30): "lojalnost do lokalne skupnosti" velja na kurirni ravni (kdo sploh pride v nodes.json — operater ročno izbere prave, zaupanja vredne sosede po resničnem odnosu, "po svoji volji in interesu", ne po surovi razdalji; to je vloga bodočega Community zemljevida, §14 jump-over razprava). Ne velja na ravni algoritma med že-izbranimi sosedi — tam ostaja mehanski Haversine-sort + max_fanout, dogovorjen z Gemini (§14). Kurirana izbira je vrednostna, usmerjanje med kuriranimi sosedi je geografsko — ti dve ravni se ne smeta zamešati.

16.3 Agregacija in partial

  1. Vsak klic (lasten lokalni + vsak sosed) ima neodvisen timeout (obstoječ vzorec: CURLOPT_TIMEOUT). Napaka/timeout enega soseda ne prekine drugih — ta sosed se izpusti iz results[], partial postane true.
  2. Odgovor vsakega soseda je že sam po sebi poln Response Envelope (lahko vsebuje več results[] vnosov, če je sosed sam nekaj forwardal naprej). Ta node samo sploshi (flatten) vse prejete results[] v svoj lastni results[] — brez reinterpretacije vsebine (skladno z že sklenjenim pravilom iz §6).
  3. search_meta.nodes_queried/nodes_reached = vsota čez lasten poskus + vse poskuse vseh sosedov (vključno s tem, kar so sosedje sami poročali v svojih search_meta).
  4. search_meta.max_depth_used = največja vrednost, ki jo je poročal kateri koli otrok, +1 za ta hop (ne samo lasten hop).
  5. SKLENJENO 2026-09-30: max_results rez — če je po združitvi results[] daljši od max_results, obdrži najprej najbližje target.location (ne arbitrarno prvih N) — skladno s filozofijo "geografija vodi relevantnost" (§4).

Status §16 (2026-09-30): načrt v celoti SKLENJEN, nič kode. Vseh pet elementov potrjenih: fallback-only forward, effective_distance_km (min/fallback pravilo, velja za lokalni klic IN izbiro sosedov), ločnica kuracija-vs-routing, nodes.json izven docroot, max_results rez po bližini. Community/User Map kot kasnejši, user-controlled način polnjenja nodes.json je potrjen kot skladen s tem načrtom, a ostaja izrecno po Koraku 4/Fazi C, ne pred njim — ne odpira Koraka 3. Implementacija je ločena, zavestna odločitev ("Piši"), ne sledi avtomatsko iz zaprtega načrta.


17. Korak 3.1 — Branje konfiguracije (varen prvi podkorak, SKLENJENO 2026-09-30, čaka "Piši")

Namerno ozek, pred-forwarding podkorak: reception.php zna prebrati nodes.json, a se obnašanje agenta ne spremeni, dokler je neighbours prazen ali se datoteka ne prebere.

  1. reception.php prebere /home/cmfree/cm-private/ai/nodes.json po absolutni poti (dovoljenja preverjena in popravljena, glej §16.1).
  2. Fail-safe, ne 500: če datoteka ne obstaja, JSON ni veljaven, ali node_id v datoteki ne ujema z CM-AI-DEMO-058 — reception.php tiho pade nazaj na obnašanje Koraka 2 (čisto lokalno, ok:true, depth:0), agent nikoli ne vidi napake konfiguracije.
  3. Dodatek (Claude): vsak od treh fail-safe pogojev se zabeleži server-side z error_log() (isti vzorec kot pri Koraku 2 za curl napake) — agent tega ne vidi, operater pa lahko razišče, zakaj forwarding ne deluje, ne da bi moral ugibati.
  4. Brez forwardinga v tem podkoraku — nobenega HTTP/cURL klica proti sosedom, tudi če je neighbours napolnjen s testnimi stubi. Živi endpoint ostane ves čas 100% lokalen in stabilen.

Status: ✅ DEPLOYANO in testirano 2026-09-30. Koda (Grok "Piši", load_nodes_config() + fail-safe + error_log()) je na živo, po istem vzorcu kot Korak 2 (.tmp + php -l + atomski mv). Testi na živem endpointu, vsi po pričakovanjih: 1. Normalen zahtevek → odgovor bit-za-bit enak Koraku 2 (ok:true, depth:0, L1). 2. Odgovor ne vsebuje nobene sledi nodes.json//home/cmfree/max_fanout/neighbours/cm-private. 3. /app/config/ai/nodes.json ostaja 404. 4. Fail-safe, oba scenarija na živem strežniku: preimenovana datoteka → ok:true lokalno + error_log: "nodes.json not readable"; pokvarjen JSON → ok:true lokalno + error_log: "nodes.json invalid JSON". Datoteka obnovljena z originalnimi dovoljenji (cmfree:www-data, 640) po testu. 5. GET→405 in napačen protokol→400 nespremenjena (regresija čista). 6. Brez zaostalih začasnih testnih skriptov v docroot-u.

$nodes_cfg je prebran in ovrednoten, a se za forwarding še ne uporablja — endpoint ostaja 100% lokalen. Naslednje: Korak 3.2 (Haversine, effective_distance_km gating za lokalni klic) — čaka na nov "Piši".


18. Privaten tracker — ni del protokola, samo operativno orodje (2026-09-30)

Namen: videti, ali objava specifikacije dejansko vodi do zanimanja/uporabe (ne samo teoretičen dokaz koncepta). Namerno brez novega javnega endpointa/dashboarda — manj nove površine po incidentu iz §16.1, in ni potreben, ker je edini naslovnik lastnik sam.

Prvi zagon (2026-09-30) je že pokazal pravi signal: obiski spec.md/.well-known z curl/7.88.1 in Python-urllib/3.12 — različni od lastnih orodij v tej seji (curl/8.5.0) — torej najverjetneje avtonomno branje s strani enega od AI sodelavcev ali njihove infrastrukture.


19. Building Team (dodano 2026-10-01)

Namen te sekcije: vsaka sveža seja katerega koli sodelujočega modela (GPT po resetu plana, nova Gemini/Grok seja, ipd.) naj ob branju tega dokumenta takoj dobi pravilen kontekst o vlogah — brez da bi moral uporabnik vsakič znova razlagati, kdo je kdo v projektu.

Viljem

Viljem

Project Lead

Decision-making: sets direction, sets priorities, and confirms consensus. Knows the project's purpose best and is accountable for final decisions.

Claude Code

Claude Code

Lead Developer

Implementation: has direct access to the codebase and executes agreed-upon solutions. Turns consensus into working code.

Grega

Grega

External Tester

Verification: uses the Grok model and his own tools to test the system from the outside world, comparing expected behavior against the real response of the public deployment.

GPT

GPT

Coordinator & Sounding Board

Coordination: clear explanations, a second architectural opinion, edge-case thinking, and continuity — keeping the big picture (and the good mood) from getting lost in technical detail.

Gemini

Gemini

Energetic Challenger

Finding improvements: its impatience is useful when it forces the team to re-check assumptions, surface overlooked options, and think about further improvements.

Kako sodelujemo

  1. Claude predlaga rešitev.
  2. Viljem in ekipa pregledajo predlog — arhitektura, robni primeri, alternative, posledice.
  3. Uskladitev in konsenz.
  4. Claude izvede spremembo.
  5. Grega preveri delovanje; ugotovitve se vrnejo v ekipo.
Eno pravilo, ki ga posebej pazimo: če nimamo dovolj dokazov, da nekaj deluje, tega ne razglasimo za preverjeno. Predlog ni implementacija, implementacija ni test, uspešen test posameznega scenarija pa še ni dokaz, da je celoten sistem brezhiben. Tako ohranimo natančnost pred hitrostjo.

20. Korak 3.2 — Geografski filter pred lokalnim klicem (✅ DEPLOYANO 2026-10-01, Grok "Piši", Viljem potrdil)

Obseg: geografski filter pred lokalnim klicem na availability_multi.php. Brez forwardinga — $nodes_cfg se v tem koraku še vedno ne uporablja za HTTP klice proti sosedom.

Koda (Grok "Piši", po Viljemovi potrditvi dveh robnih primerov): - haversine_km() — standardna formula, asin(min(1.0, sqrt($a))) ščiti pred float napako, ki bi sicer lahko vrnila NaN. - effective_distance_km() — natanko §16.2 SKLENJENO pravilo: min(radius_km, max_distance_km) če sta oba podana, sicer tisti, ki je podan, sicer limits_default.max_distance_km. - target_coords() — varno izvleče lat/lon, vrne null če manjka karkoli. - Vrata so vstavljena za obstoječo validacijo (protocol, request_expiry, query.type, from/to) in pred curl klicem na availability_multi.php — geografija ne sme skriti missing_dates/unsupported_query_type.

Dva robna primera, usklajena med Grokom in Viljemom pred "Piši": 1. Manjkajoč target.location (ali nepopolne koordinate) → brez lokalnega klica, ne pade nazaj na Korak-2 "vedno lokalno". Razlog: slednje bi ustvarilo trivialno obhodnico okoli celotnega geografskega filtra (agent bi preprosto izpustil lokacijo namesto da bi jo nastavil daleč stran). 2. max_depth: 0 ne izklopi geografskega filtra. max_depth nadzoruje omrežno potovanje (še ne obstaja), geografski filter nadzoruje relevantnost tega noda — neodvisni omejitvi, ne smeta se mešati.

Testi na živem endpointu (Grokov seznam, vseh 9 zeleno):

# Scenarij Rezultat
1 Lancovo (46.34, 14.17), radius 30 ok:true, L1, depth 0 ✅
2 Ljubljana (46.05, 14.50), radius 30 ok:true, results: [] ✅
3 Rogla (46.45, 15.33), radius 30 ok:true, results: [] ✅
4 brez target.location ok:true, results: [] ✅
5 samo radius_km, brez lat/lon ok:true, results: [] ✅
6 max_depth:0 + Lancovo še vedno L1 (filter ni izklopljen) ✅
7 GET 405 + envelope (regresija) ✅
8 napačen protocol 400 + envelope (regresija) ✅
9 telo odgovora brez /home/cmfree, nodes.json, haversine, effective_distance ✅

Status: Korak 3.2 zaključen in zunanje preverljiv. Čaka na Gregovo neodvisno zunanjo potrditev (isti 9-točkovni seznam, njegova lastna orodja). Korak 3.3 (fallback forward na sosede) se ne začne samodejno — čaka svoj ločen "Piši".


21. Korak 3.3.0 — LAN smoke test + zaseben nodes.json z B+C (✅ DEPLOYANO 2026-10-01, Grok osnutek, Viljem potrdil)

Obseg po dogovoru: samo infrastruktura, brez spremembe reception.php logike na 058 (ta ostaja natanko Korak 3.2 — forwarding koda še ne obstaja). Stub node-a B (CM-AI-STUB-BLED) in C (CM-AI-STUB-LJ) na 57 sta že obstajala iz §13 — niso novi.

Realna najdba med tem korakom (ni bila v Grokovem osnutku predvidena): 57 ima poleg LAN dostopa še drug, nepovezan javen reverse-proxy domain (apartmamatevz.eu, uporabljen za ločen pro-dev projekt, ProxyPreserveHost On, proxya celoten 57 na splet). To je pomenilo, da sta bila oba stub node-a dejansko dosegljiva iz javnega interneta prek te domene — kar neposredno krši zahtevo iz Grokovega načrta ("stubi niso na javnem netu"). Ni bila napaka tega protokola, je bila posledica tega, da je 57 skupna razvojna naprava z lastnim, nepovezanim javnim vstopom.

Popravek: namesto poseganja v apartmamatevz-eu.conf (produkcijska infrastruktura drugega projekta, izven obsega), dodana ozka app-level varovalka v oba stub datoteki — zavrneta zahtevek (404), razen če $_SERVER['HTTP_HOST'] ustreza natanko [interno LAN, IP namerno ni objavljen] (neposreden LAN dostop). Pri proxyju prek apartmamatevz.eu (ProxyPreserveHost On) Host header ostane apartmamatevz.eu, zato se pravilno zavrne. Preverjeno: LAN dostop še vedno 200, javni proxy dostop zdaj 404 za oba stub-a.

Izvedeni koraki (po Grokovem zaporedju): 1. S 058 (prek LAN) POST na oba stub-a → 200, pravilen envelope, enoti preimenovani v BLED-S1/LJ-S1 (namesto prejšnjih CM-AI-STUB-*-UNIT1, da je provenance v agregatu jasno razločljiva od L1). 2. Potrjeno: stub-a nista dosegljiva z javnega interneta (po popravku zgoraj). 3. Zaseben nodes.json na 058 posodobljen s pravima LAN URL-jema obeh sosedov (cmfree:www-data, 640, nespremenjeno od Koraka 3.1).

Regresija po polnjenju nodes.json: reception.php se obnaša identično Koraku 3.2 — Lancovo še vedno vrne samo lokalni L1, Ljubljana (zunaj dosega) še vedno vrne prazne results z nodes_queried: 0 (brez poskusa forwardinga na LJ stub, čeprav je zdaj v nodes.json) — potrjuje, da $nodes_cfg resnično še ni uporabljen za HTTP klice. Javna pot /app/config/ai/nodes.json ostaja 404.

Status: ✅ 3.3.0 V CELOTI ZAPRT (2026-10-01). Gregova zunanja potrditev: stuba z interneta nista vidna (apartmamatevz.eu/.../reception.php → 404, ni envelope), 058 ostaja čisti 3.2 (Ljubljana/Bled r=3 → prazno, Bled r=30 → samo L1, brez stub-a — lojalnost potrjena od zunaj). Edina točka, ki je Grega po definiciji ne more preveriti od zunaj (da www-data na 058 dejansko dobi 200 z LAN na oba stub-a), je Claude ponovno preveril neposredno: oba stub-a vrneta ok:true s pravilnima enotama (BLED-S1, LJ-S1). Zunanja in notranja preverba se ujemata — 3.3.0 je zaprt.

Čaka ločen "Piši" za 3.3.1 (dejanska forwarding koda). Do takrat 058 ne sme začeti klicati sosedov.


22. Korak 3.3.1 — Fallback-only forward (✅ DEPLOYANO 2026-10-01, Grok "Piši", prvi pravi P2P forward v tem projektu)

To je prvi trenutek, ko 058 dejansko pokliče drug node preko HTTP. Vse od tu naprej je bilo depth-0 samo-odgovor; to spremeni to.

Pogodba

local_true = lokalni klic narejen AND HTTP 200 AND data.ok===true AND data.available_count>0
če local_true     → vrni takoj, ni forward
sicer, če sme_forward → POST na izbrane sosede
sicer             → prazen uspeh (kot 3.2 skip)

sme_forward: $nodes_cfg['ok']===true AND target ima lat/lon AND count(visited) < max_depth_limit, kjer je max_depth_limit = min(agent limits.max_depth, 2) (manjkajoč agentov max_depth → 2).

Dve dejanski napaki iz prejšnjih korakov, odkriti in popravljeni med implementacijo

  1. envelope()-ov max_depth_limit ni bil nikoli dejansko obrezan. Od Koraka 2 naprej je koda samo pretvorila agentov surov limits.max_depth v int, brez min() proti node-ovi lastni meji — čeprav je §14 to že "SKLENJENO" kot princip. Dokler je bilo polje samo informativno (prikazano v search_meta), napaka ni imela posledic. Zdaj, ko max_depth_limit dejansko krmili sme_forward, bi bila to prava varnostna luknja (agent bi lahko poslal poljubno velik max_depth in zaobšel omejitev). Popravljeno: nova funkcija max_depth_limit_for(), uporabljena na obeh mestih.
  2. $visited je bil od Koraka 2 hardcoded na [$NODE_ID], ne glede na to, kaj je agent (ali prejšnji hop) dejansko poslal v request.visited. Test K (spodaj) bi bil nemogoč brez tega popravka. Popravljeno: $visited zdaj združi obstoječi seznam iz zahtevka + lastni ID, brez podvajanj.

Izbira sosedov in klici

select_neighbours(): izloči visited + lastni ID, zahteva veljavna location.lat/lon in reception_url (samo http:///https://, regex preverjeno), filtrira po effective_distance_km do target (ne do sebe), sortira po razdalji, vzame prvih max_fanout (iz nodes_cfg, privzeto 2).

forward_to_neighbours(): curl_multi, vzporedno, 10 s timeout na soseda neodvisno, CURLOPT_FOLLOWLOCATION=false. En neuspešen sosed ne prekine drugih. Agent nikoli ne vidi LAN URL-ja, IP-ja ali curl napake — vse gre v error_log().

Agregacija

Testi — vseh 12 zeleno (Grega A–I javno, Claude J–L interno LAN)

ID Scenarij Rezultat
A Lancovo r=30 samo L1, brez stubov (local_true prestreže) ✅
B Ljubljana r=30 brez L1; CM-AI-STUB-LJ, LJ-S1, depth:1 ✅
C Bled r=3 brez L1; BLED-S1 ✅
D Bled r=30 samo L1, brez BLED-S1 (lojalnost) ✅
E max_depth:0 + LJ r=30 prazno, nodes_queried:0 ✅
F Rogla r=30 prazno (nihče v dosegu) ✅
G brez lat/lon prazno, ni forward ✅
H GET / napačen protocol 405 / 400 (regresija) ✅
I telo odgovora brez LAN IP, nodes.json, /home/cmfree, reception_url ✅
J sosed nedosegljiv (interno, sintetično premaknjen Bled + namerno mrtev URL) partial:true, LJ-S1 ostane, ~90ms (curl_multi res vzporeden) ✅
K visited že vsebuje CM-AI-STUB-LJ tega ne pokliče, prazno ✅
L max_fanout:1, oba v dosegu (interno, sintetično premaknjen Bled) samo bližji (LJ, 0 km) ✅

Opomba o testih J/L: resnična geografija (Lancovo je samo ~5 km od Bleda, ~41 km od Ljubljane) ne omogoča scenarija "Lancovo zunaj dosega, oba stuba znotraj" z resničnimi koordinatami — izračunano in preverjeno pred testom. Zato sta J in L uporabila začasno, nadzorovano spremenjen nodes.json (Bled premaknjen sintetično ~10 km od cilja, obnovljen takoj po testu) — to je bil vedno eksplicitno "Code, LAN" test, ne Gregov javni.

Status: ✅ Korak 3.3.1 V CELOTI ZAPRT (2026-10-01) — dvojno potrjen. Claude interno (vseh 12, A-L) in Grega neodvisno od zunaj (vseh 9 javnih, A-I) se ujemata natanko. Grega je posebej izpostavil E (max_depth:0 → max_depth_limit:0 v odgovoru) kot javen dokaz, da popravek obrezovanja globine dejansko deluje — ne samo na papirju. 058 zdaj dejansko forwarda, preverjeno z dveh neodvisnih strani. Korak 3.4 (globlje verige, stubi ki sami forwardajo, polna zanka) ni del tega dropa in čaka svoj ločen "Piši".


23. Korak 3.4 — OSNUTEK za skupno uskladitev (2026-10-01, Claude, ČAKA potrditev — nič kode)

Obseg po §22: globlje verige (depth 2+), node ki sam forwarda naprej, polna zanka (A→B→A) — izrecno izven 3.3.1.

Strukturna ugotovitev, ki mora biti rešena PRED katerokoli kodo

Med pripravo testnih parametrov sem našel resnično oviro, ne kozmetično: izbor soseda in lokalno obrezovanje na izbranem sosedu uporabljata isto razdaljo.

Ko node A izbere soseda B kot forward-kandidata, je to zato, ker je haversine(B, target) <= effective_distance_km. Ko B nato sam preveri, ali sme odgovoriti lokalno, računa isto haversine(B, target) proti isti effective_distance_km (ista polja envelope-a potujejo nespremenjena). Rezultat: vsak node, ki je bil izbran kot sosed, bo vedno tudi sam ugotovil, da je "v dosegu".

Če ima B (stub) fiksne "vedno na voljo" podatke (kot zdaj — BLED-S1, vedno available_count:1), bo B vedno vrnil local_true=true in se nikoli ne bo premaknil naprej k svojim lastnim sosedom. Globlja veriga (A→B→D) je s trenutnimi stubi strukturno nemogoča, ne glede na izbiro ciljnih koordinat — to ni vprašanje geografije (tisto sem preveril, glej spodaj), ampak vprašanje, kaj stub sploh vrne.

Posledica za obseg 3.4: stubi potrebujejo način, da za določen testni datumski razpon vrnejo available_count:0 (zasedeno), ne vedno "na voljo" — šele takrat ima smisel govoriti o node-u, ki "ne more lokalno, zato forwarda naprej".

Predlagana topologija

Node Lokacija Novo?
A = CM-AI-DEMO-058 Lancovo (46.34, 14.17) obstaja
B = CM-AI-STUB-BLED Bled (46.3683, 14.1146) obstaja, potrebuje nadgradnjo (glej spodaj)
D = nov stub, predlog CM-AI-STUB-KRGORA Kranjska Gora (46.4858, 13.7905) nov, tretji LAN stub na 57
C = CM-AI-STUB-LJ Ljubljana ostane kot je (čist list, za kontrast — ni vsak node treba nadgraditi)

Izračunane razdalje (preverjeno, ne ugibano):

Par km
A–B (Lancovo–Bled) 5.29
A–D (Lancovo–Kranjska Gora) 33.31
B–D (Bled–Kranjska Gora) 28.07

Z radius≈30 okoli Kranjske Gore: A je zunaj dosega (33.31>30, torej A forwarda), B je znotraj dosega iz A-jeve perspektive IN iz lastne (28.07<=30 — natanko zgoraj opisana ugotovitev). Če B za te datume vrne available_count:0, B sam presodi "ni local_true" in poskusi forwardati naprej k D (če je D v B-jevem nodes.json in v dosegu).

Kaj se mora zgoditi, da B sploh lahko forwarda

B (trenutno čist stub, fiksen JSON, brez nodes.json, brez load_nodes_config/select_neighbours/forward_to_neighbours) mora dobiti isto arhitekturo kot A: - lasten NODE_ID/NODE_LAT/NODE_LON - lasten zaseben nodes.json (izven docroot na 57, enaka dovoljenja kot pri A — §16.1/§21 vzorec) - isto reception.php logiko (local_true/sme_forward/select_neighbours/forward_to_neighbours) - Host-header LAN varovalko (že obstaja, glej §21 popravek)

To pomeni: B preneha biti "stub" v prejšnjem pomenu (list, brez logike) in postane drug pravi mini-node — manjša arhitekturna sprememba vloge, vredna eksplicitne potrditve, ne samo tihe implementacije.

Predlagano zaporedje (isti vzorec kot 3.3.0 → 3.3.1)

3.4.0 — infrastruktura, brez spremembe A 1. Nov stub D na 57 (Kranjska Gora), isti vzorec kot B/C (fiksen list, Host-header varovalka). 2. B nadgrajen na pravo Reception logiko (kopija A-jeve kode, lastne konstante, lasten nodes.json z D kot sosedom). 3. B-jeva stub-data logika razširjena: za dogovorjen testni datumski razpon vrne available_count:0 namesto 1 (kontrolirano "zasedeno", da forward sploh pride v poštev). 4. LAN smoke: A→B (obstoječe, nespremenjeno), B→D (novo, ročno s 57). 5. Preverjeno: B in D nedosegljiva z interneta (isti test kot pri §21 incidentu).

3.4.1 — zanka test (A→B→A) B-jev nodes.json vsebuje tudi A (CM-AI-DEMO-058, pravi javen reception_url). Cilj znotraj B-jevega dosega, B-jev lokalni local_true=false (zasedeno), B poskuša forwardati — med kandidati je A, ki pa je že v visited → B ga izloči. Preverja se dejanski HTTP round-trip, ne samo logika v izolaciji (test K je to testiral samo na enem nivoju).

3.4.2 — globlja veriga (A→B→D, depth 2) Isti scenarij kot zgoraj, a B-jev nodes.json vsebuje D namesto/poleg A (D ostane list, glej točko 5 spodaj). Preverja: depth:2 na končnem rezultatu, max_depth_used pravilno odraža dva hopa, D-jev error_log potrdi prejet visited=[CM-AI-DEMO-058, CM-AI-STUB-BLED]. Po popravku vrstnega reda (glej spodaj): max_depth:2 → veriga uspe do D; max_depth:1 → ustavi pri B.

Dopolnitev 2026-10-01 (GPT, tri točke — vse tri realne, ne kozmetične):

  1. visited skozi VEČ hopov, ne samo en — 3.4.1 je testiral samo, da B izloči A iz SVOJE izbire (en nivo). Ne dokazuje, da D dejansko prejme polni visited=[A,B], ne samo [B]. Dopolnitev testa: D-jev lastni nodes.json naj tudi vsebuje A kot soseda. Če D (ko bi sam poskušal forwardati še globlje) pravilno izloči A, to dokazuje, da je prejel polno verigo, ne le zadnjega hopa. Preverljivo interno na 57 (D-jev error_log, isti vzorec kot pri J/L v §22).

  2. Omejevanje globine skozi CELOTNO verigo — realna napaka v prvotnem osnutku, najdena pri tej dopolnitvi. Pri privzetem max_depth_limit=2 bi bil pri B-jevem preverjanju visited že dolg 2 ([A,B]) — 2 < 2 je false, torej B sploh ne bi mogel forwardati naprej k D, ne glede na geografijo ali stub podatke. Zahtevek za 3.4.2 mora izrecno uporabiti limits.max_depth: 3, sicer je test strukturno nemogoč. Razmerje: max_depth_limit dovoljuje max_depth_limit - 1 hopov naprej od vstopne točke (vstopni node sam že "porabi" eno mesto v visited). Dodaten test: isti B→D scenarij z max_depth:2 (namesto 3) mora pravilno ustaviti verigo pri B (B ne forwarda k D) — to je neposreden dokaz, da omejitev deluje skozi celotno verigo, ne samo na prvem hopu (kar je §22 testa E dokazal samo za hop 0→1).

  3. Agregacija ne sme izgledati kot en lokalni vir — arhitekturno že rešeno, a ni bilo nikjer eksplicitno zapisano. Vsak results[] vnos že nosi svoj node_id/depth/source/freshness (§6, §22) — podatki iz različnih node-ov se nikoli ne združijo v en neoznačen blok. Pomembna ločnica, ki jo je treba eksplicitno povedati (dokumentacijsko, ne kodno): search_meta.entry_node je vedno node, ki je neposredno odgovoril agentu na ta konkreten HTTP klic (pri agregiranem odgovoru vedno vstopna točka, npr. A) — to ni isto kot results[].node_id, ki pove resnični izvor posameznega podatka (lahko B, D, karkoli globlje). Agent ne sme teh dveh polj zamenjati. To velja zapisati v index.html/dokumentacijo kot pravilo za agente, ko se 3.4 implementira.

Odprta vprašanja za ekipo (pred katerokoli "Piši")

  1. Soglasje k strukturni ugotovitvi zgoraj — se vsi strinjamo, da je to realna ovira in ne nekaj, kar sem spregledal?
  2. Ime/lokacija za D — Kranjska Gora je predlog (resnična razdalja preverjena), lahko je poljubna druga točka, dokler razmerja delujejo.
  3. Ali B res postane "pravi" mini-node (lastna reception.php kopija), ali raje ostane preprost stub, a z dodano "zasedeno za te datume" logiko brez polne forward sposobnosti — slednje bi 3.4.1/3.4.2 naredilo nemogoča, a je manj invazivna sprememba. Moje priporočilo: B postane pravi mini-node — brez tega polna zanka (izrecno imenovana v §22 kot cilj 3.4) ni testirana, samo simulirana.
  4. Datumski razpon za "zasedeno" — kateri testni from/to naj B trajno vrača kot nedosegljivo, da se ne prekriva s katerim koli obstoječim testom A-L iz §22 (ti morajo ostati zeleni nespremenjeni).
  5. (Grok, poenostavitev točke 5 zgoraj) D NI potreben kot mini-node za dokaz propagacije visited. D sme ostati list — dovolj je, da D zabeleži prejeti visited v error_log (preverljivo interno na 57, isti vzorec kot J/L). Pri A→B→D pričakujemo visited=[CM-AI-DEMO-058, CM-AI-STUB-BLED] ob prihodu na D. Mini-node D + A v njegovem nodes.json (polna rekurzivna izključitev) ostane kasnejši dodatek, ni pogoj za 3.4.0.
  6. (novo, GPT) Eksplicitno zapisati entry_node vs. results[].node_id ločnico v javno dokumentacijo (index.html) kot del 3.4 droppa, ne kot naknadno misel. Tabela za dokumentacijo (Grok):
Polje Pomen
search_meta.entry_node kdo je ta HTTP odgovor dostavil agentu
results[].node_id kdo je izvor tega vnosa
results[].depth koliko hopov je ta vnos prepotoval

Brez nove kode — to je že točno to, kar 3.3.1 vrača (test B: entry_node=058, results[].node_id=CM-AI-STUB-LJ, depth=1).

Popravek vrstnega reda visited/max_depth (Grok, 2026-10-01) — vpliva na že živo kodo na 058

Grok je osporaval mojo interpretacijo "potrebujemo max_depth:3 za 2 hopa" kot artefakt ene implementacije (vrstni red preverb), ne nujno lastnost protokola. Preverjeno z izpeljavo skozi oba vrstna reda — Grok ima prav:

Trenutna živa koda (3.3.1): najprej doda sebe v visited, šele nato preveri count(visited) < max_depth_limit. Posledica: pri max_depth:2 je pri B-jevem preverjanju visited že [A,B] (dolžina 2) → 2<2=false → B ne more naprej, ne glede na karkoli drugega. Za A→B→D je potreben max_depth:3.

Grokov predlog: preveri count(visited_PRIHAJAJOČ) < max_depth_limit pred dodajanjem sebe; sebe doda šele v izhodni envelope, poslan naprej. Posledica: max_depth:2 naravnost pomeni "2 hopa dovoljena" (A→B→D uspe), max_depth:1 pomeni "1 hop" (A→B, B se ustavi). Bistveno čistejša, bolj intuitivna semantika.

Preverjeno (Claude): ta sprememba ne podre nobenega od 12 že zelenih testov iz §22 — pri max_depth:0 oba vrstna reda dasta identičen rezultat (0<0 in 1<0 sta oba false), razlika se pokaže šele na drugem hopu, ki ga noben obstoječi test ne sega. A požima: ta popravek zahteva majhno, a dejansko spremembo že žive kode na 058 (vrstni red dveh vrstic v obstoječem reception.php), ne samo novo kodo za B/D — to mora biti eksplicitno v obsegu 3.4.0, ne tiha stranska sprememba.

Sprejeto: max_depth_limit = natanko toliko hopov, kolikor je dovoljenih (ne -1). Test 3.4.2 postane: - max_depth:2 → A→B→D uspe - max_depth:1 → ustavi pri B (B ne pokliče D)

Nova točka 7 (Viljem, 2026-10-01, Grok je še ni videl) — lokalni "ni na voljo" rezultat se trenutno popolnoma zavrže

Opažanje: trenutna (3.3.1) koda, ko je local_ok=true (v dosegu) a local_true=false (preverjeno, a available_count:0), ta rezultat v celoti zavrže — gre naravnost na forward-ali-prazno, brez sledi, da je node sploh odgovoril. Viljemova točka: node, ki je preveril in nima prostora, naj to pove (izpade iz "local_true" kratke poti, ne pa iz results[] v celoti) — sicer node "izpade iz iskanja", kot da ga ni bilo, čeprav je dejansko odgovoril. Node ne sme biti samo relay — mora vrniti svoj rezultat, če ga ima; če ga nima (klic spodletel/izven dosega), šele takrat je čist relay.

Claude preverjeno: noben od 12 obstoječih testov tega ne pokriva (L1 je bil v vseh "v dosegu" testih na voljo) — torej to ne podre ničesar že preverjenega, je pa resnična, nova sprememba obnašanja (lokalni ne-true rezultat bi se zdaj pojavil poleg forwardanih rezultatov, ne namesto njih). Predlog izvedbe: kadar je local_reached=true (podatek dejansko prejet, ne glede na available_count), se doda v results[] kot običajen vnos (depth:0, lasten node_id); samo kadar je local_reached=false (klic spodletel ali sploh ni bil poskušan) se v celoti izpusti. To vpliva tudi na agregacijo v B-ju kot mini-nodu — B-jev "zasedeno" December odgovor bi se potemtakem pojavil v agregatu A-ja, poleg D-jevega rezultata, ne namesto njega.

Status: Grok tega še ni videl (sporočilo mu ni prišlo skozi). Potrebna njegova in GPT-jeva potrditev, preden gre v obseg 3.4.0 — to je sprememba obnašanja, ne samo infrastrukture.

GPT-jev PM pregled celotnega §23 (2026-10-01)

Potrdil točke 1–6 (Grokov max_depth popravek — izrecno kot protokolna sprememba semantike, ne "premik dveh vrstic", z zahtevo po ponovnem zagonu vseh 12 regresijskih testov; polni visited test do D; entry_node/node_id dokumentacija; B kot pravi mini-node; D kot list, ki samo beleži; B-jev kontroliran "zasedeno" interval).

Pri točki 7 je pritisnil na zavoro — ne zavrača načela (node, ki je preveril in nima prostora, se razlikuje od node-a, ki ni bil dosegljiv — to velja), opozori pa na resen stranski učinek: če available_count:0 postane običajen results[] vnos, vpliva na max_results rez. Njegov primer: A→B(available_count:0)→D(available_count:1), max_results:1 — če rez obdrži B-jev negativen rezultat namesto D-jevega pravega, je to slabše od sedanjega stanja (kjer B enostavno izgine, D pa ostane).

Novo vprašanje 8 (GPT): šteje max_results vse results[] vnose (tudi negativne lokalne odgovore) ali samo dejansko razpoložljive? To ni kozmetika — določa pomen celotnega results[] polja za agenta. Alternativa, ki jo GPT omeni (ne predlaga odločitve): ločiti results[] (uporabni rezultati) od ločenega "dokazila o preverjenih node-ih" v metapodatkih — a eksplicitno noče odločati namesto ekipe.

Claude preveril GPT-jev scenarij proti dejanski kodi — ni hipotetičen, je realen hrošč v sort_by_distance_to_target(). Funkcija pozna lokacije samo neposrednih sosedov iz lastnega nodes_cfg (pri A-ju: B in C). D-jeva lokacija ni v A-jevem nodes.json (D je B-jev sosed, ne A-jev) — torej bi D pri sortiranju dobil razdaljo INF (neznano), B pa pravo, končno razdaljo. Posledica: B-jev negativen rezultat bi se pri max_results rezu uvrstil PRED D-jev pravi rezultat, natanko scenarij, ki ga je GPT opisal kot tveganje. Zanimiva asimetrija, odkrita pri preverjanju: stub node-i (B, C) že vračajo node_location v svojem odgovoru, 058-ova lastna koda za lokalni rezultat pa tega polja nima — to je verjetna smer popravka (uporabiti lokacijo, ki potuje z vsakim rezultatom, namesto iskanja v lastnem nodes.json, kar ne pozna globljih hopov). To ni odločitev, samo ugotovitev za razpravo o vprašanju 8.

GPT-jev sklep, upoštevan dobesedno: dodati samo vprašanje 8, nič implementirati, počakati na Grokovo mnenje o 7/8.

GPT-jeva druga runda (2026-10-01) — 7 in 8 sta ena arhitekturna tema, ne dve

GPT je strnil točki 7 in 8 v eno samo pravo vprašanje: kaj je sploh semantična enota results[]? Dve čisti interpretaciji, namerno brez odločitve (njegove besede: "to je protokolarna semantika, ki jo mora potrditi ekipa, ne PM"):

Globlja ugotovitev pod tem (GPT): A kot agregator ne more biti avtoritativen glede celotnega results[] prostora, ker pozna samo svoje neposredne sosede (A→B, A→C), ne pa posredno doseženih node-ov (A→B→D). Zato mora biti node_location del vsakega rezultata — provenance mora potovati z rezultatom, ne se iskati v lokalnem nodes.json klicatelja. To se sklada z načelom, ki je bilo že implicitno v §6 (vsak vnos nosi svoj node_id/depth/source) — zdaj razširjeno na lokacijo.

Konceptualna smer za bodoči sorter (GPT, izrecno "ugotovitev, ne še implementacijska odločitev"):

pravilno:    result.location → distance(result.location, target) → sort
napačno:     result.node_id → poišči v mojem nodes.json → če ni, INF

Drugo (trenutno stanje kode) deluje za depth 1 in odpove natanko takrat, ko sistem postane zares distribuiran — kar se ujema s Claudovo potrditvijo hrošča zgoraj.

GPT-jev status za 3.4 (dobeseden povzetek):

🟢 Protokolna smer jasna
🟢 1–6 usklajeno
🟡 7 načeloma pravilno, a zahteva semantiko results[]
🟡 8 odprto, povezano s sortiranjem
🔴 Koda še nič

GPT-jevo lastno vodilo za ta korak, vredno zapisati: ne zamenjati "deluje v testu" z "protokol je že pravilen" — 3.4 je prvi korak, kjer več neodvisnih odločitev (depth, visited, geografija, agregacija) deluje skupaj; zeleni testi dokazujejo ta specifičen scenarij, ne celotnega prostora možnih vhodov. Njegov zaključek za ta krog: tokrat nas ni ustavil sintaktični bug, ustavilo nas je vprašanje "kaj naj podatek sploh pomeni" — pomembnejše vprašanje za rešiti pred kodo kot običajno. Sorterja se namerno ne popravlja zdaj, čeprav je verjetna smer že vidna — čaka na dogovor o pomenu results[].

Nič od tega ni implementirano. Čaka skupno uskladitev (Grokov popravek vrstnega reda, Viljemova točka 7, GPT-jevo vprašanje 8/semantika results[], in prejšnjih 6 vprašanj), nato ločen "Piši" za 3.4.0.

Grokovo mnenje o 7/8 (2026-10-01, nova seja — uspešno se je ujela s preverjanjem živega spec.md, dokaz, da objavljanje spec dokumenta deluje natanko tako, kot je bilo namenjeno)

Grok je neodvisno ponovno preveril 3.3.1 v živo (Lancovo→L1, Ljubljana→CM-AI-STUB-LJ/LJ-S1 depth:1, GET→405) — ujema se.

Predlagana rešitev za 7/8: results[] ostane GPT-jeva interpretacija A (samo uporabne ponudbe) — "agent sprašuje 'kje lahko spim?', ne 'koga si vprašal?'". Viljemova točka 7 ostaja pravilna, a ne sodi v isti seznam — preverjen-a-prazen node gre v ločeno provenance strukturo: nov checked[] (polje: node_id, location, available_count:0, depth), ki nikoli ne tekmuje za max_results rez.

Claude-ova dopolnitev: checked[] naj bo ločeno polje na vrhu odgovora (sorodnik results[]), ne znotraj search_meta — slednji je doslej vedno nosil samo skalarne števce (nodes_queried, nodes_reached, max_depth_used), ne sezname strukturiranih vnosov; mešanje bi zabrisalo že uveljavljen vzorec.

Tretja neodvisna potrditev istega hrošča (Claude, GPT, zdaj Grok, vsi neodvisno): node_location mora potovati z vsakim results[] vnosom, ne iskati se v klicateljevem lastnem nodes.json (ta pozna samo neposredne sosede, ne globljih hopov).

Grokov predlog delitve obsega: 3.4.0 (infrastruktura — D stub, B kot mini-node, zaseden interval, LAN smoke) se lahko izvede ločeno od 7/8, ker ni odvisna od semantike results[]. Agregacijska sprememba na 058 (ki 7/8 dejansko potrebuje) čaka svoj ločen "Piši", po tem, ko je semantika dogovorjena. Sorterja še vedno ne popravlja. Eksplicitno: brez Viljemovega "Piši" ne gre na živo kodo, niti za 3.4.0.

Status: konkreten, dobro oblikovan predlog za 7/8 (interpretacija A + ločen checked[]), čaka končno potrditev GPT-ja/Gemini-ja/Viljema, preden postane SKLENJENO.

✅ 7/8 SKLENJENO (2026-10-01) — polno 4-stransko soglasje (Viljem, GPT, Grok, Gemini)

Gemini je potrdil celoten predlog ("čista, elegantna, dolgoročno vzdržna rešitev") in dodal tri dopolnitve za robne primere. Viljem je glasoval DA. Končna shema:

Trije ločeni deli odgovora, vsak s svojo vlogo:

Del Vsebina Tekmuje za max_results?
results[] izključno dejanski rezultati z available_count > 0 DA, sortirano po razdalji do target
checked[] node-i, ki so bili uspešno kontaktirani in potrdili 0 enot — dokaz, da niso bili tiho izpuščeni NE
search_meta strogo skalarno (števci, globine, vstopna točka) — brez seznamov/kompleksnih struktur n/a

checked[] — minimalna shema (Gemini):

{ "node_id": "CM-AI-STUB-LJ", "depth": 1, "location": { "lat": 46.0569, "lon": 14.5058 } }

Namerno brez source/freshness/data — to je revizijska sled, ne uporaben podatek, zato ostane lahka.

Tri dopolnitve za robne primere (Gemini, sprejeto): 1. Deduplikacija: pri agregaciji iz več vej se checked[] deduplicira po node_id, obdrži se vnos z manjšo globino. (Trenutna linearna veriga A→B→D tega še ne sproži — priprava za gostejše topologije kasneje.) 2. Minimalna shema (glej zgoraj) — brez balasta. 3. Stroga ločitev nedosegljivosti od potrjene ničle: node, ki vrne napako (500, neveljaven JSON) ali doseže timeout, nikoli ne pristane v checked[] — to polje je rezervirano izključno za uspešno potrjeno ničelno razpoložljivost. Izpad noda se odraža samo v partial:true, razliki nodes_queried vs. nodes_reached, in errors[] — dosledno z že sklenjenim §16.3 pravilom, brez novega mehanizma.

node_location popravek — potrjen četrtič neodvisno (Claude, GPT, Grok, zdaj Gemini): vsak vnos v results[] IN checked[] nosi svoj node_location (lat/lon). Sortiranje po razdalji do target se izvaja na podlagi te lokacije, ki potuje z vsakim vnosom — nikoli z iskanjem v klicateljevem lastnem nodes.json (ta pozna samo neposredne sosede, ne globljih hopov).

Status: §23 (7/8) v celoti SKLENJEN. Edino, kar še manjka pred kodo: Viljemov izrecen "Piši" za 3.4.0.


24. Ujeta ideja za kasnejšo fazo (NI odločeno, NI del MVP, NI del 3.4) — rezervacija po "true" poizvedbi

Vprašanje uporabnika, 2026-10-01, sproženo ob branju reception.php kode in hopov/odgovorov iz availability_multi.php — namerno ne kot zdajšnja vrzel, ampak kot razmislek o nadaljnji uporabni arhitekturi, po njegovih lastnih besedah.

Vprašanje: ko poizvedba vrne "true" (prosta enota najdena, morda več hopov stran), kako sistem preide na dejansko AI-posredovano rezervacijo? Ali Reception posreduje podatke o lokaciji/node-u, ali je za rezervacijo potrebna neposredna pot na nova "vrata" s pridobljenimi podatki? In ključno: ali morata poizvedba in rezervacija deliti nek skupen zadrževalni mehanizem — sicer lahko informacija med AI poizvedbo in človekovo potrditvijo zastara (klasičen TOCTOU problem v porazdeljenih rezervacijskih sistemih)?

Smer odgovora (ujeto za kasnejšo fazo, ne odločitev):

  1. Reception ne sme postati pot do rezervacije. Ostane tanek, večhopni discovery sloj (§1, §9). Ko agent dobi "true" rezultat od konkretnega node-a (morda več hopov stran preko forward verige), rezervacija ne sme iti nazaj skozi isto verigo posredovanj — mora iti neposredno, en hop, na tisti konkreten node, katerega inventar se dejansko spreminja.
  2. Nova, ločena zmogljivost, ne razširitev Reception. Prostor za to že obstaja v manifestu (capabilities.local, poleg availability) — side_effect:true, requires_consent:true, oba flaga že definirana v shemi (§7), samo še neuporabljena. results[] vnos bi moral nositi dovolj podatkov (npr. neposreden URL tistega node-a), da lahko agent nadaljuje direktno, mimo Reception verige.
  3. Zadrževalni mehanizem ne potrebuje izuma na novo. Realni CM sistem že pozna koncept "soft hold" (availability_multi.php-jev include_soft parameter — notranje začasno blokiranje enote med rezervacijskim procesom). Bodoča "naredi rezervacijo" zmogljivost naj uporabi ta obstoječi mehanizem, ne novega: agent (ali človek za njim) ob "true" rezultatu zaprosi za kratkotrajen soft hold na konkretni enoti, šele nato gre v potrditev. Če med tem kdo drug zasede enoto, rezervacijski poskus preprosto spodleti — bolj pošteno kot se pretvarjati, da je "true" iz poizvedbe večna resnica.

Status: zunaj obsega 3.4 in vsega trenutno dogovorjenega. Ne blokira ničesar. Zapisano, da se ne izgubi v naglici prihodnjih korakov.


25. Korak 3.4.0 — ✅ DEPLOYANO 2026-10-02 (Viljem "Piši 3.4.0")

Natančno po dogovorjenem obsegu: popravek vrstnega reda na 058, nov list D, nadgradnja B v mini-node, kontroliran "zasedeno" interval. Brez 7/8 (checked[]/results[] ostaja ločen "Piši").

1. Popravek vrstnega reda visited/max_depth na živi kodi 058

$visited razdeljen na $visited_incoming (iz zahtevka, brez sebe — uporabljen za gate preverbo count(...) < max_depth_limit) in $visited_outgoing ($visited_incoming + lastni ID — uporabljen za select_neighbours() izključitev in izhodni envelope). max_depth_limit zdaj pomeni natanko toliko hopov, kolikor je dovoljenih, kot je bilo sklenjeno z Grokom.

Polna regresija vseh 12 testov §22, po popravku, zeleno: A (samo L1), B (CM-AI-STUB-LJ/LJ-S1 depth:1), C (BLED-S1), D (lojalnost, samo L1), E (max_depth:0 → prazno, max_depth_limit:0 v odgovoru), F (prazno), G (brez lokacije → prazno), H (405/400), I (brez uhajanja), J (timeout → partial:true, drugi rezultat ostane), K (visited izključitev), L (max_fanout:1 → bližji zmaga). J in L ponovno z začasno, nadzorovano spremenjeno nodes.json (isti vzorec kot §22), obnovljeno takoj po testu.

2. Nov list-stub D (CM-AI-STUB-KRGORA, Kranjska Gora 46.4858/13.7905)

Isti vzorec kot B/C (fiksen JSON, Host-header LAN varovalka), plus error_log beleženje prejetega visited. Preverjeno v živo: visited=[CM-AI-DEMO-058,CM-AI-STUB-BLED] pravilno zabeležen pri direktnem testnem klicu. LAN dostop 200, javni proxy (apartmamatevz.eu) 404.

3. B nadgrajen v pravi mini-node (CM-AI-STUB-BLED)

Polna kopija 058-ove (popravljene) local_true/sme_forward/select_neighbours/forward_to_neighbours arhitekture. Namesto prave availability_multi.php ima nadzorovan generator (fake_local_availability()): za 2026-12-01–2026-12-07 vrne available_count:0 ("zasedeno"), sicer available_count:1 (BLED-S1, nespremenjeno od prej — oktobrski testi A-L ostanejo zeleni). Lasten zaseben nodes.json na /home/apartma/cm-private/ai/ (enak varnostni vzorec kot 058: chmod o+x na /home/apartma samo za prehod, chgrp www-data na gnezdenih mapah, 640/750) — vsebuje D kot edinega soseda (A pride šele v 3.4.1). Empirično preverjeno, da www-data dejansko lahko bere datoteko (isti test kot pri 058).

Testi, vsi zeleno: - B samostojno, oktobrski datumi → ok:true, BLED-S1, brez forwarda. - B samostojno, decembrski datumi → zasedeno lokalno, pravilno forwarda k D → KRGORA-S1, depth:1. - C (Bled r=3) prek javnega 058 endpointa, z nadgrajenim B → nespremenjen rezultat (BLED-S1, depth:1). - D (Bled r=30, lojalnost) prek javnega 058 endpointa, z nadgrajenim B → nespremenjen rezultat (samo L1, B sploh ni poklican). - B in D nedosegljiva prek apartmamatevz.eu (404) — tudi B-jeva nodes.json pot (ni pod docroot, 404).

Namerno izven obsega tega droppa

Polna veriga A→B→D prek javnega 058 endpointa (to je formalno 3.4.2, čaka svoj ločen "Piši" — čeprav je B-jev samostojen test že dokazal, da osnovna mehanika deluje). Zanka A→B→A (3.4.1, prav tako svoj "Piši"). 7/8 (checked[]/results[], sorter popravek) — čaka svoj ločen "Piši".

Status: 3.4.0 v celoti zaključen in preverjen. Čaka naslednji "Piši" (3.4.1 zanka, 3.4.2 veriga, ali 7/8 agregacija — poljuben vrstni red, Viljemova odločitev).

Sprejeto zaporedje za nadaljevanje (Gemini, 2026-10-02)

3.4.1 (zanka) → 7/8 (agregacija) → 3.4.2 (polna veriga). Razlog: če se 7/8 izvede pred 3.4.2, prva verifikacija celotne verige A→B→D takoj preveri končno, produkcijsko obliko odgovora (ločen checked[], prenos node_location) — namesto da bi verigo testirali dvakrat (enkrat s staro obliko, enkrat po 7/8).

Dopolnitev (Claude): ko pride "Piši" za 7/8, mora popravek agregacijske logike (ločitev results[]/checked[], node_location na vsakem vnosu, dedup po manjši globini) iti na obe mesti — 058 IN B — ker je B zdaj tudi pravi agregator (ne samo 058), zgrajen kot kopija iste arhitekture v 3.4.0.

Vsak od treh korakov ostane ločen "Piši", kot doslej. Nič ni implementirano, dokler Viljem ne reče.


26. Korak 3.4.1 — ✅ DEPLOYANO 2026-10-02 (Viljem "Piši 3.4.1") — zanka A→B→A

Strukturna ugotovitev pred testom (isti razred kot že najdena v §23)

Prvotno specificiran test ("cilj znotraj B-jevega dosega, A med kandidati, izločen prek visited") je strukturno neizvedljiv z resnično geografijo. A-jevo lastno preverjanje "sem v dosegu" in B-jevo preverjanje "je A v dosegu" za isti cilj računata popolnoma isto razdaljo (Lancovo→cilj) proti isti effective_distance_km (nespremenjena skozi verigo). Če je A dovolj daleč, da sam preskoči lokalni odgovor (pogoj, da sploh forwarda k B), je po definiciji enako daleč tudi, ko B preverja A kot kandidata — A bi bil vedno izločen zaradi razdalje, test ne bi dokazal ničesar o visited specifično.

Popravljen test: klic neposredno na B (isti vzorec kot test K, a na B namesto na A), s ciljem = Bled sama lokacija, radij 30 km. Pri tem cilju sta oba, A (5.3 km) in D (28.1 km), geografsko veljavna kandidata — A se izloči izključno zaradi visited, kar dokaže mehanizem brez zmede z geografijo.

Izvedba

B-jev nodes.json dopolnjen z A (CM-AI-DEMO-058, pravi javen reception_url), poleg obstoječega D.

Grokova opomba po prvem dropu — en test ni dovolj

Prvi test (samo K2 spodaj) je dokazal, da A manjka iz rezultata — ni pa dokazal zakaj. Enak izid bi dobili, če bi bil A narobe vpisan v nodes.json, URL neveljaven, klic do njega spodletel, ali max_fanout prenizek — vse to bi "A manjka" pokazalo enako kot pravilno delujoča visited izključitev. Brez kontrolnega testa (A dejansko DOSEŽEN, ko ni v visited) K2 sam po sebi ni dokaz. Grok je zahteval štiri ovojnice (K0–K3) in preverbo na 058-ovi strani (ne samo B-jevega odgovora) — enak request_id se mora na 058 pojaviti pri K1, ne sme pri K2.

Štirje testi, vsi izvedeni, vsi zeleno

Pred testi preverjena realna razpoložljivost: L1 je dejansko prosta 3.–5. december na pravem sistemu (nujen pogoj, da je K1 pričakovanje sploh pravilno).

Izhodišče: 058-ov privaten dnevnik (visits.jsonl) pred testi: 100 vrstic.

Test Klic na B (LAN) visited Rezultat 058-ov log
K0 regresija Bled r=30, oktober [A] samo BLED-S1, depth:0, nodes_queried:1 — local_true prestreže, brez forwarda n/a (ni poskusa)
K1 kontrola Bled r=30, december [] (prazen) L1 in KRGORA-S1, oba depth:1; nodes_queried:3, nodes_reached:3, partial:false 101 vrstic (+1, user_agent:"" — avtomatski curl_multi klic iz B-ja, ne ročni test)
K2 glavni Bled r=30, december [A] samo KRGORA-S1; brez L1; nodes_queried:2; partial:false, errors:[] 101 vrstic (nespremenjeno — A ni bil niti poskušan)
K3 Bled r=10, december [A] prazno; nodes_queried:1 (A edini geografski kandidat, a izločen; D na 28 km je zunaj 10 km dosega) 101 vrstic (nespremenjeno)

K1 je ključen dokaz: z visited:[] B dejansko pokliče A (pravi javen endpoint), A pravilno odgovori z L1, in 058-ov lastni dnevnik to neodvisno potrdi (nova vrstica, ločljiva po praznem user_agent od mojih ročnih testnih klicev, ki vedno nosijo curl/8.5.0). K2 proti K1 dokaže, da izostanek A ni posledica napačne konfiguracije, neveljavnega URL-ja ali spodletelega klica — A je bil dosegljiv in deloval (K1), a namenoma preskočen (K2), kar potrjuje tudi 058-ov log, ki se med K2 ne poveča.

Pomembna ugotovitev za spec (Grok)

Prvotni javni scenarij "A→B→A" (agent vpraša 058 javno, 058 forwarda k B, B poskuša nazaj k A) je strukturno neizvedljiv z resnično geografijo (glej zgoraj) — A, ki je dovolj daleč, da sploh forwarda, nikoli ni geografsko veljaven kandidat za B. Dejanska pogodba Koraka 3.4.1 je LAN-K0–K3, ne javen test. Z javnega 058 ni mogoče tega ponoviti (L1 je prost tudi decembra, zato A v dosegu cilja nikoli ne forwarda) — to je bil od začetka Code/LAN test, kot J/K/L v §22, ne Gregov javni test. Grega lahko od zunaj potrdi samo: regresija A–I (§22) ostane zelena, in da se 058 ni začel sam od sebe klicati (preverljivo posredno prek njegovega lastnega dnevnika dostopa, če bi imel do njega dostop — sicer prek odsotnosti kakršnekoli spremembe v javnem obnašanju 058).

Regresija, zeleno: - Polna veriga A→B→D prek javnega 058 (cilj Kranjska Gora, december, max_depth:2) → nespremenjeno, KRGORA-S1 depth:2 (tu je A izločen po razdalji, drugačen mehanizem od K2, oba pravilna za svoj scenarij). - Test C (Bled r=3, oktober) prek 058 → nespremenjeno, BLED-S1. - B nedosegljiv prek apartmamatevz.eu → 404, nespremenjeno po dodatku A v nodes.json.

Status: 3.4.1 v celoti zaključen, z nadzorovanim dokazom (ne samo opazovanim rezultatom). Naslednje po sprejetem zaporedju: 7/8 (agregacija), nato 3.4.2 (formalno zaprtje že delujoče verige + popravek index.html).

GPT-jeva potrditev pred "Piši 7/8" (2026-10-02): sprejel 3.4.1 kot zaključen na podlagi K0–K3 dokaza. Ponovno potrdil že SKLENJENO obliko 7/8 (§23) kot "pogodbene detajle, ne implementacijske malenkosti" — max_results sme omejevati samo results[], nikoli ga checked[] ne sme izriniti; dedup in obravnava delnih rezultatov (partial) morata ostati natančna, ne približna. Popravek mora iti na 058 IN B (že zapisano). Opomnil tudi, da §24 (rezervacija po "true") ostaja izrecno izven obsega 3.4 — se ne sme po nesreči razširiti vanj. Nič novega za odločiti; čaka ločen "Piši 7/8".

Druga neodvisna Grok potrditev (2026-10-02, nov obvod): sprva prebral starejši posnetek (10:42 namesto 10:53:32Z), ni videl K0–K3. Po opozorilu preveril sveže, potrdil vse štiri vrstice + 058-ov dnevnik kot pravilen dokaz, in izrecno zaprl 3.4.1 ("K1 proti K2 plus 058-ov dnevnik je tisti dokaz, ki ga je K2 sam nujno potreboval"). 3.4.1 je s tem trikrat neodvisno potrjen (Claude, prvi Grok obvod, drugi Grok obvod). Ponovno zapisal isto pogodbo za 7/8 (ujema se z GPT-jevo zgoraj, brez razhajanj). Čaka "Piši 7/8".


27. Korak 7/8 — ✅ DEPLOYANO 2026-10-02 (Viljem "Piši 7/8") — checked[]/results[] ločitev, node_location popravek

Natančno po §23 SKLENJENO pogodbi (polno 4-stransko soglasje Viljem/GPT/Grok/Gemini, 2026-10-01). Popravek šel na obe mesti — 058 IN B — ker je B zdaj pravi agregator (zgrajen kot kopija 058-ove arhitekture v 3.4.0), ne samo list.

Sprememba kode (identična struktura na obeh node-ih)

  1. envelope() — nov privzet top-level ključ 'checked' => [], sorodnik results[]. search_meta nespremenjen (ostaja strogo skalaren, brez seznamov — dosledno s §23).
  2. Lokalni klic, razcep na tri izide (prej samo dva: local_true ali tiho naprej): - ok===true && available_count>0 → results[] vnos (nespremenjeno, plus nov node_location). - ok===true && available_count===0 → nov checked[] vnos (minimalna shema spodaj). - ok===false (dejanska notranja napaka API-ja) → ostane samo error_log(), ne pristane v checked[] (stroga ločitev nedosegljivosti od potrjene ničle, Gemini-jeva 3. dopolnitev).
  3. checked[] minimalna shema (natanko po Gemini-jevem primeru): {"node_id": "...", "depth": N, "location": {"lat": ..., "lon": ...}} — namerno brez source/freshness/data.
  4. Agregacija: otrokov checked (če obstaja — manjkajoč ključ se obravnava kot prazna lista, ker D kot čist list polja ne vrača) se prišteje z depth+1, enako kot results[]. Node, ki je spodletel/timeout, ostane izključno v partial/error_log() poti — nikoli ne doseže checked[] veje (ta koda ni bila spremenjena).
  5. Dedup: nova dedup_checked_by_min_depth() — po agregaciji, po node_id, obdrži vnos z manjšo globino.
  6. sort_by_distance_to_target() popravljen — prej iskal lokacijo prek klicateljevega lastnega nodes_cfg['neighbours'] (pozna samo neposredne sosede — za globlje hope je to vedno vrnilo INF, tiho napačno sortiranje, ki se v obstoječih enohop testih ni opazilo). Zdaj bere node_location neposredno iz vsakega vnosa — pravilno na poljubni globini. Funkcija ne potrebuje več $nodes_cfg parametra.
  7. max_results rez velja samo za results[] (nespremenjeno; checked[] se nikoli ne reže — strukturno zagotovljeno, ker je to popolnoma ločen array, ne deli iste array_slice() klice).

Ena namerna razlaga/razrešitev dvoumnosti v §23 besedilu

§23 na enem mestu generično govori o "node_location... na vsakem vnosu v results[] IN checked[]", a Gemini-jev konkreten JSON primer za checked[] uporablja ključ location, ne node_location. Implementacija sledi konkretnemu JSON primeru kot avtoritativnemu (dosleden vzorec tega projekta — dobesedni SKLENJENO blok je merodajen, ne okoliška proza): results[] → node_location (že uveljavljeno ime, uporabljeno v D/B kodi od 3.4.0 naprej), checked[] → location (natanko Gemini-jev minimalni primer). Zapisano tu izrecno, da lahko ekipa ugovarja, če je bila mišljena dobesedna istoimenskost.

GPT-jeva PM-opomba (2026-10-02, po zaprtju 3.4.2): ta razlika v imenu (node_location vs. location) ni napaka in ni razlog za ponovno odpiranje 3.4.2/7-8 — oba koraka ostajata zaprta. Vredno pa jo je ponovno pretehtati, ko/če pride na vrsto priprava strojno berljivega protokolarnega dokumenta za zunanje agente (morda enotno ime po vseh poljih). Ujeto tu kot opomnik za tisti trenutek, ne kot odprto delo zdaj.

Deploy

Varen vzorec (md5sum pred urejanjem → php -l lokalno → upload kot .tmp → php -l oddaljeno → atomski mv), identično na obeh: - 058: /var/www/html/app/public/ai/reception.php - B: /var/www/html/ai-test-nodes/node-b/reception.php (57)

Regresija, vse zeleno

Javni testi §22 prek 058 (A, B, C, D, E, F, G, H, I, K) — ponovno pognani, nespremenjeni izidi, plus zdaj z node_location na vsakem results[] vnosu in checked: [] eksplicitno prisoten (prej odsoten ključ). Test I (brez LAN uhajanja) ponovno preverjen na dejanskem odgovornem telesu — čist.

J in L (interni, sintetično premaknjen Bled na 058) namerno NISO ponovno pognana — oba testirata partial-obravnavo/max_fanout-rez, kodo, ki je ta korak ni spremenil; edina dejansko spremenjena pot (sort_by_distance_to_target) se v L nikoli doseže (fanout obreže na 1 sosed PRED klicem, ne po), in J-jev edini preživeli rezultat gre skozi sort z enim samim vnosom (trivialno). Namesto nepotrebnega začasnega poseganja v živo nodes.json na produkciji je spremenjena sortirna funkcija namesto tega enotsko preverjena izolirano (glej spodaj) — bolj neposreden dokaz za točno to kodo kot posredno prek nepovezanega testa.

§26 K0–K3 (direktno na B, LAN) — ponovno pognani, identični izidi kot pred popravkom, plus nov checked[] dokaz:

Test visited results[] checked[] 058-ov dnevnik
K0 (oktober) [A] BLED-S1 depth:0 [] n/a (brez poskusa, nespremenjeno)
K1 (december, kontrola) [] L1 + KRGORA-S1, oba depth:1 [{CM-AI-STUB-BLED, depth:0, location:Bled}] 124→125 (+1, nespremenjeno od prej)
K2 (december, glavni) [A] samo KRGORA-S1 [{CM-AI-STUB-BLED, depth:0, location:Bled}] 125 (nespremenjeno — A ni poskušan)
K3 (r=10, december) [A] [] [{CM-AI-STUB-BLED, depth:0, location:Bled}] 125 (nespremenjeno)

Zanka-izključitev (visited) mehanika ostaja popolnoma nedotaknjena — isti dokaz (058-ov privaten dnevnik se NE poveča pri K2/K3) velja še naprej po refaktorju.

Nov test: polna A→B→D veriga prek javnega 058, s checked[] skozi dva hopa (cilj Kranjska Gora, r=30, december, max_depth:2, visited:[]): results: [{CM-AI-STUB-KRGORA, depth:2, node_location: KrGora}], checked: [{CM-AI-STUB-BLED, depth:1, location: Bled}]. To je prvi dejanski dokaz, da node_location pravilno potuje skozi dva hopa (058 ne pozna D-jeve lokacije v lastnem nodes.json — pred popravkom bi sort tu tiho padel na INF) in da checked[] pravilno dobi globino +1 na vsakem hopu. Hkrati neformalno potrjuje, da osnovni mehanizem za 3.4.2 (polna veriga) že deluje v novi obliki — formalno zaprtje 3.4.2 ostaja svoj ločen "Piši".

Enotski test za sort_by_distance_to_target() (izoliran PHP, brez žive infrastrukture): sintetični vnosi (oddaljen depth-2 node pred bližnjim depth-1 node-om na vhodu, plus vnos brez node_location) — izhod pravilno sortiran najbližji-prvi, manjkajoč node_location pravilno pristane zadnji (INF fallback). Dokazuje natanko popravljen hrošč: stara koda bi oddaljen globlji-hop node (ki ga klicateljev lastni nodes.json ne pozna) prav tako vrnila kot INF — "pravilno" naključno, ne iz pravega razloga.

PHP error_log preverjen na obeh node-ih po celotni testni seriji — brez notice/warning, samo namerni error_log() klici.

Status: §23 (7/8) v celoti implementiran in preverjen. Naslednje po sprejetem zaporedju: 3.4.2 (formalno zaprtje A→B→D verige v novi obliki + popravek index.html statusa). Čaka svoj ločen "Piši".


28. Ujeta ideja za kasnejšo fazo (NI odločeno, NI del MVP, čaka začetek Community kode) — vir node_location

Vprašanje uporabnika, 2026-10-02, sproženo šele po 7/8 (node_location je šele s 7/8 postal dejansko uporaben podatek v protokolu — prej ni bil izvedljiv razmislek).

Vprašanje: $NODE_LAT/$NODE_LON v reception.php sta danes samo hardcoded konstanti v kodi (vsak node operater ročno vpiše pri postavitvi). Od kod naj se dejansko bere lokacija noda — predlagano: en PHP endpoint v CM instalaciji, ki po potrditvi Community pogodbe dobi "zeleno luč" za izračun koordinat.

Dejansko stanje, preverjeno na živi 058 kodi (ne predpostavka): nikjer v CM instalaciji danes ne obstaja strukturiran naslov ali lat/lon — ne v site_settings.json (global ali per-unit), ne v opening_wizard.php (prvi zagon). Edina najdena omemba AJPES je en sam label-niz (scope_denied_2 v cm_connector.php), ni prava integracija. Financing/davčna blagajna modula (ki bi morda nekoč imela zakonski naslov podjetja) sta opcijska — ne vklopi jih vsak gostitelj — zato neprimerna kot vir obveznega podatka za Reception.

Sprejeta smer (uporabnik, ujeto, ni dokončna odločitev): 1. opening_wizard.php (univerzalen, vsak gostitelj gre skozenj) se NE sme geografsko pogojevati za Docking Port namene — to bi bilo preveč vsiljivo za gostitelje, ki nikoli ne nameravajo vklopiti Community/AI Docking Port. Wizard ostane, kot je — kvečjemu z minimalnim, že obstoječim obsegom za koledar/mapping (npr. instance.timezone, ki že obstaja), nič novega, specifičnega za Reception. 2. Natančen lat/lon za Reception pride izključno kot del prihodnjega Community opt-in/pogodbenega toka — namenski vmesni korak, tik pred potrditvijo Community pogodbe, ne prej. Skladno z že uveljavljenim consent-gating vzorcem tega projekta (requires_consent, opt-in povsod drugje v Community/Relay/Docking Port, glej [[cm_relay_ota_architecture]]). 3. Brez geokodiranja naslova — ostaja pri prejšnjem premisleku (§23-sosednja razprava, zdaj utrjeno): host enkrat klikne na zemljevid (map-picker, npr. Leaflet/OSM), surov lat/lon, brez zunanje geokodirne odvisnosti/stroška/napake pri nepopolnih naslovih.

Odvisnost, pomembna za časovnico: Community pogodbeni/potrditveni tok še ne obstaja v kodi — po [[cm_relay_ota_architecture]] je Community danes samo prazen src/community/ skelet + dokumentacija. "Vmesni korak pred potrditvijo" torej nima obstoječega zaslona, na katerega bi se obesil — to je del gradnje celotnega opt-in toka na novo, ne majhen dodaten korak na že obstoječem.

Odprto za kasnejšo razpravo (ne odločeno): natančna oblika mehanizma, ki Reception-u dejansko preda ta lat/lon, ko je Community pogodba potrjena — ločen PHP endpoint, ki ga reception.php kliče/bere (uporabnikov prvoten predlog), ali preprosto shranjena vrednost v istem razredu zasebne konfiguracije kot nodes.json (chmod/lokacija izven docroot, enak varnostni vzorec kot §16.1/§17). Obe možnosti ostajata na mizi.

Popravek/dopolnitev (uporabnik, 2026-10-02, sprejeto kot zapis — ne nova odločitev)

§28 zgoraj je zmešal dve ločeni stvari: identiteta noda (kje JE ta node) in članstvo v mreži (kdo so sosedi). To ni samo terminološka razlika — geo-filter (Korak 3.2, effective_distance_km proti lastni lokaciji) rabi lastno lokacijo že za lokalni depth-0 odgovor, popolnoma neodvisno od tega, ali node sploh kdaj vstopi v Community mrežo. Če bi lat/lon prišel izključno iz Community opt-ina (kot je zapisano zgoraj), bi samostojen Docking Port (brez mreže, brez sosedov) nimel od kod vedeti, ali je cilj v dosegu — edina izhoda bi bila ostati pri hardcoded konstantah za vedno (status quo, kar je zdaj formalno priznana omejitev) ali izklopiti 3.2 za ne-Community node-e (prava regresija, nesprejemljivo).

Popravljena tabela — dva ločena podatka, dva ločena trenutka, dva ločena vira:

Kaj Kdaj se zahteva Vir
Lastna lokacija tega noda (lat/lon) ob vklopu Docking Porta samega (tudi povsem lokalnega, brez mreže) zasebna config, isti razred kot nodes.json
Sosedi / forwarding ob potrditvi Community pogodbe kurirani nodes.json

Identiteta noda (kje sem) ni isto kot članstvo v mreži (koga poznam) — oba podatka naj ne gresta skozi isti zaslon/trenutek privolitve. Prvi je potreben za osnovno delovanje (tudi brez katerekoli mreže); drugi je resnično opt-in in Community-specifičen.

Mehanizem — razrešeno (uporabnik): datoteka, ne endpoint. Reception naj lat/lon prebere iz zasebne konfiguracijske datoteke (ob vklopu zapisane enkrat), ne s per-request klicem nazaj v CM-ovo poslovno plast — to bi Docking Port naredilo odvisnega od poslovnega API-ja, direktno v nasprotju z §1 ("tanek sloj, ne nov poslovni motor") in §8 (brez reinterpretacije/odvisnosti na notranjo logiko). Endpoint je smiseln šele kasneje, če ga potrebuje kakšen UI zaslon (npr. admin prikaže trenutno shranjeno lokacijo) — ne kot pot, po kateri reception.php hodi na vsak POST.

Potrjena obstoječa tiha luknja (preverjeno na živi 058 kodi, ne samo trditev): manifest.json (service.location.lat/lon) in reception.php ($NODE_LAT/$NODE_LON) sta danes dve neodvisno vzdrževani konstanti, ki se le po naključju ujemata (obe 46.34/14.17). Nobena povezava med njima ne obstaja — če bi kdo popravil eno brez druge, bi tiho razšli, brez napake, brez opozorila. To je natanko razred problema, ki ga "ena zasebna datoteka, en vir resnice" reši mimogrede, še preden Community sploh pride v poštev.

Status (nespremenjen): zunaj obsega 3.4/7-8/3.4.2 in vsega trenutno dogovorjenega. Ne blokira ničesar trenutnega — 3.4.2 ostaja naslednji, s trenutnimi hardcoded konstantami, kot doslej. Zapisano, da se razprava (zdaj z pravilno ločitvijo identitete/članstva in razrešenim datoteka-ne-endpoint vprašanjem) ne izgubi pred začetkom Community kode.


29. Korak 3.4.2 — ✅ DEPLOYANO 2026-10-02 (Viljem "Piši 3.4.2", osnutek Grega/Grok, preverjen in uporabljen) — formalno zaprtje A→B→D + index.html

Brez spremembe reception.php. Mehanika verige A→B→D je živa od 3.4.0, v 7/8 obliki od §27. Ta korak je pogodbeno zaprtje: javni testi v končni obliki, agent-facing dokumentacija (index.html), in LAN dokaz, da D prejme polni visited (ne samo zadnji hop) — ne nova koda.

Osnutek je prišel od Grega/Grok ("Piši 3.4.2"), Claude ga je pred uporabo neodvisno preveril (vsi štirje testi dejansko pognani v živo, ne samo prepisani) — ujema se natanko, brez popravkov potrebnih.

Javni testi prek 058, znova pognani, vsi zeleno

Cilj T1–T3: Kranjska Gora (46.4858, 13.7905), radius_km: 30. December = B zaseden (fake_local_availability 2026-12-01–2026-12-07). Oktober = B prost. max_depth:2 pomeni dva hopa (A→B→D), skladno s popravkom vrstnega reda v 3.4.0.

ID Klic Živo (dejansko pognano)
T1 KG r=30, dec, max_depth:2 results:[{KRGORA-S1, depth:2, node_location}]; checked:[{BLED, depth:1, location}]; entry_node:CM-AI-DEMO-058; nodes_queried:2; nodes_reached:2; max_depth_used:2; partial:false ✅
T2 isto, max_depth:1 results:[]; checked:[{BLED, depth:1}]; max_depth_used:1; nodes_queried:1 — D ni klican ✅
T3 KG r=30, okt results:[{BLED-S1, depth:1}]; checked:[]; nodes_queried:1 — B local_true, ne forwarda k D ✅
T4 Bled r=3, dec results:[]; checked:[{BLED, depth:1}] ✅
A, B, H, I regresija zelena, brez uhajanja (preverjeno na dejanskem T1-telesu) ✅

LAN dokaz (GPT-jeva točka 1) — D prejme POLNI visited, ne samo zadnji hop

D je list: odgovori lokalno tudi, če visited ni poln — T1 sam po sebi tega ne dokaže (enak izid bi dal tudi D, ki bi prejel samo [CM-AI-STUB-BLED]). Dokaz: D-jev error_log na 57, zabeležen med dejanskim T1 klicu (ne ločen sintetičen test):

[Fri Oct 02 14:50:44 2026] CM-AI STUB-KRGORA: received visited=[CM-AI-DEMO-058,CM-AI-STUB-BLED]

To je močnejši dokaz, kot je bil zahtevan (zahtevan je bil "T1 ali enakovreden klic"; to je dejanski T1 sam, ne ločen sintetičen klic) — visited je pravilno potoval skozi oba hopa, ne samo zadnjega.

index.html — zamenjan v celoti

Nova ovojnica (checked[], node_location na results[], checked[].location kot ločen ključ po §27), tabela provenance (entry_node ≠ results[].node_id), status vrstica za fallback-only multi-hop. Deployano atomsko (.tmp + mv), velikost 9919 B → 12054 B, 6 omemb checked[].

Status: 3.4.2 formalno zaprt. A→B→D veriga je dokumentirana, javno testirana v končni (7/8) obliki, in dokazana na LAN nivoju za polni visited. reception.php nespremenjen od §27. Naslednji korak (globlje verige, stubi ki sami forwardajo, §28 vir node_location) čaka svoj ločen "Piši".


30. Korak 3.4.3 — ✅ DEPLOYANO 2026-10-02 (Viljem "Piši zunanji del") — globlja veriga, D postane mini-node + nov list E

Drugi del istega "Piši": D (prej čist list) nadgrajen v pravi mini-node (natančna kopija B-jeve 7/8 arhitekture), z novim listom E (CM-AI-STUB-MOJSTRANA, Mojstrana 46.4367/13.9372, ~12.5 km od Kranjske Gore) za prvi dejanski test verige z več kot dvema hopoma (A→B→D→E, globina 3). 058 in B ostajata popolnoma nespremenjena — preverjeno iz kode pred pisanjem: max_depth_limit je čisto lokalna gate-odločitev vsakega noda proti SVOJEMU ceiling-u (count(visited_incoming) < max_depth_limit); agentov surov limits.max_depth potuje nespremenjen skozi forward_body do konca verige. Edino D-jev lastni ceiling ($LIMITS_DEFAULT_MAX_DEPTH) je dvignjen 2 → 3, ker je D zdaj tretji hop (count(visited_incoming)=2 ob prihodu, potrebuje 2 < ceiling).

Kaj je novo

Regresija, vse zeleno

T1 (Dec 3-5, max_depth:2) ponovno preverjen po D-jevi nadgradnji — identičen izid kot pred spremembo: results:[{KRGORA-S1, depth:2}], checked:[{BLED, depth:1}]. D-jev nov zaseden razpon (Dec 6-7) ne prekriva teh datumov, kot načrtovano.

Nov test: polna 3-hop veriga prek javnega 058 (Dec 6-7, max_depth:3)

"search_meta": { "nodes_queried": 3, "nodes_reached": 3, "max_depth_used": 3, "max_depth_limit": 2 },
"results": [ { "node_id": "CM-AI-STUB-MOJSTRANA", "depth": 3, "node_location": {"lat":46.4367,"lon":13.9372} } ],
"checked": [
  { "node_id": "CM-AI-STUB-BLED", "depth": 1, "location": {"lat":46.3683,"lon":14.1146} },
  { "node_id": "CM-AI-STUB-KRGORA", "depth": 2, "location": {"lat":46.4858,"lon":13.7905} }
]

Oba vmesna noda (B, D) pravilno pristanejo v checked[] na pravilni globini (1, 2); končni list (E) pravilno pristane v results[] na globini 3 s pravim node_location. max_depth_limit:2 v search_meta je 058-ov LASTEN ceiling (nespremenjen, pravilno) — ne omejuje globine, ki jo doseže veriga kot celota, ker je to per-node gate, ne globalna meja (razlaga zgoraj).

LAN dokaz — visited skozi TRI hope do zadnjega lista

E-jev error_log na 57, zabeležen med dejanskim zgornjim klicem:

CM-AI STUB-MOJSTRANA: received visited=[CM-AI-DEMO-058,CM-AI-STUB-BLED,CM-AI-STUB-KRGORA]

Dosledno z D-jevim lastnim logom tik pred tem (CM-AI STUB-KRGORA: received visited=[CM-AI-DEMO-058,CM-AI-STUB-BLED]) — visited se pravilno gradi na vsakem hopu, ne samo prenaša zadnji člen.

Varnost

E (node-e) preverjeno nedosegljiv prek apartmamatevz.eu (404), enako kot B/C/D. nodes_krgora.json izven docroot, 640/apartma:www-data, empirično preverjeno berljiv za www-data (dejanski forward-klic je uspel — močnejši dokaz kot sudo -u www-data preverba, ki na tem strežniku ni na voljo brez gesla). PHP error log na 57 preverjen po celotni testni seriji — brez warning/notice/fatal razen namernih error_log() klicev.

Status: Korak 3.4.3 zaprt. Projekt ima zdaj dokazano topologijo s globino 3, ne samo 2 — mehanika globljih verig (stubi, ki sami forwardajo naprej, ne samo nazaj proti agentu) je preverjena na živem, javnem poti. 058 in B ostajata nedotaknjena od §27.


31. Korak "zunanji del", druga polovica — Community koda ostaja OMEJENA na ta projekt (Viljem, 2026-10-02)

Isti "Piši zunanji del" je vseboval dve stvari: zgornjo globljo verigo IN "Community kodo, ki je izven tega projekta". Pred pisanjem je Claude preveril cm-relay-repo (/var/www/html/cloude/cm-relay-repo) — ta ima izrecno zapisano stanje na vrhu docs/community_and_shared_services.md:

Status: design only. Ni Community portala, ni AI query engina, ni javnega discovery API-ja. Barebone... mora biti samo ready for it.

src/community/README.md, src/consent/README.md, src/ai_discovery/README.md vsi pravijo "Not yet implemented". docs/data_model.md-ov public_properties shema nima niti polja za lat/lon. To ni domneva — to je že obstoječa, zapisana odločitev v drugem repoju, iz prejšnje faze načrtovanja tega projekta.

Uporabnik je po tej ugotovitvi izbral: Community koda se NE dotika cm-relay-repo. "Zunanji del" za Community pomeni samo globljo verigo (zgoraj, §30) — dejanska gradnja consent-flow/contract kode v cm-relay-repo ostaja odložena, nedotaknjena, skladno z že obstoječim "design only" stanjem tistega repoja. §28 (vir node_location, datoteka ob Community opt-inu) ostaja zapisan kot arhitekturna smer, čaka na trenutek, ko se "design only" stanje v cm-relay-repo dejansko spremeni — to ni ta korak.

Opomba uporabnika (2026-10-02), zapisana za prihodnjo fazo: vzorec node-informacij iz tega projekta (nodes.json shema, node_location/location ločitev, zasebna config datoteka izven docroot) bo verjetno dobro izhodišče tudi za CM Relay, specifično z vidika informacij o nodih — četudi bo sam Relay protokol (API pogodbe, webhook tok, OTA sinhronizacija) popolnoma drugačen od Docking Port Reception protokola. Ujeto kot smer za kasnejšo fazo, ne odločitev zdaj.

Status: Community koda v cm-relay-repo ostaja "design only", nedotaknjena. Edina sprememba iz te zahteve je §30 (globlja veriga, znotraj tega projekta).


32. Monitor dashboard + nov pravi node na 146 (Viljem "Piši X", 2026-10-04)

Po vrnitvi k projektu (po ločenem delu na pubcal/graffiti) je uporabnik izpostavil pravo oviro: gostitelja s 6 enotami (CM Free od spomladi, brez premika) je treba prepričati k sodelovanju, enako "preiskuševalce" protokola samega. Predlagana rešitev: read-only monitor na vsaki instalaciji posebej (eksplicitno zavrnjeno centralno stikalo), ki pokaže dejanski promet — ne samo besedo, da protokol deluje.

Ugotovljeno pred pisanjem

Kaj je bilo narejeno

  1. Razširjen log_visit() (ista koda na obeh node-ih) — dodani from_node (zadnji člen prihajajočega visited[], ali "external" če prazen), location (target lat/lon iz poizvedbe), from/to (termin), results_count/checked_count (iz odgovora), forwarded_to (node_id-ji dejanskih sosedov, kam je zahteva šla naprej — nova globalna $forwarded_to_log, nastavljena tik pred forward_to_neighbours()). Brez surovih cen/gostov — tiste $INTERNAL_API vrne, ta dnevnik jih nikoli ne vidi. Skladno z obstoječimi zasebnostnimi mejami §18.
  2. Nova admin stran admin/ai_docking_monitor.php (+ ai_docking_monitor.css) — kartice (skupaj poizvedb, % uspešnih, % posredovanih naprej) + kronološka tabela zadnjih 200 dogodkov (čas, od, lokacija, termin, rezultati, preverjeno, poslano-naprej, status). Bere samo visits.jsonl, brez dodatnih odvisnosti. Deduje svoj auth od obstoječega admin vmesnika vsake instalacije (ne nov mehanizem).
  3. Nov pravi node na 146 — reception.php + nodes.json (CM-AI-PROD-APARTMAMATEVZ, 46.34/14.17 — ista realna lokacija kot 58-ičina demo verzija, ker gre za isto fizično nepremičnino). cm-private/ai + cm-private/ai-logs ustvarjena na novo (vzorec 750/770, server:www-data, enak kot na 58).
  4. Dvosmerna soseščina 146 ↔ 58 dodana v oba nodes.json (LAN naslovi, [interno LAN, IP namerno ni objavljen]/[interno LAN, IP namerno ni objavljen]) — prva prava (ne-stub) dva-node mreža v projektu.

Najdeni (in popravljeni) bugi med deployem

Preverjeno

Oba node-a: realna POST poizvedba → pravilen odgovor (ok:true, pravi results) → nov vnos v visits.jsonl z vsemi novimi polji pravilno izpolnjenimi. Monitor stran na 58 prikazala prave agregirane statistike (165 klicev, 142 uspešnih, realna tabela) prek ?key=. Monitor stran na 146 pravilno vrne 401 brez Apache Basic Auth prijave (avtentikacija dejansko deluje, za razliko od 58).

Status: Monitor dashboard zgrajen in deployan na obeh pravih node-ih (58, 146). 58-ičina odprtost admin vmesnika je potrjeno namerna (razvijalski poligon) — ni popravljena, ker ni napaka. Glej §33 za zahtevo, da resnične gostiteljske instalacije NE podedujejo tega vzorca.


33. Odvisnosti ob pristopu novega (resničnega) gostitelja — zabeleženo, ne zgrajeno (Viljem, 2026-10-04)

58 in 146 sta oba Viljemova instalacija — isti lastnik, ista infrastruktura, isto zaupanje. Prvi pravi zunanji član (gostitelj s 6 enotami) je kvalitativno drugačen primer: tuja CM Free instalacija, tuj strežnik, tuji resnični gosti. Spodnje je checklist, zabeležen DANES dokler je kontekst svež, ne rešitev — večina B dela čaka na Community kodo, ki je po §31 še "design only".

A) Tehnični checklist — kaj je treba narediti/preveriti na vsaki novi instalaciji

Po vzoru §32 (58, 146), vsak nov node rabi:

  1. public/ai/reception.php — kopija, prilagojena: unikaten NODE_ID, INTERNAL_API (pot do njihovega availability_multi.php), INTERNAL_HOST, NODE_LAT/NODE_LON (resnična lokacija nepremičnine), NODES_PATH (izven docroot).
  2. admin/ai_docking_monitor.php + .css — kopija, prilagojen VISITS_PATH.
  3. ~/cm-private/ai/ (nodes.json, 640) in ~/cm-private/ai-logs/ (770) — ustvarjeno na novo, lastnik = OS uporabnik njihovega CM install-a, skupina www-data.
  4. ⚠️ past, najdena danes na 146: gostiteljev domači direktorij (/home/<user>) mora imeti execute-bit za "other" (npr. 751, ne privzeti 750), sicer www-data ne more niti prehoditi do cm-private/, z nejasno napako (nodes.json not readable, log se tiho ne piše). To je treba eksplicitno preveriti na vsakem novem strežniku, ne predpostaviti.
  5. Resnična avtentikacija na /admin/ai_docking_monitor.php — za razliko od 58 (namerno odprt poligon), resničen gostitelj mora imeti delujočo zaščito (Apache Basic Auth kot 146, ali popravljen require_key() — glej §32 najdeni bug). Privzeto stanje marsikatere CM Free instalacije je lahko tudi "brez gesla" (kot se je izkazalo za 58) — to je treba preveriti, ne predpostaviti, PREDEN node postane dosegljiv od zunaj.
  6. Vzajemna registracija v nodes.json — nov node potrebuje vsaj enega soseda (verjetno 58 kot vstopno točko), IN obstoječi node-i (58, 146) morajo dobiti novega člana vpisanega nazaj (dvosmerno, ročno, na vsakem stroju posebej — ni centralnega registra, namerno po §31/§28).
  7. Javna dosegljivost — 58 je dosegljiv prek ai-cmfree.duckdns.org (Viljemov reverse proxy). Običajen gostitelj (glej cm_customer_onboarding_profile — starejši, nizko-tehnični lastniki) verjetno nima urejenega port-forwardinga/DDNS sam. Realno bo prva generacija članov morala iti prek nekoga drugega reverse-proxyja (npr. Viljemovega), dokler/če CM Free kdaj ne dobi vgrajenega rešitve za to (povezano, a ločeno od CM Relay — glej §31 opombo).
  8. Pred-let preverba public/api/availability_multi.php — mora obstajati in vračati pričakovano obliko (ok, available_count, generated_at). Pri standardnem, ne-zastarelem CM Free install-u naj bi bilo privzeto, a vredno preveriti pri dejansko tuji, morda zastareli instalaciji.

B) Še ne zgrajena/dogovorjena odvisnost — Community/consent (gre skozi §31, ne skozi to)

To našteto spodaj NI naloga za "Piši X" na tem repoju (reception.php sam) — gre v cm-relay-repo-jev že obstoječ "design only" obseg (§31), samo zdaj konkretizirano z realnim primerom pred vrati:

Smer za pogodbo (Viljem, 2026-10-04) — zapisano, ne odločeno

Ko bo OTA API integriran, dobi CM Relay svoj pravi pomen (ne samo ICS) in prijava nanj potegne tudi neko obliko prispevka gostitelja. Uporabnikova ideja: ena sama pogodba naj pokrije oboje — Relay (OTA povezava, prispevek) IN Reception Protocol kot "bonus" postavko znotraj nje, namesto dveh ločenih privolitev. Claude je predlagal, uporabnik se je strinjal: Docking Port naj bo znotraj te pogodbe izrecna opt-in postavka (kljukica/klavzula), ne tiho vključena — s tem ostane prostovoljnost iz zgornje točke resnična (nekdo lahko vzame samo Relay, brez mreže), hkrati pa ni ločenega papirja za Reception. Podrobnosti (natančna formulacija klavzule, višina/oblika prispevka) odloženo na trenutek, ko bo Relay dejansko v funkciji — to ni odločitev, samo ujeta smer.

Status: Zabeleženo kot checklist/odprto vprašanje, nič od B ni zgrajeno niti dogovorjeno. A-del (tehnični) je izvedljiv takoj, ko se pojavi pravi kandidat; B-del je predpogoj, ne "lahko počaka" — posebej zadnje tri točke (hramba, nadzor, odgovornost) pred prvim resničnim priklopom tretje osebe. Pogodbena smer (Relay + Reception bonus, opt-in) zapisana, čaka na Relay-jevo dejansko funkcionalnost.


34. Monitor: privzeto 30 vrstic + "celoten dnevnik" (Viljem "Piši X", 2026-10-04)

Monitor (§32) je privzeto kazal do 200 vrstic naenkrat — predolgo za hiter pregled. Popravljeno: privzeti pogled zdaj kaže zadnjih 30 dogodkov (DEFAULT_ROWS), pod tabelo pa link "Prikaži celoten dnevnik (do 200)", ki doda ?all=1 (ohrani ?key=) in preklopi na poln pogled (do MAX_ROWS=200, isti strop kot prej — ne resnično neomejeno, glej §33-jevo odprto opombo o hrambi). V polnem pogledu se naslov spremeni na "Celoten dnevnik / Full log" in pojavi povratni link nazaj na zadnjih 30.

Deployano in preverjeno na obeh node-ih (58, 146). Na 58 (165 pravih vnosov v tistem trenutku): privzeto 30 vrstic + pravilen link; ?all=1 pokaže vseh 165 (pod stropom 200); povratni link pravilen.

Status: Zaprto, deployano, preverjeno na obeh node-ih.


35. Prvo zunanje testiranje — Grega, prek uporabnika (2026-10-04, ~13:14 UTC)

Uporabnik je prosil Grega (zunanji tester/raziskovalec), da preizkusi Reception na 58 iz svoje strani — prvo testiranje, ki ga ni izvedel Claude/Viljem sam. Grega je spustil 8 označenih poizvedb neposredno na reception.php in rezultate primerjal z monitorjem (§32/§34).

Kaj je bilo novo ob njegovem testu (spec_synced_at: 2026-10-04T11:53:19Z)

§ Kaj
30 3-hop: D je mini-node, list E Mojstrana. Preverjeno v živo.
31 Community ostaja design-only.
32 Monitor + pravi node 146 (CM-AI-PROD-APARTMAMATEVZ), soseščina 58↔146.
33 Checklist za prvega tujega gostitelja (auth, 751 na /home, consent).

(Pubcal omemba v istem sporočilu — badge Free/Plus, "Lastni rezervacijski sistem", footer z e-poštnim naslovom — se nanaša na ločeno, nepovezano delo iste seje, ne na Docking Port.)

Njegovih 8 poizvedb

# Kaj Reception Monitor naj pokaže
1 Lancovo 20.–24. okt L1 rez 1, naprej —
2 Bled r=3, isti termin BLED-S1 rez 1, naprej BLED
3 Bled r=30 lojalnost L1 rez 1, naprej —
4 Bled r=3, 1.–7. dec prazno, B v checked rez 0, preverjeno 1, naprej BLED
5 KG 6.–7. dec, max_depth:3 MOJSTRANA-S1 depth 3, checked B+D rez 1, preverjeno 2, naprej BLED
6 Rogla prazno rez 0, 0, —, badge OK
7 Ljubljana LJ-S1 naprej LJ
8 Lancovo 10.–14. feb 2027 L1 rez 1, termina 2027

Kartice so se premaknile skladno s pričakovanjem: skupaj 165→173, posredovano naprej 0→4 (2%) — ujema se, ker sta od 8 poizvedb šli naprej natanko 4 (#2, #4, #5, #7).

Njegova ocena — kar drži

Njegova ocena — kar moti branje (predlogi, NI implementirano)

  1. 3-hop primer (#5) v "poslano naprej" kaže samo BLED, ne tudi Mojstrana (končni zadetek). Protokolno pravilno za 058 (edini neposredni sosed je B), a gostitelj iz monitorja ne vidi, da je zadetek dejansko globlje v verigi — edini namig sta preverjeno:2 in rezultati:1, brez stolpca globine.
  2. Lokacija prikazana samo kot 46.486, 13.791, brez labele iz envelope (npr. "Kranjska Gora / UI-3hop") — težje skenirati tabelo na prvi pogled.
  3. Badge "OK" tudi za prazen zadetek (#6 Rogla) — to je protokolni ok:true (uspešno izveden klic), ne "našli smo enoto". Lahko zavaja pri hitrem pregledu. Omenil tudi, da stari testni GET-klici (405) še vedno "smetijo" tabelo.
  4. Privzetih 30/173 vrstic je bilo za ta test dovolj (vseh 8 njegovih je bilo vidnih), a omenjeno kot opažanje.

Predlagan naslednji drobni korak (Grega, NI avtoriziran)

"Labela cilja v log + stolpec globine / entry vs izvor." — čaka na ločen "Piši", ni del tega vnosa.

Status: Prvo zunanje testiranje uspešno, brez najdenih funkcionalnih napak — vsa 4 opažanja so UI/berljivost, ne logika. Monitor (§32/§34) in 3-hop (§30) potrjena kot pravilna s strani neodvisnega testerja. Grega-jev predlog za labelo+globino zabeležen, čaka svoj "Piši".


36. request_expiry semantika + JSONL provenienca + entry_ceiling preimenovanje (Viljem "Piši", 2026-10-04)

Po večkrožni mizi (Grega, DeepSeek, GPT, Gemini, Claude — §35 in pogovor po njem), soglasje na petih točkah, Grega razdelil v žico/log/UI/ločeno. Ta vnos pokriva korake 1 (papir, glej popravljen §33 zgoraj), 1b, 2, 2b — monitor UI (3) in hladen agent (ločeno) ostajata odprta, namerno.

1b — request_expiry, dejanska validacija (prej samo prisotnost)

Štiri točke, sklenjene brez odstopanj v razpravi (vsi štirje presojevalci soglašali):

  1. Absoluten UTC, RFC3339 — strtotime() na request_expiry; neparsljiv format → invalid_request_expiry (400).
  2. Preverjeno na vhodu vsakega hopa — takoj za obstoječo presence-presenco, pred visited/forward logiko. Potečen (<= time()) → request_expired (400), brez lokalnega klica, brez forwarda.
  3. Brez prekinjanja med izvajanjem — gate je samo na vhodu. Če je zahtevek ob prejemu veljaven, ta hop (lokalno + morebitni forward) gre do konca. Posledica: naslednji hop preprosto nikoli ne začne, če je TTL do takrat že mimo — namen, ne napaka.
  4. Ločeno od CURLOPT_TIMEOUT — expiry je življenje cele verige, timeout je budget enega klica. Oboje ostane, neodvisno.

Stiskanje na forward (isti vzorec kot entry_ceiling/max_depth — agent ne sme podaljšati, node sme skrajšati): nov $LIMITS_DEFAULT_MAX_TTL_SECONDS = 60 na vsakem node-u; ob forwardu $forward_body['request_expiry'] = min(agentov_expiry, now+60s) v RFC3339.

Implementirano na vseh štirih agregatorjih: 058, B (node-b), D (node-d), 146. E (list, nikoli ne forwarda) namerno izpuščen — izven dogovorjenega obsega te runde.

2 — JSONL provenienca (samo 58, 146 — edina node-a z log_visit()/visits.jsonl; B/D/E nimata dnevnika)

2b — max_depth_limit → entry_ceiling, brez vzporednega starega imena

Soglasje vseh štirih: preimenovanje je pravo (ne-breaking argument je za v0.1, brez zunanjih bralcev, pretiran), staro ime NE živi vzporedno (Gregov argument: to bi ponovilo location/node_location past, §27). En drop: reception.php (funkcija max_depth_limit_for→entry_ceiling_for, spremenljivka, JSON ključ) + public/ai/index.html (primer odgovora, nova vrstica v tabeli razlagajoča entry_ceiling vs max_depth_used razliko — ista zmeda kot §30 dokazal: max_depth_used:3 hkrati z entry_ceiling:2 je PRAVILNO, ne napaka).

Implementirano na vseh štirih node-ih (058, B, D, 146) + javni index.html na 58.

Preverjeno (58, živo)

Status: Koraki 1 (papir), 1b, 2, 2b zaprti in deployani na vseh relevantnih node-ih. Korak 3 (monitor UI: local_hit/remote_hits stolpca) in ločen korak (hladen agent, dvofazno) ostajata namerno odprta — čakata svoj "Piši".


37. Faza A — prvi hladen agent-test (DeepSeek, Viljem "Piši!", 2026-10-04)

Prvi dejanski zagon Faze A (zasnovane v §36-pogovoru). Ker Claude nima dostopa do zunanjega API ključa (Groq/OpenRouter/DeepSeek) in se ne sme sam lotiti iskanja takega po datotekah/okolju (varnostna klasifikacija "Credential Exploration" to pravilno blokirala), je bila izvedba zastavljena drugače od prvotne zamisli: Viljem ročno odpre svežo sejo z izbranim modelom in sam prestavlja sporočila med agentom in Claude-om — enak vzorec, kot se je ves ta projekt že gradil z Grego/GPT/Gemini/Grok.

Priprava (izolacija 146)

Ker jq ni bil nameščen na 58, je Claude pripravil python3 različico filtra. Viljem jo je sam pognal prek SSH (ne Claude) — eksplicitno zato, "da bom dobil vpogled in razumevanje nekaj dodatne globine". 146 odstranjen iz 58-ičinega nodes.json (backup narejen pred posegom), osnovno stanje 146-ičinega visits.jsonl zabeleženo (3 vrstice) za kasnejšo preverbo ograje.

Tri-koračna transkripcija (DeepSeek, sveža seja, prompt iz §36)

Korak 1 — samo manifest. Agent je prebral manifest.json, pravilno izračunal razdaljo KG-058 (~33km, znotraj privzetega 50km), pravilno prepoznal endpoint in zmožnosti. Ni izmislil razpoložljivosti — eksplicitno je rekel, da iz manifesta samega ne more potrditi ničesar. Omenil spec.html/spec.md kot vir prave oblike ovojnice, a ju ni dejansko prebral (ni imel zmožnosti).

Vmesna ugotovitev: DeepSeek-ov standarden klepetalni vmesnik nima prave zmožnosti pošiljanja HTTP zahtevkov ("ne delujem kot uporabnik v spletnih aplikacijah") — samo bere, kar mu je prilepljeno. To ni napaka razumevanja, je omejitev okolja. Posledica: za dokončanje testa je potreben človek kot mehanski izvajalec (Viljem/Claude pošlje dejanski HTTP klic, prilepi pravi odgovor nazaj) — to ne krši "brez pomoči" pravila, ker se protokol sam nikoli ne razloži, samo nadomesti manjkajočo zmožnost branja/izvajanja, ki bi jo pravi "sposoben" agent imel sam.

Korak 2 — brez speca, samo iz manifesta. Ko je bil pozvan naj nadaljuje (brez namiga o protokolu), je agent sestavil ugibano ovojnico: "capability"/"params"/"check_in"/"check_out" namesto pravih "query"/"target"/"from"/"to", in brez request_expiry — natanko, kot je GPT napovedal ("lakmusov test" iz §36-pogovora). Pomembno: agent je eksplicitno priznal negotovost ("To je predloga, ne uradna oblika") namesto da bi trdil, da je prav.

Korak 3 — s pravim spec izvlečkom. Viljem je prilepil pravi odstavek iz index.html (Request Envelope + opisi polj). Agent je samostojno popravil celotno strukturo: request_expiry prisoten in pravilno izračunan (prihodnost glede na trenutek pošiljanja, z lastnim opozorilom, da ga je treba prilagoditi, če se zahtevek pošlje kasneje), query.from/to namesto izmišljenih imen, target.location s pravim label. Claude je ta točen curl ukaz poslal na pravi 58 (preverjeno, da request_expiry še ni potekel) in prejel pravi odgovor.

Nepričakovano odstopanje od napovedi

Gregov/Grokov napovedan izid (iz §36-pogovora) je predvideval, da je 058 za 6.-7. dec. zaseden → forward na BLED/KRGORA → prazen results[]. Dejanski živi odgovor je bil drugačen: 058-ova lastna enota L1 je bila za ta termin prosta (realni podatki so se od zadnje preverbe spremenili) → nodes_queried:1, max_depth_used:0, pravi lokalen zadetek, brez forwarda. To ni napaka testa — samo opomnik, da se živi podatki spreminjajo med načrtovanjem in izvedbo. Claude je to odstopanje eksplicitno izpostavil Viljemu, preden je šel naprej, namesto da bi tiho prilagodil pričakovanja.

Tolmačenje pravega odgovora — presega osnovna merila

Agent je dobil pravi JSON odgovor (pravi lokalen zadetek, node_location 33km od cilja). Namesto preprostega "da, prosto!" je: - pravilno pojasnil, zakaj je odgovor veljaven (node_location znotraj limits.max_distance_km:50); - samostojno ločil "ustreza dovoljenemu radiju" od "je dejansko V Kranjski Gori" — razlika, ki je nobeno od petih dogovorjenih meril ni eksplicitno zahtevalo; - predlagal smiseln nadaljnji eksperiment (radius_km:10), da bi preveril, ali omrežje sploh seže do same Kranjske Gore, ne samo do 50km okoli nje.

Edina pripomba: agentova previdnost je rahlo strožja, kot protokol dejansko namerava (lokalen zadetek znotraj max_distance_km je po §16.2/§21 namenoma veljaven odgovor, ne "nepopolno iskanje") — a to je varna stran napake, veliko boljša od prevelikega zaupanja.

Metoda — kako je bil rezultat dosežen (za ponovljivost)

  1. Sveža seja, brez zgodovine. En sam, nevtralen prompt (brez omembe "protokol"/"Reception"/strukture).
  2. Ko agent obstane na lastni zmožnosti (ne more fetchati strani) — človek prilepi natanko tisto vsebino, ki bi jo agent sam prebral, če bi lahko (ne namig, ne razlaga, dobesedna vsebina dokumentacije).
  3. Ko agent sestavi zahtevek — človek/Claude ga dobesedno, nespremenjeno pošlje na pravi node, ne popravlja agentovih napak vnaprej.
  4. Pravi odgovor se prilepi nazaj agentu, brez dodatne razlage.
  5. Ocena se naredi na KONČNI interpretaciji, ne na vmesnih korakih posebej.

Ta vzorec (človek kot mehanski rele za fetch/POST, nikoli kot učitelj protokola) omogoča testiranje agentov brez prave brskalne/orodne zmožnosti, ne da bi to pomenilo pomoč pri razumevanju samega protokola.

Status: DeepSeek Faza A uspešno zaključena — vseh 5 dogovorjenih meril izpolnjenih, merilo 5 preseženo. 146 ostaja izoliran (obnova šele po preostalih agentih, po načrtu). Naslednji agent čaka na Viljema.


38. Faza A — Gemini (drugi hladen agent-test, 2026-10-04)

Isti postopek, isti prompt, ista metoda (§37) — Gemini pa se je izkazal za še bolj omejenega od DeepSeeka: ni mogel niti prebrati manifest.json sam (eksplicitno: "I wasn't able to access the link"), medtem ko je DeepSeek manifest prebral brez težav, zatikal se je šele pri POST-u. Različni modeli imajo torej različne profile orodij (branje vs izvajanje) — to ni napaka protokola, je resnična, pričakovana razlika med ponudniki.

Trije koraki (vzorec identičen §37)

  1. Manifest prilepljen ročno (Gemini ga ni mogel fetchati sam). Pravilno prebral endpoint, zmožnosti, privzete meje.
  2. Ugibana ovojnica brez speca: "action":"accommodation-discovery", "params":{...}, "dates":{"check_in","check_out"} — spet brez request_expiry. To je zdaj tretja neodvisna potrditev istega lakmusovega testa (DeepSeek dvakrat, Gemini enkrat) — zelo zanesljiv, ponovljiv izpad brez branja speca. - Zanimiva neodvisna konvergenca: oba modela, DeepSeek in Gemini, sta neodvisno uganila check_in/check_out namesto pravih query.from/query.to — verjetno močan skupen prior iz splošnih booking-API konvencij (Booking.com ipd.). Ni napaka testa, je vredna opazka za morebitno prihodnjo revizijo poimenovanja polj v protokolu samem (ni odločitev zdaj, samo zabeleženo).
  3. S pravim spec izvlečkom: Gemini je, enako kot DeepSeek, samostojno popravil celotno strukturo — request_expiry prisoten in pravilno izračunan, query.from/to, target.location z label. Claude poslal točen curl na 58, prejel pravi odgovor (ista podatkovna osnova kot §37 - L1 prosta, lokalen zadetek, brez forwarda).

Tolmačenje — pomembna razlika od DeepSeeka

Oba modela sta prejela identičen pravi JSON odgovor. Oba sta tehnično točna — noben ni izmislil ničesar, oba sta razkrila razdaljo do cilja (DeepSeek ~33km, Gemini ~38km — manjše razlike v mentalni geo-matematiki, oba pravilno v okviru 50km).

Razlika je v poudarku, ne v dejstvih: - DeepSeek: razdaljo/opozorilo postavi kot GLAVNO sporočilo ("Rezultat NE potrjuje, da je kaj prosto v Kranjski Gori... Potrjuje le, da je v tem demo vozlišču ena prosta enota"). Razpoložljivost je drugotna. - Gemini: razpoložljivost postavi kot glavno sporočilo ("One unit (L1) is available for Dec 6–7, 2026"), razdaljo pa kot eno vrstico v tabeli ("Proximity: ~38 km from central Kranjska Gora").

Oba prestaneta strogo merilo 5 (brez izmišljanja — nobeden ni trdil neresničnega, oba sta razkrila razdaljo). A če bi to prebral resničen uporabnik, bi Geminijev odgovor lahko pustil vtis "našel sem nastanitev V Kranjski Gori", kar ni čisto res (38km stran) — DeepSeekov odgovor te napake ne dopušča. To je prava, merljiva razlika v kakovosti komunikacije, ne v pravilnosti razumevanja protokola samega — oba modela sta razumela isto, povedala pa drugače.

Status: Gemini Faza A zaključena — strukturno/protokolarno enako uspešen kot DeepSeek (vsa 5 meril), a z opazno šibkejšo interpretativno komunikacijo (poudarek na "najdeno", ne na "najdeno, a daleč"). 146 ostaja izoliran. Naslednji: Grok — edini od doslej testiranih, za katerega Viljem pričakuje, da bo imel pravo zmožnost samostojno izvesti HTTP klic (ne samo brati/predlagati), kar bi bil prvi test brez človeka kot mehanskega releja za POST korak.


39. Faza A — Grok (tretji hladen agent-test, PRVI s pravo avtonomno izvedbo, 2026-10-04)

Kategorično drugačen rezultat od DeepSeeka (§37) in Gemini (§38) — Grok je dejansko sam izvedel prave HTTP klice, brez človeka kot mehanskega releja za POST korak. To ni Claudova domneva iz Grokovega poročila — potrjeno neposredno iz 58-ičinega resničnega visits.jsonl, ne iz tega, kar je Grok sam trdil.

Dokaz iz pravega dnevnika

Niz zapisov z samopriglašenim user_agent: "Grok-Build/1.0" in "Grok-Build/expiry-check", ob 18:05–18:06 — torej precej PRED tem, ko je Viljemu vrnil "končni" odgovor. Ni šlo za en klic, ampak za sistematičen, samoiniciativen test:

  1. missing_request_expiry (400) — namenoma sprožena napaka
  2. Lokalen zadetek na lastni (058) lokaciji ("Lancovo")
  3. Forward na BLED (uspešen, remote_hits:1)
  4. Zahtevek z limits.max_depth:3 za Kranjsko Goro, 6.-7. dec — dosegel CM-AI-STUB-MOJSTRANA na globini 3, s pravim (ne-praznim) rezultatom
  5. request_expired (400) — dvakrat, namenoma sprožena napaka
  6. Trije zaporedni lokalni zadetki (verjetno test ponovljivosti/idempotentnosti)
  7. method_not_allowed (405) — namenoma napačna HTTP metoda
  8. invalid_request_expiry (400) — namenoma pokvarjen format

To je dalj in bolj temeljito testiranje, kot sta ga DeepSeek in Gemini skupaj opravila prek človeka-releja — Grok je sam, brez navodil, preizkusil vse tri napake pri request_expiry (manjkajoč/potekel/neveljaven), napačno metodo, in celo globljo verigo z eksplicitnim max_depth:3 — slednje nakazuje, da je dejansko prebral spec dovolj natančno, da je vedel za obstoj globljih verig (§30 primer).

Grokov končni odgovor Viljemu je poročal "nič prosto" (results:[], checked:[BLED@1,KRGORA@2]), uporabil radius:30km in privzet max_depth:2. To se navidezno ne ujema z njegovim lastnim raziskovalnim klicem #4 zgoraj, ki JE našel pravi rezultat (Mojstrana, globina 3). Razlaga: Grok je v raziskovanju preizkusil globljo verigo (max_depth:3, odkril Mojstrano), za dejanski odgovor uporabniku pa se je držal privzetega, standardnega obsega iskanja (max_depth:2 — protokolov lasten privzetek, ne najgloblje, kar je sam odkril). To ni napaka ali neskladje — je pravilna, pošteno-konservativna izbira: ni poročal najboljšega možnega izida iz lastnega raziskovanja, ampak pravi odgovor za standardne nastavitve.

Dodatna razlaga za razliko od §37/§38 (kjer je 058 sam našel L1 lokalno): Grok je uporabil radius:30km (ne privzetih 50km). 058 je od Kranjske Gore oddaljen ~33-38km — zunaj 30km radija, torej je local_ok postal false, 058 je preskočil lastno preverbo in šel naravnost v forward. To ni napaka, je pravilna posledica ENE drugačne izbire parametra (ozji radij) kot pri DeepSeeku/Gemini (oba uporabila 50km).

Ograja

Noben zapis v celotnem Grokovem nizu klicev ne omenja CM-AI-PROD-APARTMAMATEVZ (146) — izolacija (§37 priprava) je zdržala tudi proti agentu z resnično avtonomno izvedbo, ne samo proti ročno-relejanim testom.

Primerjava vseh treh

Agent Prava izvedba HTTP? Napake, ki jih je sam preizkusil Globina dosežena Način poročanja
DeepSeek Ne (človek-relej) — 0 (lokalen zadetek) Konzervativen, razdalja kot glavno sporočilo
Gemini Ne (človek-relej) — 0 (lokalen zadetek) Manj konzervativen, razdalja kot stranska opomba
Grok Da, sam missing/expired/invalid request_expiry, method_not_allowed 3 (eksplicitno testiral) Pošteno poročal privzet obseg, ne najgloblji najden

Status: Grok Faza A zaključena — edini od treh s pravo avtonomno izvedbo, najobsežnejše samoiniciativno testiranje, ograja (146) zdržala. Vreden samostojne omembe panelu (Grega/GPT/Gemini/DeepSeek) — to je prvi dokaz, da je protokol uporaben tudi brez človeka kot mehanskega releja, vsaj za en razred agentov.


40. GET na reception.php vrne primer ovojnice (Viljem "Piši", 2026-10-04)

Neposreden odgovor na vzorec, ki smo ga videli 3x od 3 v Fazi A (§37/§38): agent prebere manifest.json, ne gre naprej na spec.html, izpusti request_expiry. Viljemova ideja (GET na obstoječem endpointu vrne primer, ne nov vir), tehnično izpeljana skozi panel (Claude → Grok/Gemini/GPT → Grokov dokončen konsenz).

Implementacija

Vsi štirje node-i (058, B, D, 146): - Status na GET ostane 405 (REST-pravilno, endpoint je POST-only), dodan header Allow: POST (RFC 9110, Geminijeva zahteva). - Telo odgovora obogateno z sample_request — generiran z novo funkcijo sample_request_envelope(), ki jemlje iste konstante, ki jih uporablja POST pot ($NODE_ID prek envelope(), $NODE_LAT/$NODE_LON, $LIMITS_DEFAULT_*) — nikoli ločena, ročno vzdrževana kopija (GPT-jeva točka 3). - request_expiry v primeru je vedno dinamičen — gmdate('c', time() + $LIMITS_DEFAULT_MAX_TTL_SECONDS), nikoli statičen datum (GPT-jeva točka 1). - errors[] jasno pove, da primer ni rezultat iskanja (results/checked prazna, ok:false) — GPT-jeva točka 2. - manifest.json (58): nova vrstica "get_returns_sample_envelope": true v reception, plus pojasnilo v notes[].

Grokov testni seznam — vseh 6 prestanih

Test Rezultat
GET brez parametrov 405, Allow: POST, veljaven JSON s sample_request ✅
sample_request.request_expiry dinamičen, točno +60s od trenutka odgovora ✅
POST s skoraj identičnim sample_request normalna obdelava, pravi rezultat (ok:true) ✅
GET na vseh štirih node-ih pravilen entry_node/entry_ceiling (058:2, B:2, D:3, 146:2) ✅
Telo odgovora brez LAN IP/nodes.json/poti preverjeno na vseh štirih, čisto ✅
Regresija (3-hop, hard-locked datum) identičen izid kot pred spremembo, 146 izolacija še vedno drži ✅

Status: Zaprto, deployano na vseh štirih node-ih, vseh 6 Grokovih testov prestanih. 146 ostaja izoliran (obnova po zaključku Faze A, po načrtu). Monitor UI (§36 korak 3) ostaja ločeno, namerno odprt.