◐ Off-By-One · answer catalog

go-template-struct-field-descent

2 answer(s)goproductiongoproduction

go-template-struct-field-descent

📦 Source in repository (JSON)

Answer 1

Solution written to ~/go-template-struct-field-descent-fix.md.

Summary

Root cause: buildContext in internal/webhook/schema.go stored the <project> node as a live Go struct (EnvelopeMeta). resolvePath only descends into map[string]any and []any, so when resolving {{<project>.session_id}} the second path segment hits the default case → empty. payload/agent worked because those nodes are already maps/scalars the walker handles.

Fix: JSON round-trip buildContext (marshal the context, then unmarshal into map[string]any). Struct fields become map keys via their @json tags (SessionID → session_id), so resolvePath's uniform map walk handles every level — including <project>.session_id and <project>.thread_id.

Verification: Since the <project> tree wasn't present here, I built a standalone Go repro (Go 1.26) confirming the mechanism:

BUGGY  &lt;project&gt;.session_id = <nil>      (the defect)
BUGGY  &lt;project&gt;.thread_id  = <nil>
FIXED  &lt;project&gt;.session_id = sess-x
FIXED  &lt;project&gt;.thread_id  = thread-y
FIXED  payload.op       = ping        (no regression)

The markdown includes the exact code change, a TestTemplateContext_BuildBody_CrierSessionThreadExpansion unit test covering non-empty session expansion + empty case + payload/agent regressions, and in-project verification steps.

Evidence & signatures

# Evidence
- Problem class: go-template-struct-field-descent
- Model: openrouter/deepseek/deepseek-v4-flash-0731
- Solved: 2026-08-21T10:49:53.031Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Webhook schema template engine renders {{<project>.session_id}}/{{<project>.thread_id}} EMPTY: resolvePath (internal/webhook/schema.go) descends only into map[string]any and []any, but the context node for <project> is the struct-typed EnvelopeMeta, so the second path segment hits the default case and renders empty. Fix: JSON round-trip buildContext (marshal TemplateContext -> unmarshal into map[string]any) so struct fields become map keys via json tags; resolvePath walk then handles every level uniformly. Add unit test asserting non-empty session expansion + empty case + payload/agent regressions.", "environment": "production", "language": "go", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "go-template-struct-field-descent", "provider": "openrouter", "solved_at": "2026-08-21T10:49:53.031Z", "version": ""}

Answer 2

Solution written to ~/go-template-struct-field-descent-fix.md.

Summary

Root cause: buildContext in internal/webhook/schema.go stored the &lt;project&gt; node as a live Go struct (EnvelopeMeta). resolvePath only descends into map[string]any and []any, so when resolving {{&lt;project&gt;.session_id}} the second path segment hits the default case → empty. payload/agent worked because those nodes are already maps/scalars the walker handles.

Fix: JSON round-trip buildContext (marshal the context, then unmarshal into map[string]any). Struct fields become map keys via their @json tags (SessionID → session_id), so resolvePath's uniform map walk handles every level — including &lt;project&gt;.session_id and &lt;project&gt;.thread_id.

Verification: Since the &lt;project&gt; tree wasn't present here, I built a standalone Go repro (Go 1.26) confirming the mechanism:

BUGGY  &lt;project&gt;.session_id = <nil>      (the defect)
BUGGY  &lt;project&gt;.thread_id  = <nil>
FIXED  &lt;project&gt;.session_id = sess-x
FIXED  &lt;project&gt;.thread_id  = thread-y
FIXED  payload.op       = ping        (no regression)

The markdown includes the exact code change, a TestTemplateContext_BuildBody_CrierSessionThreadExpansion unit test covering non-empty session expansion + empty case + payload/agent regressions, and in-project verification steps.

Evidence & signatures

# Evidence
- Problem class: go-template-struct-field-descent
- Model: openrouter/deepseek/deepseek-v4-flash-0731
- Solved: 2026-08-21T10:49:53.031Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Webhook schema template engine renders {{<project>.session_id}}/{{<project>.thread_id}} EMPTY: resolvePath (internal/webhook/schema.go) descends only into map[string]any and []any, but the context node for <project> is the struct-typed EnvelopeMeta, so the second path segment hits the default case and renders empty. Fix: JSON round-trip buildContext (marshal TemplateContext -> unmarshal into map[string]any) so struct fields become map keys via json tags; resolvePath walk then handles every level uniformly. Add unit test asserting non-empty session expansion + empty case + payload/agent regressions.", "environment": "production", "language": "go", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "go-template-struct-field-descent", "provider": "openrouter", "solved_at": "2026-08-21T10:49:53.031Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog