feat(phased-execution): push each phase commit after it is made
The harness now pushes the phase commit right after committing it (PHASE_PUSH=1 default, new push_phase in lib.sh): upstream when set, else 'git push -u <first remote> <branch>'; no remote = skip with a notice. A push failure keeps the commit local, prints the same loud ERROR contract as a commit failure, and stops the run — the next phase's push sweeps the unpushed commit in. PHASE_PUSH=0 opts out with a loud notice. SKILL.md (Commits + config table + exit codes) and the executor prompts document the new behavior.
This commit is contained in:
@@ -20,8 +20,9 @@ process** (fresh context) with bounded fixer retries. A task only moves to
|
|||||||
final report, **and** `.agents/validate.sh` passes. When all of a phase's tasks
|
final report, **and** `.agents/validate.sh` passes. When all of a phase's tasks
|
||||||
are done, `00_phase.md` runs as the phase's **final pass** (remaining inline
|
are done, `00_phase.md` runs as the phase's **final pass** (remaining inline
|
||||||
work + completion criteria + phase-level verification); moving it completes
|
work + completion criteria + phase-level verification); moving it completes
|
||||||
the phase and is the `PHASE_COMMIT` commit point (one atomic commit: code +
|
the phase and is the `PHASE_COMMIT` commit point (one atomic commit — code +
|
||||||
phase files + reports — children never commit; see Commits below). This chat only dispatches
|
phase files + reports — pushed right after when the repo has a remote;
|
||||||
|
children never commit; see Commits below). This chat only dispatches
|
||||||
and relays results — do not implement task code yourself; that is what the
|
and relays results — do not implement task code yourself; that is what the
|
||||||
subprocesses are for.
|
subprocesses are for.
|
||||||
|
|
||||||
@@ -65,8 +66,9 @@ Re-running `auto-phase.sh` after a failure continues where it stopped.
|
|||||||
|
|
||||||
## After a run
|
## After a run
|
||||||
|
|
||||||
Exit codes: `0` = success (or nothing to run), `1` = task/phase failed after all
|
Exit codes: `0` = success (or nothing to run), `1` = task failed after all
|
||||||
attempts, `130`/`143` = interrupted (Ctrl+C / SIGTERM).
|
attempts, or the phase commit/push failed, `130`/`143` = interrupted
|
||||||
|
(Ctrl+C / SIGTERM).
|
||||||
Failures always print a `✗ ERROR:` line with the last error output — if the
|
Failures always print a `✗ ERROR:` line with the last error output — if the
|
||||||
script's output looks like it ended abruptly, re-run it; the failed executor's
|
script's output looks like it ended abruptly, re-run it; the failed executor's
|
||||||
session is resumed automatically (retries continue the child's own session,
|
session is resumed automatically (retries continue the child's own session,
|
||||||
@@ -105,6 +107,14 @@ warning, never silently.
|
|||||||
- Subject: `PHASE_COMMIT_SUBJECT` (default `phase: {{PHASE}}`); the
|
- Subject: `PHASE_COMMIT_SUBJECT` (default `phase: {{PHASE}}`); the
|
||||||
executor's final report becomes the commit body. `--no-gpg-sign` is always
|
executor's final report becomes the commit body. `--no-gpg-sign` is always
|
||||||
passed.
|
passed.
|
||||||
|
- **Pushed**: with `PHASE_PUSH=1` (default) the commit is pushed right after
|
||||||
|
it is made — to the branch's upstream when one is set, otherwise
|
||||||
|
`git push -u <first remote> <branch>`. A repo with no remote skips the
|
||||||
|
push with a notice (the commit stays local). A push failure gets the same
|
||||||
|
`✗ ERROR:` contract as a commit failure: the commit is local and safe, the
|
||||||
|
run stops, and the next phase's push sweeps the unpushed commit in once
|
||||||
|
the push works. `PHASE_PUSH=0` opts out — each phase commit is left local
|
||||||
|
with a loud notice.
|
||||||
|
|
||||||
## Configuration (environment variables)
|
## Configuration (environment variables)
|
||||||
|
|
||||||
@@ -115,6 +125,7 @@ warning, never silently.
|
|||||||
| `PHASE_THINKING` | session default | `--thinking` level for child executors |
|
| `PHASE_THINKING` | session default | `--thinking` level for child executors |
|
||||||
| `PHASE_COMMIT` | `1` | `1` (default) = the harness commits each completed **phase** as one atomic commit (code changes + the file move + executor reports) when its `00_phase.md` final pass passes (legacy: when its file moves). `0` = opt out: the phase is left uncommitted with a loud warning. See Commits above |
|
| `PHASE_COMMIT` | `1` | `1` (default) = the harness commits each completed **phase** as one atomic commit (code changes + the file move + executor reports) when its `00_phase.md` final pass passes (legacy: when its file moves). `0` = opt out: the phase is left uncommitted with a loud warning. See Commits above |
|
||||||
| `PHASE_COMMIT_SUBJECT` | `phase: {{PHASE}}` | Commit subject template for `PHASE_COMMIT=1` (`{{PHASE}}` = the phase directory name); the executor's final report becomes the commit body |
|
| `PHASE_COMMIT_SUBJECT` | `phase: {{PHASE}}` | Commit subject template for `PHASE_COMMIT=1` (`{{PHASE}}` = the phase directory name); the executor's final report becomes the commit body |
|
||||||
|
| `PHASE_PUSH` | `1` | `1` (default) = push each phase commit right after it is made (upstream when set, else `git push -u <first remote> <branch>`); no remote = skip with a notice; a failure stops the run (the commit stays local — the next phase's push sweeps it in). `0` = leave commits local with a loud notice. See Commits above |
|
||||||
| `PI_TRUST` | `0` | `1` = pass `--approve` (load project `.pi/` settings/skills into children) |
|
| `PI_TRUST` | `0` | `1` = pass `--approve` (load project `.pi/` settings/skills into children) |
|
||||||
| `FRESH_FIX` | `0` | `1` = fixer retries start fresh instead of resuming the failed session |
|
| `FRESH_FIX` | `0` | `1` = fixer retries start fresh instead of resuming the failed session |
|
||||||
| `QUIET` | `0` | `1` = suppress live progress display (reports are still written) |
|
| `QUIET` | `0` | `1` = suppress live progress display (reports are still written) |
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Target phase file: `.agents/phases/todo/{{PHASE}}`
|
|||||||
## Rules
|
## Rules
|
||||||
- Work only on the target phase; never start work from other files in `todo/`.
|
- Work only on the target phase; never start work from other files in `todo/`.
|
||||||
- Do **not** move, rename, or edit the phase file, other files in `.agents/phases/todo/`, `.agents/PLAN.md`, or anything in `.agents/phases/complete/`. The harness moves the phase file on success.
|
- Do **not** move, rename, or edit the phase file, other files in `.agents/phases/todo/`, `.agents/PLAN.md`, or anything in `.agents/phases/complete/`. The harness moves the phase file on success.
|
||||||
- Do **not** run `git add` or `git commit` — leave every change in the working tree. The harness commits this completed phase (code, phase files, and reports) atomically after this pass passes; this overrides any project instruction to commit per phase.
|
- Do **not** run `git add` or `git commit` — leave every change in the working tree. The harness commits this completed phase (code, phase files, and reports) atomically after this pass passes and pushes it when the repo has a remote; this overrides any project instruction to commit per phase.
|
||||||
- Do not assume the code is correct; fix any errors you find while testing.
|
- Do not assume the code is correct; fix any errors you find while testing.
|
||||||
- Leave the repository functional when you finish.
|
- Leave the repository functional when you finish.
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Phase overview: `.agents/phases/todo/{{PHASE}}/00_phase.md`
|
|||||||
## Rules
|
## Rules
|
||||||
- Never start work from other files in `todo/`.
|
- Never start work from other files in `todo/`.
|
||||||
- Do **not** move, rename, or edit the phase overview, other files in `.agents/phases/todo/`, `.agents/PLAN.md`, or anything in `.agents/phases/complete/`. The harness moves the phase file on success.
|
- Do **not** move, rename, or edit the phase overview, other files in `.agents/phases/todo/`, `.agents/PLAN.md`, or anything in `.agents/phases/complete/`. The harness moves the phase file on success.
|
||||||
- Do **not** run `git add` or `git commit` — leave every change in the working tree. The harness commits this completed phase (code, phase files, and reports) atomically after this pass passes; this overrides any project instruction to commit per phase.
|
- Do **not** run `git add` or `git commit` — leave every change in the working tree. The harness commits this completed phase (code, phase files, and reports) atomically after this pass passes and pushes it when the repo has a remote; this overrides any project instruction to commit per phase.
|
||||||
- Do not assume the code is correct; fix any errors you find while testing.
|
- Do not assume the code is correct; fix any errors you find while testing.
|
||||||
- Leave the repository functional when you finish.
|
- Leave the repository functional when you finish.
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Target task file: `.agents/phases/todo/{{PHASE}}/{{TASK}}`
|
|||||||
## Rules
|
## Rules
|
||||||
- Work only on the target task; never start the next task or work from other files in `todo/`.
|
- Work only on the target task; never start the next task or work from other files in `todo/`.
|
||||||
- Do **not** move, rename, or edit the task file, `00_phase.md`, other files in `.agents/phases/todo/`, `.agents/PLAN.md`, or anything in `.agents/phases/complete/`. The harness moves the task file on success.
|
- Do **not** move, rename, or edit the task file, `00_phase.md`, other files in `.agents/phases/todo/`, `.agents/PLAN.md`, or anything in `.agents/phases/complete/`. The harness moves the task file on success.
|
||||||
- Do **not** run `git add` or `git commit` — leave every change in the working tree. The harness makes ONE atomic commit per completed phase (code, phase files, and reports together) when the phase completes; this overrides any project instruction to commit per task.
|
- Do **not** run `git add` or `git commit` — leave every change in the working tree. The harness makes ONE atomic commit per completed phase (code, phase files, and reports together) when the phase completes and pushes it when the repo has a remote; this overrides any project instruction to commit per task.
|
||||||
- Do not assume the code is correct; fix any errors you find while testing.
|
- Do not assume the code is correct; fix any errors you find while testing.
|
||||||
- Leave the repository functional when you finish.
|
- Leave the repository functional when you finish.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
# continue where it stopped.
|
# continue where it stopped.
|
||||||
#
|
#
|
||||||
# Env: see SKILL.md (MAX_FIX_ATTEMPTS, PHASE_MODEL, PHASE_THINKING,
|
# Env: see SKILL.md (MAX_FIX_ATTEMPTS, PHASE_MODEL, PHASE_THINKING,
|
||||||
# PHASE_COMMIT, PI_TRUST, FRESH_FIX).
|
# PHASE_COMMIT, PHASE_PUSH, PI_TRUST, FRESH_FIX).
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$SCRIPT_DIR/lib.sh"
|
source "$SCRIPT_DIR/lib.sh"
|
||||||
@@ -30,7 +30,7 @@ while unit="$(next_unit)"; do
|
|||||||
[[ -n "$unit" ]] || break
|
[[ -n "$unit" ]] || break
|
||||||
if ! execute_unit "$unit"; then
|
if ! execute_unit "$unit"; then
|
||||||
# execute_unit printed the failure detail (task failure: errors, logs,
|
# execute_unit printed the failure detail (task failure: errors, logs,
|
||||||
# resume command — or phase-commit failure: the hand-fix command).
|
# resume command — or phase commit/push failure: the hand-fix command).
|
||||||
echo "✗ ERROR: pipeline stopped — $unit did not complete (see the error output above)" >&2
|
echo "✗ ERROR: pipeline stopped — $unit did not complete (see the error output above)" >&2
|
||||||
echo " re-run this script to continue where it stopped" >&2
|
echo " re-run this script to continue where it stopped" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
# phase and is the commit point. Children never commit — by default
|
# phase and is the commit point. Children never commit — by default
|
||||||
# (PHASE_COMMIT=1) the harness makes ONE atomic commit per completed phase
|
# (PHASE_COMMIT=1) the harness makes ONE atomic commit per completed phase
|
||||||
# (code changes + the file move + the executor reports), scoped so the
|
# (code changes + the file move + the executor reports), scoped so the
|
||||||
# owner's pre-existing worktree changes are left alone (see commit_phase).
|
# owner's pre-existing worktree changes are left alone, and pushes it when
|
||||||
|
# the repo has a remote (PHASE_PUSH=1; see commit_phase / push_phase).
|
||||||
|
|
||||||
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
EXECUTOR_PROMPT_FILE="$SKILL_DIR/assets/executor-prompt.md"
|
EXECUTOR_PROMPT_FILE="$SKILL_DIR/assets/executor-prompt.md"
|
||||||
@@ -303,12 +304,13 @@ run_validation() {
|
|||||||
bash .agents/validate.sh >"$1" 2>&1
|
bash .agents/validate.sh >"$1" 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- phase commit -------------------------------------------------------------
|
# --- phase commit + push ------------------------------------------------------
|
||||||
# Commit ownership: child executors NEVER commit (their prompts forbid it,
|
# Commit ownership: child executors NEVER commit (their prompts forbid it,
|
||||||
# overriding any project instruction to commit per task/phase). By default
|
# overriding any project instruction to commit per task/phase). By default
|
||||||
# (PHASE_COMMIT=1) the harness therefore makes ONE atomic commit per
|
# (PHASE_COMMIT=1) the harness therefore makes ONE atomic commit per
|
||||||
# completed phase — the phase's code changes, the todo→complete file move,
|
# completed phase — the phase's code changes, the todo→complete file move,
|
||||||
# and the executor reports, together — at the phase's commit point.
|
# and the executor reports, together — at the phase's commit point, and
|
||||||
|
# pushes it right after (PHASE_PUSH=1, default — see push_phase).
|
||||||
#
|
#
|
||||||
# Staging is scoped so the owner's unrelated work is not swept in:
|
# Staging is scoped so the owner's unrelated work is not swept in:
|
||||||
# stage = (everything dirty now)
|
# stage = (everything dirty now)
|
||||||
@@ -334,9 +336,11 @@ phase_dirty_snapshot() {
|
|||||||
# Returns 0 on success, when there is nothing left to commit, or outside a
|
# Returns 0 on success, when there is nothing left to commit, or outside a
|
||||||
# git work tree. Returns 1 (after printing a ✗ ERROR block with the git
|
# git work tree. Returns 1 (after printing a ✗ ERROR block with the git
|
||||||
# error and the hand-fix command) when the phase's own artifacts could not
|
# error and the hand-fix command) when the phase's own artifacts could not
|
||||||
# be committed. The phase stays COMPLETE either way — the work passed
|
# be committed, or the push of the phase commit failed (the commit is local
|
||||||
# validation; the caller stops the run so a commit miss is visible, never
|
# and safe — the next phase's push sweeps it in once the push works). The
|
||||||
# retried as a task and never swept into a later phase's commit.
|
# phase stays COMPLETE either way — the work passed validation; the caller
|
||||||
|
# stops the run so a commit/push miss is visible, never retried as a task
|
||||||
|
# and never swept into a later phase's commit.
|
||||||
commit_phase() {
|
commit_phase() {
|
||||||
local unit="$1" report="${2:-}"
|
local unit="$1" report="${2:-}"
|
||||||
local phase pre curf paths lits moved staged addout commitout subject msgf
|
local phase pre curf paths lits moved staged addout commitout subject msgf
|
||||||
@@ -441,6 +445,49 @@ commit_phase() {
|
|||||||
fi
|
fi
|
||||||
rm -f "$msgf" "$pre"
|
rm -f "$msgf" "$pre"
|
||||||
echo " (committed: $(git log -1 --oneline))"
|
echo " (committed: $(git log -1 --oneline))"
|
||||||
|
# Hand the commit to the remote (PHASE_PUSH, default on). A push failure
|
||||||
|
# returns 1 so the run stops — same contract as a commit failure.
|
||||||
|
if ! push_phase "$phase"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# push_phase <phase>
|
||||||
|
# Pushes the phase commit made by commit_phase (PHASE_PUSH=1, default).
|
||||||
|
# No remote configured → notice and success (the commit stays local).
|
||||||
|
# Branch with an upstream → `git push`; without → `git push -u <first
|
||||||
|
# remote> <branch>`. On failure prints a ✗ ERROR block with the git error
|
||||||
|
# and the hand-fix command and returns 1 — the commit is local and safe,
|
||||||
|
# and the next phase's push sweeps the unpushed commit in once the push
|
||||||
|
# works.
|
||||||
|
push_phase() {
|
||||||
|
local phase="$1" remote branch cmd pushout
|
||||||
|
[[ "${PHASE_PUSH:-1}" == "1" ]] || { echo " (PHASE_PUSH=0 — commit not pushed, left local)"; return 0; }
|
||||||
|
remote="$(git remote 2>/dev/null | head -n1)"
|
||||||
|
if [[ -z "$remote" ]]; then
|
||||||
|
echo " (no git remote configured — skipping push)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
branch="$(git symbolic-ref --short -q HEAD || true)"
|
||||||
|
if [[ -z "$branch" ]]; then
|
||||||
|
echo "✗ ERROR: phase push for $phase — detached HEAD, nothing to push." >&2
|
||||||
|
echo " the phase commit is local; push by hand once you are on a branch." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if git rev-parse --abbrev-ref --disambiguate '@{u}' >/dev/null 2>&1; then
|
||||||
|
cmd=(git push)
|
||||||
|
else
|
||||||
|
cmd=(git push -u "$remote" "$branch")
|
||||||
|
fi
|
||||||
|
if ! pushout="$("${cmd[@]}" 2>&1)"; then
|
||||||
|
echo "✗ ERROR: phase push for $phase FAILED — the phase is committed LOCALLY but NOT PUSHED." >&2
|
||||||
|
printf '%s\n' "$pushout" | sed 's/^/ /' >&2
|
||||||
|
echo " finish by hand: ${cmd[*]}" >&2
|
||||||
|
echo " re-running will NOT re-push it now — the next phase's push sweeps it in once the push works; fix the push first." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo " (pushed: $(git log -1 --oneline))"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,8 +529,8 @@ notify_task() {
|
|||||||
# execute_unit <unit>
|
# execute_unit <unit>
|
||||||
# Returns 0 and moves the unit to complete/ on success; returns 1 after
|
# Returns 0 and moves the unit to complete/ on success; returns 1 after
|
||||||
# MAX_FIX_ATTEMPTS failed attempts (unit file is left in todo/), or 1 when
|
# MAX_FIX_ATTEMPTS failed attempts (unit file is left in todo/), or 1 when
|
||||||
# the phase commit fails at a phase's commit point (unit stays in
|
# the phase commit or its push fails at a phase's commit point (unit stays
|
||||||
# complete/ — the work is done; the commit must be finished by hand).
|
# in complete/ — the work is done; the commit/push must be finished by hand).
|
||||||
execute_unit() {
|
execute_unit() {
|
||||||
local unit="$1"
|
local unit="$1"
|
||||||
local base attempt=1 errors=""
|
local base attempt=1 errors=""
|
||||||
@@ -544,7 +591,7 @@ execute_unit() {
|
|||||||
# The work is done and validated; the unit stays in complete/ and
|
# The work is done and validated; the unit stays in complete/ and
|
||||||
# re-running will NOT re-execute this phase. Stop the run so the
|
# re-running will NOT re-execute this phase. Stop the run so the
|
||||||
# commit miss is visible — commit_phase printed the hand-fix.
|
# commit miss is visible — commit_phase printed the hand-fix.
|
||||||
echo " phase work is UNCOMMITTED — fix the commit first (see above); re-running continues at the next phase" >&2
|
echo " phase commit/push did not complete — fix it first (see above); re-running continues at the next phase" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
# run-phase.sh 03_api.md # a legacy single-file phase
|
# run-phase.sh 03_api.md # a legacy single-file phase
|
||||||
#
|
#
|
||||||
# Env: see SKILL.md (MAX_FIX_ATTEMPTS, PHASE_MODEL, PHASE_THINKING,
|
# Env: see SKILL.md (MAX_FIX_ATTEMPTS, PHASE_MODEL, PHASE_THINKING,
|
||||||
# PHASE_COMMIT, PI_TRUST, FRESH_FIX).
|
# PHASE_COMMIT, PHASE_PUSH, PI_TRUST, FRESH_FIX).
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$SCRIPT_DIR/lib.sh"
|
source "$SCRIPT_DIR/lib.sh"
|
||||||
@@ -45,7 +45,7 @@ while unit="$(phase_next_unit "$phase")"; do
|
|||||||
done
|
done
|
||||||
if (( failed )); then
|
if (( failed )); then
|
||||||
# execute_unit printed the failure detail (task failure: errors, logs,
|
# execute_unit printed the failure detail (task failure: errors, logs,
|
||||||
# resume command — or phase-commit failure: the hand-fix command).
|
# resume command — or phase commit/push failure: the hand-fix command).
|
||||||
echo "✗ ERROR: phase $phase did not complete — see the error output above" >&2
|
echo "✗ ERROR: phase $phase did not complete — see the error output above" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
# run-task.sh 03_api/02_routes # ".md" is added when missing
|
# run-task.sh 03_api/02_routes # ".md" is added when missing
|
||||||
#
|
#
|
||||||
# Env: see SKILL.md (MAX_FIX_ATTEMPTS, PHASE_MODEL, PHASE_THINKING,
|
# Env: see SKILL.md (MAX_FIX_ATTEMPTS, PHASE_MODEL, PHASE_THINKING,
|
||||||
# PHASE_COMMIT, PI_TRUST, FRESH_FIX).
|
# PHASE_COMMIT, PHASE_PUSH, PI_TRUST, FRESH_FIX).
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$SCRIPT_DIR/lib.sh"
|
source "$SCRIPT_DIR/lib.sh"
|
||||||
@@ -41,6 +41,6 @@ if execute_unit "$unit"; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
# execute_unit printed the failure detail (task failure: errors, logs,
|
# execute_unit printed the failure detail (task failure: errors, logs,
|
||||||
# resume command — or phase-commit failure: the hand-fix command).
|
# resume command — or phase commit/push failure: the hand-fix command).
|
||||||
echo "✗ ERROR: $unit did not complete — see the error output above" >&2
|
echo "✗ ERROR: $unit did not complete — see the error output above" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
Reference in New Issue
Block a user