fix(#2009): load-failed capability gates fail open with a loud warning
Previously a capability that failed to LOAD (e.g. incompatible engines.gsd) but declared a gate-kind loop hook caused the loop resolver to inject a BLOCKING synthetic gate (blocking:true, onError:halt) at every declared point, halting every ship:pre / verify:post project-wide over an unrelated load error, with no remediation surfaced. Per maintainer decision (#2009) it now fails OPEN: no gate is injected (the loop proceeds; --active-cap correctly reports the failed cap inactive) and a loud warning is emitted — to stderr (the channel host workflows/agents actually see) and in the envelope 'warnings' array — naming the load reason and the exact 'gsd capability remove <id>' remediation. The loader still records blockedGates; only the consequence changes from block to warn. Security (review): capId and reason originate from a third-party manifest / directory name. capId is validated against the canonical kebab-case id shape before it is placed in the runnable remediation command (withheld otherwise); reason is stripped of control chars and backticks. This closes an argument/ prompt-injection vector in the surfaced message. Docs updated to the fail-open-warning posture (ARCHITECTURE, INVENTORY, CONFIGURATION, README, capability-overlay-model). Also removes a dead 'before' import surfaced by lint in the issue-2045 test.
This commit is contained in:
@@ -17,10 +17,12 @@
|
||||
* `gsd-core-` / `anthropic-` id prefix) is rejected.
|
||||
* - Load-time re-gate (default-resilient): an overlay that fails validation or
|
||||
* whose `engines.gsd` does not satisfy the running GSD version is SKIPPED
|
||||
* with a warning — it never crashes the loop. EXCEPTION (per-hook-kind
|
||||
* policy): a skipped capability that declares a `gate` is recorded in
|
||||
* `_overlay.incompatibleGateCapIds` so the loop resolver can fail CLOSED for
|
||||
* that gate rather than silently proceeding as if it had passed.
|
||||
* with a warning — it never crashes the loop. A skipped capability that
|
||||
* declares a `gate` is additionally recorded in
|
||||
* `_overlay.incompatibleGateCapIds` / `_overlay.blockedGates` so the loop
|
||||
* resolver can surface a loud fail-OPEN advisory for that gate (#2009): the
|
||||
* un-evaluable gate is skipped (not enforced) with a remediation message,
|
||||
* rather than silently vanishing.
|
||||
*
|
||||
* The merged registry is materialized by the canonical `buildRegistry`
|
||||
* (re-exported from the generator, which ships) over a cap-map reconstructed
|
||||
@@ -845,12 +847,12 @@ export function loadRegistry(options: LoadRegistryOptions = {}): Registry {
|
||||
// to load. Clear the map (the first-party base never lists overlay commandRoots — first-party
|
||||
// command modules ship in bin/lib/, not via _overlay.commandRoots).
|
||||
meta.commandRoots = {};
|
||||
// #1461 OVL-2 fail-CLOSED on compose failure (HIGH): the fallback DROPS every accepted overlay,
|
||||
// so any accepted overlay that DECLARED a gate would have its gate silently vanish → a blocking
|
||||
// gate FAILS OPEN, violating ADR-1244 (a skipped capability declaring a gate must FAIL CLOSED).
|
||||
// #1461 OVL-2 (HIGH): on compose failure the fallback DROPS every accepted overlay, so any
|
||||
// accepted overlay that DECLARED a gate would have its gate silently vanish with no trace.
|
||||
// Record each dropped gate-declaring overlay's gate as blocked using the SAME extraction the
|
||||
// per-candidate `skip()` closure uses (gatePointsOf), so loop-resolver injects the synthetic
|
||||
// blocking gate at each declared point exactly as it would for a per-candidate skip.
|
||||
// per-candidate `skip()` closure uses (gatePointsOf), so loop-resolver surfaces the loud
|
||||
// fail-OPEN advisory (#2009) at each declared point exactly as it would for a per-candidate
|
||||
// skip — the gate does not silently disappear.
|
||||
for (const cap of overlayCaps) {
|
||||
const gatePoints = gatePointsOf(cap);
|
||||
if (gatePoints.length === 0) continue;
|
||||
|
||||
Reference in New Issue
Block a user