~bigbes/agents-dev · parade

main · last commit 13 days ago · 7g0stsfu

← Back to the parade

ah-jzv config: export a Configured() predicate per optional block; stop re-deriving presence in the wiring Past Stand

status: closed P3 chore milestone:v0.1.0
bd reopen ah-jzv
Created byEugene Blikh
Ownerbigbes@gmail.com
Created2026-08-04T23:44:00Z
Started2026-08-04T23:54:12Z
Updated2026-08-05T00:00:42Z
Closed2026-08-05T00:00:42Z
Description
Found while fixing ah-1cx.3.

config's optional blocks each have an unexported present() predicate, so every consumer outside the package hand-rolls its own presence test against raw fields: cmd/agenthubd/main.go:180 (yonote client + boot identity probe), :192 (mem0 client + health probe), :292/:298 (notifier selection), plus internal/reconcile/{publish,qa,artifacts}.go reading cfg.Yonote.BaseURL directly.

They agree with present() TODAY only because resolve trims those base URLs — which is exactly the coupling that produced ah-1cx.3, where a whitespace-only ntfy.url was absent to validation and present to wiring. The next field added without a matching trim reintroduces the same class of bug.

Fix: export one predicate per optional block (e.g. func (y Yonote) Configured() bool wrapping the unexported present()) and have every consumer call it instead of restating the test. present() being unexported is precisely why the wiring hand-rolls it.
Acceptance criteria
No consumer outside internal/config decides whether an optional block is configured by inspecting its raw fields; each calls the exported predicate. A grep for cfg.<Block>.<Field> != "" outside the package comes back empty.

Depends on

No outgoing dependencies.

Depended on by

Nothing depends on this issue.

No comments.

Close reason

Done in baa8075. Each optional block's presence rule lived only in an unexported present(), so every consumer outside the package restated it against raw fields — cmd/agenthubd/main.go at the yonote client + identity probe, the mem0 client + health probe, and both arms of the notifier-selection switch. Those raw != "" tests agreed with present() ONLY because resolve happens to trim the fields they read; that coupling is what produced ah-1cx.3, where a whitespace-only ntfy.url was absent to validation and present to the wiring and killed a boot. Added an exported Configured() on AgentsView, Telegram, Ntfy, Yonote and Mem0 — each a one-line wrapper over present(), so there is exactly one definition of presence and validate() and the wiring can never diverge. AgentsView.Configured carries the full doc comment (Configured() is the only sanctioned presence test outside the package, plus the ah-1cx.3 boot failure that justifies the rule); the other four state the rule and point at it. All four cmd/agenthubd call sites migrated, comments rewritten to record why. Purely additive: present() stays, no signature changed, go build ./... passes for every package. Cover: table-driven TestConfiguredPredicates asserting Configured()==present() for absent, single-field-set and whitespace-only inputs per block (the whitespace rows are exactly where a raw != "" test would disagree), plus an end-to-end check through Load. FOLLOW-UP NOT DONE (internal/reconcile was owned by another agent at the time), split into its own bead: reconcile.go:2010 agentsViewLink is the one real drift risk left — its raw test silently ignores a set-but-blank machine and renders a link ending in '~pi:' rather than no link; reconcile.go:1996, publish.go:83/93/114, qa.go:83/203 and artifacts.go:77/89 are correct today because they are gated upstream by r.yonote != nil, which main.go now sets from Configured(). The Configured() doc comment also asserts a rule no test or lint enforces — the acceptance grep is verified by hand.
  • Eugene Blikh created the issue · 2026-08-05T02:44:00Z
  • Eugene Blikh changed status to in_progress · 2026-08-05T02:54:12Z
  • Eugene Blikh closed the issue · 2026-08-05T03:00:41Z
    Done in baa8075. Each optional block's presence rule lived only in an unexported present(), so every consumer outside the package restated it against raw fields — cmd/agenthubd/main.go at the yonote client + identity probe, the mem0 client + health probe, and both arms of the notifier-selection switch. Those raw != "" tests agreed with present() ONLY because resolve happens to trim the fields they read; that coupling is what produced ah-1cx.3, where a whitespace-only ntfy.url was absent to validation and present to the wiring and killed a boot. Added an exported Configured() on AgentsView, Telegram, Ntfy, Yonote and Mem0 — each a one-line wrapper over present(), so there is exactly one definition of presence and validate() and the wiring can never diverge. AgentsView.Configured carries the full doc comment (Configured() is the only sanctioned presence test outside the package, plus the ah-1cx.3 boot failure that justifies the rule); the other four state the rule and point at it. All four cmd/agenthubd call sites migrated, comments rewritten to record why. Purely additive: present() stays, no signature changed, go build ./... passes for every package. Cover: table-driven TestConfiguredPredicates asserting Configured()==present() for absent, single-field-set and whitespace-only inputs per block (the whitespace rows are exactly where a raw != "" test would disagree), plus an end-to-end check through Load. FOLLOW-UP NOT DONE (internal/reconcile was owned by another agent at the time), split into its own bead: reconcile.go:2010 agentsViewLink is the one real drift risk left — its raw test silently ignores a set-but-blank machine and renders a link ending in '~pi:' rather than no link; reconcile.go:1996, publish.go:83/93/114, qa.go:83/203 and artifacts.go:77/89 are correct today because they are gated upstream by r.yonote != nil, which main.go now sets from Configured(). The Configured() doc comment also asserts a rule no test or lint enforces — the acceptance grep is verified by hand.
  • Eugene Blikh added label milestone:v0.1.0 · 2026-08-05T03:10:38Z
Stored rows — what this pane was built from, as read
issues 1 row
id ah-jzv
content_hash 0d78f613570ca2c75adf528c012e34dcb3619fcf996b32327a645896ef97baa4
title config: export a Configured() predicate per optional block; stop re-deriving presence in the wiring
description Found while fixing ah-1cx.3. config's optional blocks each have an unexported present() predicate, so every consumer outside the package hand-rolls its own presence test against raw fields: cmd/agenthubd/main.go:180 (yonote client + boot identity probe), :192 (mem0 client + health probe), :292/:298 (notifier selection), plus internal/reconcile/{publish,qa,artifacts}.go reading cfg.Yonote.BaseURL directly. They agree with present() TODAY only because resolve trims those base URLs — which is exactly the coupling that produced ah-1cx.3, where a whitespace-only ntfy.url was absent to validation and present to wiring. The next field added without a matching trim reintroduces the same class of bug. Fix: export one predicate per optional block (e.g. func (y Yonote) Configured() bool wrapping the unexported present()) and have every consumer call it instead of restating the test. present() being unexported is precisely why the wiring hand-rolls it.
design
acceptance_criteria No consumer outside internal/config decides whether an optional block is configured by inspecting its raw fields; each calls the exported predicate. A grep for cfg.<Block>.<Field> != "" outside the package comes back empty.
notes
status closed
priority 3
issue_type chore
assignee NULL
estimated_minutes NULL
created_at 2026-08-04T23:44:00Z
created_by Eugene Blikh
owner bigbes@gmail.com
updated_at 2026-08-05T00:00:42Z
closed_at 2026-08-05T00:00:42Z
closed_by_session
external_ref NULL
spec_id
compaction_level 0
compacted_at NULL
compacted_at_commit NULL
original_size NULL
sender
ephemeral 0
wisp_type
pinned 0
is_template 0
mol_type
work_type
source_system
metadata �{}
source_repo
close_reason Done in baa8075. Each optional block's presence rule lived only in an unexported present(), so every consumer outside the package restated it against raw fields — cmd/agenthubd/main.go at the yonote client + identity probe, the mem0 client + health probe, and both arms of the notifier-selection switch. Those raw != "" tests agreed with present() ONLY because resolve happens to trim the fields they read; that coupling is what produced ah-1cx.3, where a whitespace-only ntfy.url was absent to validation and present to the wiring and killed a boot. Added an exported Configured() on AgentsView, Telegram, Ntfy, Yonote and Mem0 — each a one-line wrapper over present(), so there is exactly one definition of presence and validate() and the wiring can never diverge. AgentsView.Configured carries the full doc comment (Configured() is the only sanctioned presence test outside the package, plus the ah-1cx.3 boot failure that justifies the rule); the other four state the rule and point at it. All four cmd/agenthubd call sites migrated, comments rewritten to record why. Purely additive: present() stays, no signature changed, go build ./... passes for every package. Cover: table-driven TestConfiguredPredicates asserting Configured()==present() for absent, single-field-set and whitespace-only inputs per block (the whitespace rows are exactly where a raw != "" test would disagree), plus an end-to-end check through Load. FOLLOW-UP NOT DONE (internal/reconcile was owned by another agent at the time), split into its own bead: reconcile.go:2010 agentsViewLink is the one real drift risk left — its raw test silently ignores a set-but-blank machine and renders a link ending in '~pi:' rather than no link; reconcile.go:1996, publish.go:83/93/114, qa.go:83/203 and artifacts.go:77/89 are correct today because they are gated upstream by r.yonote != nil, which main.go now sets from Configured(). The Configured() doc comment also asserts a rule no test or lint enforces — the acceptance grep is verified by hand.
event_kind
actor
target
payload
await_type
await_id
timeout_ns 0
waiters
hook_bead
role_bead
agent_state
last_activity NULL
role_type
rig
due_at NULL
defer_until NULL
no_history 0
started_at 2026-08-04T23:54:12Z
is_blocked 0
labels 1 row
issue_id ah-jzv
label milestone:v0.1.0
events 4 rows
id 019fcf29-a21f-7f26-b3cc-c9e46cec2c7f
issue_id ah-jzv
event_type created
actor Eugene Blikh
old_value
new_value
comment NULL
created_at 2026-08-05T02:44:00Z
id 019fcf32-f980-7b04-b949-d078af8d8d6f
issue_id ah-jzv
event_type status_changed
actor Eugene Blikh
old_value {"id":"ah-jzv","title":"config: export a Configured() predicate per optional block; stop re-deriving presence in the wiring","description":"Found while fixing ah-1cx.3.\n\nconfig's optional blocks each have an unexported present() predicate, so every consumer outside the package hand-rolls its own presence test against raw fields: cmd/agenthubd/main.go:180 (yonote client + boot identity probe), :192 (mem0 client + health probe), :292/:298 (notifier selection), plus internal/reconcile/{publish,qa,artifacts}.go reading cfg.Yonote.BaseURL directly.\n\nThey agree with present() TODAY only because resolve trims those base URLs — which is exactly the coupling that produced ah-1cx.3, where a whitespace-only ntfy.url was absent to validation and present to wiring. The next field added without a matching trim reintroduces the same class of bug.\n\nFix: export one predicate per optional block (e.g. func (y Yonote) Configured() bool wrapping the unexported present()) and have every consumer call it instead of restating the test. present() being unexported is precisely why the wiring hand-rolls it.","acceptance_criteria":"No consumer outside internal/config decides whether an optional block is configured by inspecting its raw fields; each calls the exported predicate. A grep for cfg.\u003cBlock\u003e.\u003cField\u003e != \"\" outside the package comes back empty.","status":"open","priority":3,"issue_type":"chore","owner":"bigbes@gmail.com","created_at":"2026-08-04T23:44:00Z","created_by":"Eugene Blikh","updated_at":"2026-08-04T23:44:00Z"}
new_value {"status":"in_progress"}
comment NULL
created_at 2026-08-05T02:54:12Z
id 019fcf38-ea48-7357-80a2-118316b8c63f
issue_id ah-jzv
event_type closed
actor Eugene Blikh
old_value
new_value Done in baa8075. Each optional block's presence rule lived only in an unexported present(), so every consumer outside the package restated it against raw fields — cmd/agenthubd/main.go at the yonote client + identity probe, the mem0 client + health probe, and both arms of the notifier-selection switch. Those raw != "" tests agreed with present() ONLY because resolve happens to trim the fields they read; that coupling is what produced ah-1cx.3, where a whitespace-only ntfy.url was absent to validation and present to the wiring and killed a boot. Added an exported Configured() on AgentsView, Telegram, Ntfy, Yonote and Mem0 — each a one-line wrapper over present(), so there is exactly one definition of presence and validate() and the wiring can never diverge. AgentsView.Configured carries the full doc comment (Configured() is the only sanctioned presence test outside the package, plus the ah-1cx.3 boot failure that justifies the rule); the other four state the rule and point at it. All four cmd/agenthubd call sites migrated, comments rewritten to record why. Purely additive: present() stays, no signature changed, go build ./... passes for every package. Cover: table-driven TestConfiguredPredicates asserting Configured()==present() for absent, single-field-set and whitespace-only inputs per block (the whitespace rows are exactly where a raw != "" test would disagree), plus an end-to-end check through Load. FOLLOW-UP NOT DONE (internal/reconcile was owned by another agent at the time), split into its own bead: reconcile.go:2010 agentsViewLink is the one real drift risk left — its raw test silently ignores a set-but-blank machine and renders a link ending in '~pi:' rather than no link; reconcile.go:1996, publish.go:83/93/114, qa.go:83/203 and artifacts.go:77/89 are correct today because they are gated upstream by r.yonote != nil, which main.go now sets from Configured(). The Configured() doc comment also asserts a rule no test or lint enforces — the acceptance grep is verified by hand.
comment NULL
created_at 2026-08-05T03:00:41Z
id 019fcf42-0485-7b8d-a6fc-fab4bff9e63b
issue_id ah-jzv
event_type label_added
actor Eugene Blikh
old_value NULL
new_value NULL
comment Added label: milestone:v0.1.0
created_at 2026-08-05T03:10:38Z