FormatForRemediation surfaced config and log paths but not data paths,
while FormatForAIContext (chat) does. For remediation those matter —
backup targets, disk-full triage, restore points (e.g. a database data
dir or HA's /config/.storage). Add a Data Directories section, matching
the chat pack. Extends the remediation test to assert a data path
reaches it; teeth-checked. Full servicediscovery package green.
Add two common-service cells to the context oracle. Beyond documenting
nginx and MQTT, they pin two code paths the existing cells did not cover:
the read-only bind-mount marker (nginx config mounted read-only) and
security-category fact surfacing (mosquitto auth). Both pass against the
current formatters (no production gap) — regression protection for the
iter4/iter5 mount + fact-filter work. Corpus now 6 cells (HA-LXC,
HA-Docker, postgres, frigate, nginx, mosquitto). Full package green.
FormatForRemediation (the discovery context Patrol/remediation consume)
surfaced CLI access, config/log paths and ONLY hardware facts — so the
context meant for fixing a workload never told you how to restart it or
where to edit its files on the host, the two core fix actions. Add a
'Service Control' section (service-category facts: systemd unit / restart
command) and a 'Bind Mounts (host -> container)' section, matching the
parity FormatForAIContext already has after iters 4-7.
New test asserts the restart command and host bind-mount source reach the
remediation context; teeth-checked. Build + vet + gofmt clean, full
servicediscovery package green.
The backend captures DockerMounts (and now surfaces them to the
assistant), but the human-facing DiscoveryTab showed config/data/log
paths and ports while dropping the host<->container bind mounts — so a
user could not see where a container path like /config actually lives on
the host to edit or back up files. Add docker_mounts to the frontend
ResourceDiscovery type and render a 'Bind Mounts' card (host source
copyable, '-> destination in container', read-only marker), consistent
with the Ports card.
Test renders an app-container discovery with a bind mount and asserts
the host source is visible. Type-check + full lint + prettier clean;
full suite green (2 pre-existing guardrail failures unrelated).
Iter 5 added service+storage to the surfaced fact categories but the
context pack still capped at the first 5 facts by insertion order — so a
trailing service-control fact (how to restart the workload, the most
actionable one we just started capturing) could be silently dropped,
undermining iter 5-6.
Sort the priority facts by actionability (service > security >
dependency > hardware > version > storage, stable within category)
before capping, and raise the cap 5 -> 8 (still under the analyzer's
12-fact limit). The most useful facts now always survive.
Corpus: add a fact-heavy Frigate cell (6 priority facts, service-control
last). Filter test now asserts cap=8 and that a trailing service fact
sorts first and survives. Both teeth-checked. Full package green.
Iterations 4-5 made the context pack SURFACE Docker mounts and
service/storage facts; this closes the CAPTURE side so the analyzer
actually produces them. The workload analysis prompt asked for config
dirs but never for how to restart/reload the service or the specific
files a user edits. Add: (q9) how the service is managed/restarted; an
instruction to put specific key files (configuration.yaml,
automations.yaml, postgresql.conf) in config_paths rather than just the
parent dir; and an instruction to record the service-control mechanism
as a 'service'-category fact. Directly serves the 'reload my automation'
case.
Test pins both instructions in the built deep prompt; teeth-checked.
Build + vet + gofmt clean, full servicediscovery package green.
filterImportantFacts kept only hardware/dependency/security/version
facts, dropping 'service' and 'storage'. But a service fact (e.g. the
systemd unit) is exactly how the Assistant restarts/reloads a workload,
and a storage fact (the backing dataset/disk) is where its data lives —
neither is redundant with the CLI/path sections, and both are what a
real question like 'the database is slow, restart it' needs. Add both to
the priority categories.
Corpus: add a postgresql LXC cell whose required context includes the
systemd unit and data filesystem. Teeth-checked — the case fails without
the filter change. Build + vet + gofmt clean, full servicediscovery
package green.
Two-part start of the Discovery->Assistant context-completeness work.
1. FormatForAIContext (the context pack Chat/Patrol consume) dropped
DockerMounts, even though the model captures them. A container path
like /config is meaningless for editing or backing up persistent files
without its host source, so the Assistant could not act on a real
request like 'edit my blinds automation on the host'. Surface the
host -> container mapping (with read-only marker).
2. Add scenario_corpus_test.go — the verifiable oracle for the goal:
given a realistic discovered workload, the context pack must surface
everything the Assistant needs to answer a concrete user question with
zero re-explanation. Seeded with Home Assistant (LXC: pct exec +
automations.yaml + log; Docker: docker exec + bind-mount source). The
corpus grows one service-type cell at a time; a missing substring is a
concrete gap to close in the analyzer or formatter.
Teeth-checked: the Docker case fails without change #1. Build + vet clean,
full servicediscovery package green.
The tab-wide banner says 'configure a provider before scanning', yet all
four 'Run Discovery' affordances stayed clickable into a guaranteed
failure (discovery uses the AI to analyze evidence). Route every run
button through canTriggerDiscovery, now also requiring a configured
provider, so the action matches the banner. Command/connectivity gaps
are surfaced separately and intentionally not blocked here.
Tests: existing manual-run test configures a provider so its button
stays enabled; new test asserts the run action is disabled (and trigger
never fires) without a provider. Also hardened afterEach to reset
getDiscoveryInfo between tests — clearAllMocks does not reset
mockResolvedValue impls, so a provider mock would otherwise bleed into
later no-provider cases. Discovery 16/16, type-check + lint + prettier
clean; full suite 6018 pass (2 pre-existing guardrail failures).
The AI-provider prerequisite only rendered inside the agent-only
connection-status trio, so opening Discovery on a VM/container with no
provider configured gave no hint the feature could not run — the
silently on-but-useless dead end. Promote it to a tab-wide banner driven
by the same canonical readiness verdict, styled to match the sibling
'disabled' banner, shown for every resource type. Remove the now-
redundant provider banner from the agent trio (which keeps commands ->
connectivity).
Test: new case asserts a non-agent resource surfaces the provider
prerequisite. Discovery 15/15, type-check + full lint clean; the 2
unrelated guardrail failures pre-exist on the branch.
Pure formatting: prettier collapses the readiness-trio Show condition to
a single line. No behavior change; follow-up to c2c5ce048 (eslint passed
at commit time but prettier format was not enforced by the hook).
Discovery re-derived its prerequisite state (disabled / provider /
commands / connectivity) ad hoc across surfaces, so the agent
connection-status banners could nag about commands while the more
fundamental gap — no AI provider configured — went unsaid.
Wire the (previously orphaned, unwired) computeDiscoveryReadiness module
in as the canonical verdict: useDiscoveryTabState now derives
aiProviderConfigured + a single discoveryReadiness memo, and the
DiscoveryTab connection-status block renders from readiness().status,
surfacing the most-fundamental missing prerequisite first. Adds the
missing needs_ai_provider banner. The green 'connected' banner stays
literal by design (the verdict treats unknown command state as ready).
Tests: existing command-state cases now configure a provider to reach
those states; new case asserts the provider prerequisite precedes
command guidance. Full frontend suite + type-check + lint green (the 2
unrelated guardrail failures pre-exist on the branch, proven via scoped
stash).