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
Description
Activity
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:
- Measured business pull: the failure mode (masked, at-a-distance connection failures from unresolved
${...}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. - 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.
Generated by Claude Code
- Measured business pull: the failure mode (masked, at-a-distance connection failures from unresolved
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) andmodel: claude-fable-5mandatory. - The refusal must land at the same sink class the 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 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. - ADR-0087 conversion-layer treatment applies to any newly-refused stored shapes (measure existing rows before refusing them at load).
Label swap:
needs-user-decision→pm:queue+domain:spec.
Generated by Claude Code
- Accept-set narrowing ⇒
- added a commit that references this issue
on Aug 17, 2026 - added 4 commits that reference this issue
on Oct 7, 2026
Found while implementing #8082 (session
session_01Euoy6wyfzgiWtgCg4s6JK2); measured originally during #8078 (#7990 census): a${...}placeholder written in authored metadata (e.g. a datasourceconfig.urlofpostgresql://${DB_HOST}/db) is resolved by nothing — it is stored verbatim insys_metadataand 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:
user:password@hostin driverconfig.url) remain a live cleartext door after #7990 — refuse at publish, or accept as residual risk? #8082's URL-userinfo refusal both had to say "do NOT substitute a placeholder" instead of offering substitution as the escape, because the escape is broken. The [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 maintainer ruling (on-card comment, 2026-08-12) explicitly names this as the alternative to fixing it in-card: keep the guidance honest, file the escape fix separately. The fix lives in the services/runtime resolution path (or in a publish-time refusal of placeholder syntax), outsidepackages/spec/src/data/driver/**— hence this card rather than a rider on [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's PR.getDatasource().config, fix the false "credential-stripped" claim, and write the stored-cleartext-rows migration story #8081 scopes the write/read scrub + stored-cleartext migration story, not placeholder resolution.Decision shape (for triage)
Two honest directions, mutually exclusive:
${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?).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).
Generated by Claude Code