You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
${...} placeholders in authored metadata resolve to nothing and reach the consumer verbatim — the masked-failure escape #8078 measured, now load-bearing for two refusal messages #8336
Found while implementing #8082 (session session_01Euoy6wyfzgiWtgCg4s6JK2); measured originally during #8078 (#7990 census): a ${...} placeholder written in authored metadata (e.g. a datasource config.url of postgresql://${DB_HOST}/db) is resolved by nothing — it is stored verbatim in sys_metadata and handed verbatim to the database client at connect. The author believes environment substitution happens; the connection then fails (or connects somewhere unintended) with no error pointing at the unresolved placeholder — the masked-failure shape.
Why this now needs its own card
Two shipped refusal messages are written AROUND this defect rather than through it:
Implement resolution: define where ${ENV} in authored metadata is substituted (presumably at connect/render in services, never at rest), and which keys participate. Cost: a real capability with a security surface (env exfiltration via metadata authorship needs thought — who can author metadata that reads arbitrary server env vars?).
Refuse loudly at publish: placeholder syntax in connection-material keys is rejected with "placeholders are not resolved here" guidance, making the non-capability explicit (declared = enforced; startup-focus favors this until a real pull exists).
Either ends the silent half. The worst state is the current one: syntax that looks supported, stores fine, and fails at a distance.
Refs: #7990 (census measurement), #8078 (pinned "refuses a placeholder value exactly like a real one — the KEY is the sink"), #8082 (ruling naming this escape as binding context).
Triage: needs-user-decision — the filer frames this as two mutually-exclusive honest directions and that's correct: this is a scope call, not a bug fix.
Platform long-term coherence: option 2 (refuse loudly at publish) makes declared = enforced, closing a syntax-that-looks-supported-but-isn't gap cleanly. Option 1 (implement resolution) adds a real runtime capability with its own lifecycle to maintain.
AI-agent error-resistance: option 1 opens a genuine security question the filer names directly — "who can author metadata that reads arbitrary server env vars?" — that's exactly the kind of consumption-side leniency this repo's contract-first posture tries to avoid designing in. Option 2 has no such surface.
Startup scope discipline: no measured pull for the substitution capability today; the filer's own read ("startup-focus favors this until a real pull exists") points at option 2.
Recommendation: option 2 — refuse ${...}-shaped values loudly at publish in connection-material keys, with guidance pointing at the real escape (credentialsRef / bound secrets, per the #8082 family). Revisit option 1 only if a real deployment need for environment-driven config surfaces later; implementing a security-sensitive capability speculatively is exactly the kind of scope creep the startup-stage principle warns against.
Maintainer ruling — direction 2 (refuse loudly at publish)
Ruled by the maintainer in a live PM session, 2026-08-13 (session session_015XyMgCSMGGn9oSwbQEeWYg), verbatim: 「其他全部接受你的建议。」 — accepting direction 2 as recommended.
Ruling: direction 2. Placeholder syntax (${...}) in connection-material keys is rejected at publish with explicit "placeholders are not resolved here" guidance. Direction 1 (implement resolution) is rejected for now: it is a real new capability with a real security surface (metadata authors reading arbitrary server env vars) and zero measured pull for actual substitution — declared = enforced favours making the non-capability explicit. If a real pull materialises later, implementing resolution supersedes the refusal via a new decision card.
Implementation notes for dispatch:
Accept-set narrowing ⇒ domain:spec (label set by this triage) and model: claude-fable-5 mandatory.
Activity
hotlong commentedon Aug 13, 2026
Triage:
needs-user-decision— the filer frames this as two mutually-exclusive honest directions and that's correct: this is a scope call, not a bug fix.Four-axis read:
${...}placeholders) is real and already load-bearing for two shipped refusal messages (feat(spec)!: refuse inline credentials at publish — driver config + connector authoring door (#7990, spec half) #8078, [Decision] URL-embedded credentials (user:password@hostin driverconfig.url) remain a live cleartext door after #7990 — refuse at publish, or accept as residual risk? #8082) that had to talk around it. No evidence yet of a real pull for the capability (actual env-substitution) itself — nobody has asked for${ENV}to work, only been confused that it silently doesn't.Recommendation: option 2 — refuse
${...}-shaped values loudly at publish in connection-material keys, with guidance pointing at the real escape (credentialsRef/ bound secrets, per the #8082 family). Revisit option 1 only if a real deployment need for environment-driven config surfaces later; implementing a security-sensitive capability speculatively is exactly the kind of scope creep the startup-stage principle warns against.Generated by Claude Code
hotlong commentedon Aug 13, 2026
Maintainer ruling — direction 2 (refuse loudly at publish)
Ruled by the maintainer in a live PM session, 2026-08-13 (session
session_015XyMgCSMGGn9oSwbQEeWYg), verbatim: 「其他全部接受你的建议。」 — accepting direction 2 as recommended.Ruling: direction 2. Placeholder syntax (
${...}) in connection-material keys is rejected at publish with explicit "placeholders are not resolved here" guidance. Direction 1 (implement resolution) is rejected for now: it is a real new capability with a real security surface (metadata authors reading arbitrary server env vars) and zero measured pull for actual substitution — declared = enforced favours making the non-capability explicit. If a real pull materialises later, implementing resolution supersedes the refusal via a new decision card.Implementation notes for dispatch:
domain:spec(label set by this triage) andmodel: claude-fable-5mandatory.user:password@hostin driverconfig.url) remain a live cleartext door after #7990 — refuse at publish, or accept as residual risk? #8082 refusals guard (connection-material keys), withcode+statusasserted in coverage; align the refusal guidance with the two shipped messages that currently say "do NOT substitute a placeholder" — after this lands they can point at the refusal instead of warning around it.Label swap:
needs-user-decision→pm:queue+domain:spec.Generated by Claude Code
feat(spec): refuse `${…}` placeholder syntax in connection-material d…
docs(spec): nine migration-step rationale passages and two registry c…
docs(spec): re-anchor the last six dead tracker citations in packages…
test(spec): the first data/ file group's test titles state each cited…
test(spec): the last data/ file group's test titles state each cited …