Starchaser1024 8d3af33f8e
fix(skills): keep a required secret when an optional duplicate precedes it (#7569)
* fix(skills): keep a required secret when an optional duplicate precedes it

parse_required_secrets dropped every declaration of a name after the first,
so an earlier 'optional: true' entry made a later required declaration
unreachable and the skill ran without the secret. Duplicates now keep the
first position but a required declaration upgrades the entry, and the export
manifest applies the same rule.

* test(skills): cover duplicate secret order and whitespace

Port the three parameterized ordering and whitespace cases from PR #7570 while retaining #7569's full-YAML, optional-only, and export regressions.

* refactor(skills): keep the export optional read fail-closed

export.py read `optional` with `bool(secret.get("optional", False))`, so a
truthy string would have become optional=True — the opposite of
parse_required_secrets, which treats a non-boolean value as required
(#5738). validate_skill_frontmatter_text already refuses such a package
before the dedup loop (installation applies the same rule), so that
coercion is unreachable today; read the flag as `is True` anyway so the
two surfaces cannot drift if the gate ever moves.

Pin the parity from both sides: the secrets suite asserts that the same
document is rejected by validation and read as required by the parser, and
the export suite asserts a manifest with skill_export_invalid_frontmatter
and no requirements instead of a coerced optional=True.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-26 21:25:46 +08:00
..