Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ script-test:
$(call run-timed,bash scripts/post-code-test.sh)
$(call run-timed,bash scripts/post-review-test.sh)
$(call run-timed,bash scripts/post-fix-test.sh)
$(call run-timed,bash skills/merge-queue/scripts/write-approval-check-test.sh)
$(call run-timed,bash scripts/post-retro-test.sh)
$(call run-timed,bash scripts/post-scribe-test.sh)
$(call run-timed,bash scripts/validate-output-schema-test.sh)
Expand Down
2 changes: 2 additions & 0 deletions docs/code.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ on issues (not PRs).
|-------|---------|
| `ready-to-code` | Triggers the code agent. Applied by the [triage](triage.md) agent for low-risk categories (bug, documentation, performance), or manually by a human for feature work after prioritization. Not applied when the triage result sets `requires_workflow_changes`, since the code agent cannot modify workflow files. |
| `ready-for-review` | Applied by the code agent after pushing a PR. In per-repo installs, triggers the [review agent](review.md) when applied to a PR. Also marks workflow state for humans and the [retro agent](retro.md). |
| `needs-write-approval` | Applied when `TRIGGER_ROLE` is `triage` — the dispatching user held only the GitHub `triage` role, not write+. `skills/merge-queue/scripts/enqueue-pr.sh` and `await-and-enqueue.sh` refuse to enqueue such a PR without an APPROVE review, on its current head commit, from a currently admin/maintain/write human collaborator (checked live — this label having ever been applied is derived from the immutable issue-events timeline, not the label's current presence, since GitHub's `triage` role can remove it). This does not prevent a write+ collaborator from merging directly via GitHub's native UI or `gh pr merge`, which is unaware of this label. |

## Configuration

Expand All @@ -42,6 +43,7 @@ See [Customizing with AGENTS.md](https://fullsend.sh/docs/guides/user/customizin
| Variable | Description | Default | Valid values |
|----------|-------------|---------|--------------|
| `CODE_ALLOWED_TARGET_BRANCHES` | Restricts which branches the code agent can target when pushing. The post-code script validates the agent's chosen target branch against this variable before pushing. Set via `runner_env` in `harness/code.yaml` (never injected into the sandbox). | Repo default branch (auto-detected via GitHub API; falls back to `main`) | Comma-separated branch names (e.g. `main,develop`) or `*` for any branch |
| `TRIGGER_ROLE` | Permission tier that authorized this dispatch, set by dispatch routing. When `triage`, the post-code script applies the `needs-write-approval` label. | Unset (treated as `write` — no gate) | `triage`, `write` |

## How the agent works

Expand Down
5 changes: 4 additions & 1 deletion docs/fix.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ The fix agent enforces iteration caps to prevent infinite review-fix loops:
|-------|---------|
| `fullsend-no-fix` | Prevents automatic fix runs on this PR. Applied by `/fs-fix-stop`. Manual `/fs-fix` commands are unaffected. |
| `needs-human` | The fix agent is approaching its iteration cap and needs human direction. Applied automatically when an automatic fix iteration reaches the warning threshold. |
| `needs-write-approval` | Applied when `TRIGGER_ROLE` is `triage` — the dispatching user held only the GitHub `triage` role, not write+. `skills/merge-queue/scripts/enqueue-pr.sh` and `await-and-enqueue.sh` refuse to enqueue such a PR without an APPROVE review, on its current head commit, from a currently admin/maintain/write human collaborator (checked live — this label having ever been applied is derived from the immutable issue-events timeline, not the label's current presence, since GitHub's `triage` role can remove it). This does not prevent a write+ collaborator from merging directly via GitHub's native UI or `gh pr merge`, which is unaware of this label. |

## Configuration

Expand All @@ -113,7 +114,9 @@ See [Customizing with AGENTS.md](https://fullsend.sh/docs/guides/user/customizin

### Variables

None.
| Variable | Description | Default | Valid values |
|----------|-------------|---------|--------------|
| `TRIGGER_ROLE` | Permission tier that authorized this dispatch, set by dispatch routing. When `triage`, the post-fix script applies the `needs-write-approval` label. | Unset (treated as `write` — no gate) | `triage`, `write` |

## Custom sandbox image

Expand Down
68 changes: 68 additions & 0 deletions scripts/lib/write-approval-gate.lib.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# write-approval-gate.lib.sh — Merge gate for triage-role-triggered code/fix runs.
#
# Source from post-code.src.sh / post-fix.src.sh (after post-failure-report.lib.sh
# for gha_echo):
# source "${SCRIPT_DIR}/lib/write-approval-gate.lib.sh"
#
# fullsend-ai/fullsend#5687: dispatch now accepts the GitHub `triage` role for
# /fs-code and /fs-fix. Triage-role users still get a bot-authored PR (the
# agent always held write-level credentials), but that PR must carry an
# explicit visible marker so reviewers and merge tooling know it needs a
# write+ collaborator's approval — the actual enforcement (requiring an
# approval from a currently write+ user, not just any reviewDecision) lives
# in skills/merge-queue/scripts/await-and-enqueue.sh, which checks this label.

# shellcheck shell=bash

[[ -n "${WRITE_APPROVAL_GATE_SH_LOADED:-}" ]] && return 0
WRITE_APPROVAL_GATE_SH_LOADED=1

# Emit a runner warning through gha_echo when available.
_write_approval_gate_warn() {
if declare -F gha_echo >/dev/null 2>&1; then
gha_echo warning "$*"
else
echo "warning: $*" >&2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] authorization-bypass

Unrecognized TRIGGER_ROLE values are treated as write (no gate applied). If a new role is introduced and this script is not updated, PRs triggered by that role will bypass the write-approval gate.

Suggested fix: Invert the logic: only allow known safe roles to skip the gate.

fi
}

# Normalize a TRIGGER_ROLE value: lowercase and trim surrounding whitespace.
_normalize_trigger_role() {
printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]' | xargs
}

# Apply the needs-write-approval label to a PR when TRIGGER_ROLE is "triage"
# (case-insensitive, surrounding whitespace ignored). No-op (and no gh calls)
# when TRIGGER_ROLE is unset or "write" — unset means the trigger was resolved
# at write+ (or a path that predates TRIGGER_ROLE, e.g. bot-triggered
# review->fix), so no gate is needed. Any other non-empty value is treated
# the same as "write" (no gate) but logged, since it likely indicates a bug
# in the caller rather than a legitimate write+ trigger.
# Requires REPO_FULL_NAME. Best-effort: never fails the calling script.
# Note: parameter is target_pr (not pr_number) to avoid SC2153 against
# PR_NUMBER from post-failure-report.lib.sh once both libs are bundled into

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] fail-open

The merge gate for triage-role PRs is best-effort only: if gh pr edit --add-label fails, the PR proceeds without the needs-write-approval label. The enforcement side (write_approval_ever_required) checks the issue-events timeline for a labeled event — a failed label application means the enforcement gate sees ever_required == false and returns 0, making the triage-triggered PR indistinguishable from a write-triggered PR.

Suggested fix: Make the label application a hard failure (exit non-zero), or use a second enforcement signal so enforcement does not depend solely on the label.

# post-code.sh / post-fix.sh — same fix as maybe_assign_pr in pr-assignee.lib.sh.
apply_write_approval_gate_if_needed() {
local target_pr="$1"
local role
role="$(_normalize_trigger_role "${TRIGGER_ROLE:-}")"

if [[ -z "${role}" ]]; then
return 0
fi
if [[ "${role}" != "triage" ]]; then
if [[ "${role}" != "write" ]]; then
_write_approval_gate_warn "Unrecognized TRIGGER_ROLE '${TRIGGER_ROLE}' — treating as write (no gate applied). Expected 'triage' or 'write'."
fi
return 0
fi

echo "Trigger role is 'triage' — applying needs-write-approval gate to PR #${target_pr}"
gh label create "needs-write-approval" --repo "${REPO_FULL_NAME}" \
--description "Triggered by a triage-role user; needs write+ approval before merge" \
--color "B60205" 2>/dev/null || true
gh pr edit "${target_pr}" --repo "${REPO_FULL_NAME}" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] fail-open

The merge gate for triage-role PRs is best-effort only: if gh pr edit --add-label fails (network error, token permission issue, API rate limit), the PR proceeds without the needs-write-approval label and has no merge gate. The 2>/dev/null || _write_approval_gate_warn pattern suppresses the error and continues. Since the label is the sole enforcement mechanism in this code, a transient gh failure means a triage-triggered PR becomes indistinguishable from a write-triggered PR — a silent fail-open on the authorization gate.

Suggested fix: Consider making the label application non-best-effort for the triage path (exit with error or retry on failure), or implement complementary server-side enforcement so the label is defense-in-depth rather than the sole gate.

--add-label "needs-write-approval" 2>/dev/null || \
_write_approval_gate_warn "Failed to apply needs-write-approval label to PR #${target_pr}"
Comment on lines +61 to +67

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Gate label application fail-open 📜 Skill insight ☼ Reliability

When TRIGGER_ROLE=triage, apply_write_approval_gate_if_needed treats gh pr edit failures as
warnings and returns success, so post-code/post-fix can continue and a triage-triggered PR may
proceed without the required needs-write-approval merge gate label. This creates a fail-open
authorization path where labeling errors (permissions/auth/network/API) can silently bypass the
intended enforcement signal.
Agent Prompt
## Issue description
`apply_write_approval_gate_if_needed` is intended to enforce an explicit merge-authorization gate for triage-triggered PRs by applying the `needs-write-approval` label, but it currently fails open: when `gh pr edit` cannot apply the label (permissions/auth/network/API errors, invalid PR input, missing label), the helper suppresses the error and returns success, allowing post-code/post-fix to complete and leaving a triage PR potentially mergeable without the required gate marker.

## Issue Context
The gate is described as “informational-but-load-bearing” and as a mandatory explicit merge gate for triage-triggered PRs; therefore, failure to apply the label must be treated as a hard failure (or otherwise enforced) when `TRIGGER_ROLE=triage`. Today the behavior is effectively best-effort (warning-only, `2>/dev/null`, `|| true` style suppression), and both post-code and post-fix call the helper and then continue/exit 0, meaning labeling failure is not surfaced or enforced.

## Fix Focus Areas
- scripts/lib/write-approval-gate.lib.sh[29-51]
- scripts/lib/write-approval-gate.lib.sh[37-51]
- scripts/post-code.src.sh[521-533]
- scripts/post-code.src.sh[522-532]
- scripts/post-fix.src.sh[436-456]
- scripts/post-fix.src.sh[446-456]
- scripts/post-code-test.sh[1187-1214]
- scripts/post-fix-test.sh[403-430]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

}
47 changes: 47 additions & 0 deletions scripts/post-code-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,53 @@ else
echo "PASS: script-has-noop-comment"
fi

# Verify apply_write_approval_gate_if_needed is present in the post-code script
if ! grep -q 'apply_write_approval_gate_if_needed' "${POST_SCRIPT}"; then
echo "FAIL: bundled-script-has-write-approval-gate"
echo " ${POST_SCRIPT} missing apply_write_approval_gate_if_needed"
FAILURES=$((FAILURES + 1))
else
echo "PASS: bundled-script-has-write-approval-gate"
fi

# ---------------------------------------------------------------------------
# Test helper — reimplements the gating predicate from
# lib/write-approval-gate.lib.sh's apply_write_approval_gate_if_needed:
# gate applies when TRIGGER_ROLE normalizes (lowercased, trimmed) to "triage".
# ---------------------------------------------------------------------------
gate_applies_for_role() {
local trigger_role="${1:-}"
local role
role="$(printf '%s' "${trigger_role}" | tr '[:upper:]' '[:lower:]' | xargs)"
[[ "${role}" == "triage" ]]
}

run_gate_test() {
local test_name="$1"
local trigger_role="$2"
local expected="$3" # "yes" or "no"

local actual="no"
gate_applies_for_role "${trigger_role}" && actual="yes"

if [ "${actual}" != "${expected}" ]; then
echo "FAIL: ${test_name}"
echo " TRIGGER_ROLE='${trigger_role}' expected gate=${expected}, got gate=${actual}"
FAILURES=$((FAILURES + 1))
return
fi

echo "PASS: ${test_name}"
}

run_gate_test "gate-applies-for-triage" "triage" "yes"
run_gate_test "gate-applies-for-mixed-case" "Triage" "yes"
run_gate_test "gate-applies-for-upper-case" "TRIAGE" "yes"
run_gate_test "gate-applies-with-surrounding-whitespace" " triage " "yes"
run_gate_test "gate-skipped-for-write" "write" "no"
run_gate_test "gate-skipped-for-unset" "" "no"
run_gate_test "gate-skipped-for-garbage-value" "admin" "no"

# --- Summary ---

echo ""
Expand Down
83 changes: 82 additions & 1 deletion scripts/post-code.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
# branch is allowed. (default: auto-detected)
# POST_FAILURE_DETAIL_MAX_LINES
# — max lines of failure detail in issue/PR comments (default: 30)
# TRIGGER_ROLE — permission tier that authorized this dispatch: "triage"
# or "write" (default: unset, treated as write — no gate).
# When "triage", the created PR is labeled
# needs-write-approval (fullsend-ai/fullsend#5687).
#
# Exit codes:
# 0 — branch pushed and PR created, OR agent determined nothing to do
Expand Down Expand Up @@ -676,6 +680,76 @@ maybe_assign_pr() {
}
}
# END bundled: lib/pr-assignee.lib.sh
# shellcheck source=lib/write-approval-gate.lib.sh
# BEGIN bundled: lib/write-approval-gate.lib.sh
# write-approval-gate.lib.sh — Merge gate for triage-role-triggered code/fix runs.
#
# Source from post-code.src.sh / post-fix.src.sh (after post-failure-report.lib.sh
# for gha_echo):
# source "${SCRIPT_DIR}/lib/write-approval-gate.lib.sh"
#
# fullsend-ai/fullsend#5687: dispatch now accepts the GitHub `triage` role for
# /fs-code and /fs-fix. Triage-role users still get a bot-authored PR (the
# agent always held write-level credentials), but that PR must carry an
# explicit visible marker so reviewers and merge tooling know it needs a
# write+ collaborator's approval — the actual enforcement (requiring an
# approval from a currently write+ user, not just any reviewDecision) lives
# in skills/merge-queue/scripts/await-and-enqueue.sh, which checks this label.

# shellcheck shell=bash

[[ -n "${WRITE_APPROVAL_GATE_SH_LOADED:-}" ]] && return 0
WRITE_APPROVAL_GATE_SH_LOADED=1

# Emit a runner warning through gha_echo when available.
_write_approval_gate_warn() {
if declare -F gha_echo >/dev/null 2>&1; then
gha_echo warning "$*"
else
echo "warning: $*" >&2
fi
}

# Normalize a TRIGGER_ROLE value: lowercase and trim surrounding whitespace.
_normalize_trigger_role() {
printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]' | xargs
}

# Apply the needs-write-approval label to a PR when TRIGGER_ROLE is "triage"
# (case-insensitive, surrounding whitespace ignored). No-op (and no gh calls)
# when TRIGGER_ROLE is unset or "write" — unset means the trigger was resolved
# at write+ (or a path that predates TRIGGER_ROLE, e.g. bot-triggered
# review->fix), so no gate is needed. Any other non-empty value is treated
# the same as "write" (no gate) but logged, since it likely indicates a bug
# in the caller rather than a legitimate write+ trigger.
# Requires REPO_FULL_NAME. Best-effort: never fails the calling script.
# Note: parameter is target_pr (not pr_number) to avoid SC2153 against
# PR_NUMBER from post-failure-report.lib.sh once both libs are bundled into
# post-code.sh / post-fix.sh — same fix as maybe_assign_pr in pr-assignee.lib.sh.
apply_write_approval_gate_if_needed() {
local target_pr="$1"
local role
role="$(_normalize_trigger_role "${TRIGGER_ROLE:-}")"

if [[ -z "${role}" ]]; then
return 0
fi
if [[ "${role}" != "triage" ]]; then
if [[ "${role}" != "write" ]]; then
_write_approval_gate_warn "Unrecognized TRIGGER_ROLE '${TRIGGER_ROLE}' — treating as write (no gate applied). Expected 'triage' or 'write'."
fi
return 0
fi

echo "Trigger role is 'triage' — applying needs-write-approval gate to PR #${target_pr}"
gh label create "needs-write-approval" --repo "${REPO_FULL_NAME}" \
--description "Triggered by a triage-role user; needs write+ approval before merge" \
--color "B60205" 2>/dev/null || true
gh pr edit "${target_pr}" --repo "${REPO_FULL_NAME}" \
--add-label "needs-write-approval" 2>/dev/null || \
_write_approval_gate_warn "Failed to apply needs-write-approval label to PR #${target_pr}"
}
# END bundled: lib/write-approval-gate.lib.sh

# ---------------------------------------------------------------------------
# Setup
Expand Down Expand Up @@ -1152,6 +1226,7 @@ if [ -n "${EXISTING_PR_NUM}" ]; then
echo "PR: ${EXISTING_PR_URL}"
echo "pr_url=${EXISTING_PR_URL}" >> "${GITHUB_OUTPUT:-/dev/null}"
maybe_assign_pr "${EXISTING_PR_NUM}"
apply_write_approval_gate_if_needed "${EXISTING_PR_NUM}"
exit 0
fi

Expand Down Expand Up @@ -1303,12 +1378,18 @@ rm -f "${PR_CREATE_STDERR}"
echo "PR created: ${PR_URL}"
echo "pr_url=${PR_URL}" >> "${GITHUB_OUTPUT:-/dev/null}"

PR_NUMBER_FROM_URL="${PR_URL##*/}"

# Apply the write-approval gate BEFORE ready-for-review: ready-for-review is
# what dispatches the review agent, so the gate label must already be in
# place before any downstream automation can act on this PR.
apply_write_approval_gate_if_needed "${PR_NUMBER_FROM_URL}"

# Apply ready-for-review label so the review agent is dispatched via the
# issues.labeled path. pull_request_target.opened requires the PR author to
# pass authorization checks that often exclude bot accounts; the label path
# is used instead (label application requires repo write access). See
# .github/scripts/check-e2e-authorization-test.sh for trusted-actor rules.
PR_NUMBER_FROM_URL="${PR_URL##*/}"
gh issue edit "${PR_NUMBER_FROM_URL}" \
--repo "${REPO_FULL_NAME}" \
--add-label "ready-for-review" 2>/dev/null || \
Expand Down
15 changes: 14 additions & 1 deletion scripts/post-code.src.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
# branch is allowed. (default: auto-detected)
# POST_FAILURE_DETAIL_MAX_LINES
# — max lines of failure detail in issue/PR comments (default: 30)
# TRIGGER_ROLE — permission tier that authorized this dispatch: "triage"
# or "write" (default: unset, treated as write — no gate).
# When "triage", the created PR is labeled
# needs-write-approval (fullsend-ai/fullsend#5687).
#
# Exit codes:
# 0 — branch pushed and PR created, OR agent determined nothing to do
Expand All @@ -46,6 +50,8 @@ source "${SCRIPT_DIR_POST}/lib/post-failure-report.lib.sh"
source "${SCRIPT_DIR_POST}/lib/gitleaks-install.lib.sh"
# shellcheck source=lib/pr-assignee.lib.sh
source "${SCRIPT_DIR_POST}/lib/pr-assignee.lib.sh"
# shellcheck source=lib/write-approval-gate.lib.sh
source "${SCRIPT_DIR_POST}/lib/write-approval-gate.lib.sh"
Comment on lines 50 to +54

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Protected scripts/ files modified 📜 Skill insight § Compliance

This PR modifies multiple files under the protected scripts/ path, which requires explicit human
review and must not be auto-approved. Ensure governance/infrastructure review expectations are
followed for these changes.


# ---------------------------------------------------------------------------
# Setup
Expand Down Expand Up @@ -522,6 +528,7 @@ if [ -n "${EXISTING_PR_NUM}" ]; then
echo "PR: ${EXISTING_PR_URL}"
echo "pr_url=${EXISTING_PR_URL}" >> "${GITHUB_OUTPUT:-/dev/null}"
maybe_assign_pr "${EXISTING_PR_NUM}"
apply_write_approval_gate_if_needed "${EXISTING_PR_NUM}"
exit 0
fi

Expand Down Expand Up @@ -673,12 +680,18 @@ rm -f "${PR_CREATE_STDERR}"
echo "PR created: ${PR_URL}"
echo "pr_url=${PR_URL}" >> "${GITHUB_OUTPUT:-/dev/null}"

PR_NUMBER_FROM_URL="${PR_URL##*/}"

# Apply the write-approval gate BEFORE ready-for-review: ready-for-review is
# what dispatches the review agent, so the gate label must already be in
# place before any downstream automation can act on this PR.
apply_write_approval_gate_if_needed "${PR_NUMBER_FROM_URL}"

# Apply ready-for-review label so the review agent is dispatched via the
# issues.labeled path. pull_request_target.opened requires the PR author to
# pass authorization checks that often exclude bot accounts; the label path
# is used instead (label application requires repo write access). See
# .github/scripts/check-e2e-authorization-test.sh for trusted-actor rules.
PR_NUMBER_FROM_URL="${PR_URL##*/}"
gh issue edit "${PR_NUMBER_FROM_URL}" \
--repo "${REPO_FULL_NAME}" \
--add-label "ready-for-review" 2>/dev/null || \
Expand Down
47 changes: 47 additions & 0 deletions scripts/post-fix-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,53 @@ run_postfix_integration_test "integration-neither-filename-fails-closed" "true"

rm -rf "${INTEGRATION_TMPDIR}"

# Verify apply_write_approval_gate_if_needed is present in the post-fix script
if ! grep -q 'apply_write_approval_gate_if_needed' "${POST_SCRIPT}"; then
echo "FAIL: bundled-script-has-write-approval-gate"
echo " ${POST_SCRIPT} missing apply_write_approval_gate_if_needed"
FAILURES=$((FAILURES + 1))
else
echo "PASS: bundled-script-has-write-approval-gate"
fi

# ---------------------------------------------------------------------------
# Test helper — reimplements the gating predicate from
# lib/write-approval-gate.lib.sh's apply_write_approval_gate_if_needed:
# gate applies when TRIGGER_ROLE normalizes (lowercased, trimmed) to "triage".
# ---------------------------------------------------------------------------
gate_applies_for_role() {
local trigger_role="${1:-}"
local role
role="$(printf '%s' "${trigger_role}" | tr '[:upper:]' '[:lower:]' | xargs)"
[[ "${role}" == "triage" ]]
}

run_gate_test() {
local test_name="$1"
local trigger_role="$2"
local expected="$3" # "yes" or "no"

local actual="no"
gate_applies_for_role "${trigger_role}" && actual="yes"

if [ "${actual}" != "${expected}" ]; then
echo "FAIL: ${test_name}"
echo " TRIGGER_ROLE='${trigger_role}' expected gate=${expected}, got gate=${actual}"
FAILURES=$((FAILURES + 1))
return
fi

echo "PASS: ${test_name}"
}

run_gate_test "gate-applies-for-triage" "triage" "yes"
run_gate_test "gate-applies-for-mixed-case" "Triage" "yes"
run_gate_test "gate-applies-for-upper-case" "TRIAGE" "yes"
run_gate_test "gate-applies-with-surrounding-whitespace" " triage " "yes"
run_gate_test "gate-skipped-for-write" "write" "no"
run_gate_test "gate-skipped-for-unset" "" "no"
run_gate_test "gate-skipped-for-garbage-value" "admin" "no"

# --- Summary ---

echo ""
Expand Down
Loading
Loading